diff --git a/include/urc/crypto_psbt.h b/include/urc/crypto_psbt.h index 5206210..e79e9a9 100644 --- a/include/urc/crypto_psbt.h +++ b/include/urc/crypto_psbt.h @@ -10,13 +10,15 @@ extern "C" { #include "urc/error.h" typedef struct { - uint8_t *buffer; - size_t buffer_size; + uint8_t *psbt; size_t psbt_len; } crypto_psbt; -int urc_crypto_psbt_deserialize(const uint8_t *buffer, size_t len, crypto_psbt *out); +// ``out`` must be freed by caller using urc_crypto_psbt_free +int urc_crypto_psbt_deserialize(const uint8_t *cbor_buffer, size_t cbor_len, crypto_psbt *out); +int urc_crypto_psbt_serialize(const crypto_psbt *psbt, uint8_t **cbor_out, size_t *cbor_len); +void urc_crypto_psbt_free(crypto_psbt *psbt); #ifdef __cplusplus } diff --git a/src/bip8539.c b/src/bip8539.c index 208038d..1dfa1c5 100644 --- a/src/bip8539.c +++ b/src/bip8539.c @@ -38,6 +38,7 @@ static int jade_bip8539_request_serialize_op(CborEncoder *encoder, const jade_bi static int jade_bip8539_response_deserialize_op(CborValue *iter, jade_bip8539_response *out, uint8_t *buffer, size_t len) { + out->encrypted_data = NULL; out->encrypted_len = 0; int result = URC_OK; @@ -125,6 +126,8 @@ int urc_jade_bip8539_request_serialize(const jade_bip8539_request *request, uint int urc_jade_bip8539_response_deserialize(const uint8_t *cbor, size_t cbor_len, jade_bip8539_response *response) { + response->encrypted_data = NULL; + response->encrypted_len = 0; int result = URC_OK; size_t buffer_len = cbor_len; uint8_t *buffer = NULL; diff --git a/src/psbt.c b/src/psbt.c index e14af5e..c111a26 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -1,26 +1,36 @@ +#include "wally_core.h" + #include "urc/crypto_psbt.h" #include "urc/error.h" #include "macros.h" #include "utils.h" -int urc_crypto_psbt_deserialize_impl(CborValue *iter, crypto_psbt *out); +// max_len represents the maximum length of the psbt buffer +// if cbor byte string length is greater than max_len, return URC_EINVALIDARG +// max_len == 0 means no limit +int urc_crypto_psbt_deserialize_impl(CborValue *iter, crypto_psbt *out, size_t max_len); -int urc_crypto_psbt_deserialize(const uint8_t *buffer, size_t len, crypto_psbt *out) +int urc_crypto_psbt_deserialize(const uint8_t *cbor_buffer, size_t cbor_len, crypto_psbt *out) { + if (!cbor_buffer || !out) { + return URC_EINVALIDARG; + } + CborParser parser; CborValue iter; CborError err; - err = cbor_parser_init(buffer, len, cbor_flags, &parser, &iter); + err = cbor_parser_init(cbor_buffer, cbor_len, cbor_flags, &parser, &iter); if (err != CborNoError) { return URC_ECBORINTERNALERROR; } - return urc_crypto_psbt_deserialize_impl(&iter, out); + return urc_crypto_psbt_deserialize_impl(&iter, out, cbor_len); } -int urc_crypto_psbt_deserialize_impl(CborValue *iter, crypto_psbt *out) +int urc_crypto_psbt_deserialize_impl(CborValue *iter, crypto_psbt *out, size_t max_len) { + out->psbt = NULL; out->psbt_len = 0; int result = URC_OK; @@ -28,19 +38,79 @@ int urc_crypto_psbt_deserialize_impl(CborValue *iter, crypto_psbt *out) size_t len; CborError err = cbor_value_get_string_length(iter, &len); CHECK_CBOR_ERROR(err, result, exit); - - if (out->buffer_size < len) { - return URC_EBUFFERTOOSMALL; + if (len == 0 || (max_len && len > max_len)) { + return URC_EINVALIDARG; } - len = out->buffer_size; - err = cbor_value_copy_byte_string(iter, out->buffer, &len, NULL); - CHECK_CBOR_ERROR(err, result, exit); + out->psbt = wally_malloc(len); + if (!out->psbt) { + return URC_ENOMEM; + } + result = copy_fixed_size_byte_string(iter, out->psbt, len); + if (result != URC_OK) { + goto free_and_exit; + } out->psbt_len = len; - ADVANCE(iter, result, exit); + ADVANCE(iter, result, free_and_exit); + return URC_OK; +free_and_exit: + urc_crypto_psbt_free(out); exit: return result; } + +int urc_crypto_psbt_serialize_impl(const crypto_psbt *psbt, uint8_t *out, size_t *out_len) +{ + CborEncoder encoder; + cbor_encoder_init(&encoder, out, *out_len, 0); + CborError err = cbor_encode_byte_string(&encoder, psbt->psbt, psbt->psbt_len); + if (err == CborErrorOutOfMemory) { + return URC_EBUFFERTOOSMALL; + } + if (err != CborNoError) { + return URC_ECBORINTERNALERROR; + } + *out_len = cbor_encoder_get_buffer_size(&encoder, out); + return URC_OK; +} + +int urc_crypto_psbt_serialize(const crypto_psbt *psbt, uint8_t **cbor_out, size_t *cbor_len) +{ + if (!psbt || !cbor_out) { + return URC_EINVALIDARG; + } + *cbor_len = 0; + *cbor_out = NULL; + + size_t buffer_len = psbt->psbt_len * 2 + 1; + int result = URC_OK; + *cbor_out = NULL; + do { + wally_free(*cbor_out); + *cbor_out = wally_malloc(buffer_len); + if (!*cbor_out) { + return URC_ENOMEM; + } + *cbor_len = buffer_len; + result = urc_crypto_psbt_serialize_impl(psbt, *cbor_out, cbor_len); + buffer_len *= 2; + } while (result == URC_EBUFFERTOOSMALL); + if (result != URC_OK) { + wally_free(*cbor_out); + *cbor_out = NULL; + *cbor_len = 0; + } + return result; +} + +void urc_crypto_psbt_free(crypto_psbt *psbt) +{ + if (psbt) { + wally_free(psbt->psbt); + psbt->psbt = NULL; + psbt->psbt_len = 0; + } +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c6f8e69..72f9c6d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,6 +10,7 @@ add_executable( helpers.c formatter.c parser.c + psbt.c runner.c jade_rpc.c ) diff --git a/tests/fuzzy/parser.c b/tests/fuzzy/parser.c index 3fae3e0..b0b4fd4 100644 --- a/tests/fuzzy/parser.c +++ b/tests/fuzzy/parser.c @@ -13,13 +13,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t len) { } crypto_psbt psbt; - uint8_t buffer[BUFSIZE]; - psbt.buffer = buffer; - psbt.buffer_size = BUFSIZE; result = urc_crypto_psbt_deserialize(data, len, &psbt); - if(result == URC_OK) { - return -1; - } + // it is so easy to generate a valid cbor representation of a psbt that checking the result is pointless + urc_crypto_psbt_free(&psbt); crypto_eckey eckey; result = urc_crypto_eckey_deserialize(data, len, &eckey); diff --git a/tests/parser.c b/tests/parser.c index 7489617..300768f 100644 --- a/tests/parser.c +++ b/tests/parser.c @@ -56,28 +56,6 @@ TEST(parser, crypto_seed_deserialize) TEST_ASSERT_EQUAL_HEX(0x52, seed.seed[CRYPTO_SEED_SIZE - 1]); } -TEST(parser, crypto_psbt_deserialize) -{ - // https://github.com/BlockchainCommons/Research/blob/master/papers/urc-2020-006-urtypes.md#partially-signed-bitcoin-transaction-psbt-crypto-psbt - const char *hex = "58a770736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7500" - "00000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffff" - "ff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2" - "e5f0f876a588df5546e8742d1d87008f000000000000000000"; - uint8_t raw[BUFLEN]; - size_t len = h2b(hex, BUFLEN, (uint8_t *)&raw); - TEST_ASSERT_GREATER_THAN_INT(0, len); - - crypto_psbt psbt; - uint8_t buffer[1000]; - psbt.buffer = buffer; - psbt.buffer_size = BUFLEN; - int err = urc_crypto_psbt_deserialize(raw, len, &psbt); - TEST_ASSERT_EQUAL(URC_OK, err); - TEST_ASSERT_EQUAL(167, psbt.psbt_len); - TEST_ASSERT_EQUAL_HEX(0x70, psbt.buffer[0]); - TEST_ASSERT_EQUAL_HEX(0x00, psbt.buffer[psbt.psbt_len - 1]); -} - TEST(parser, crypto_eckey_deserialize) { // https://github.com/BlockchainCommons/Research/blob/master/papers/urc-2020-006-urtypes.md#partially-signed-bitcoin-transaction-psbt-crypto-psbt diff --git a/tests/psbt.c b/tests/psbt.c new file mode 100644 index 0000000..5f18ac5 --- /dev/null +++ b/tests/psbt.c @@ -0,0 +1,49 @@ + +#include "unity_fixture.h" + +#include "urc/crypto_psbt.h" +#include "urc/core.h" + +#include "helpers.h" + +#define BUFLEN 1024 + +TEST_GROUP(psbt); + +TEST_SETUP(psbt) {} +TEST_TEAR_DOWN(psbt) {} + +TEST(psbt, test_vector_1) +{ + const char *psbt_hex = + "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec" + "650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb81" + "5e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000000000000000000"; + const char *cbor_psbt_hex = + "58a770736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427" + "d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886a" + "eb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000000000000000000"; + + uint8_t raw_psbt[BUFLEN]; + size_t raw_len = h2b(psbt_hex, BUFLEN, (uint8_t *)&raw_psbt); + TEST_ASSERT_GREATER_THAN_INT(0, raw_len); + + uint8_t raw_cbor_psbt[BUFLEN]; + size_t cbor_len = h2b(cbor_psbt_hex, BUFLEN, (uint8_t *)&raw_cbor_psbt); + TEST_ASSERT_GREATER_THAN_INT(0, cbor_len); + + crypto_psbt psbt; + psbt.psbt = raw_psbt; + psbt.psbt_len = raw_len; + uint8_t *buffer; + size_t buffer_len; + int result = urc_crypto_psbt_serialize(&psbt, &buffer, &buffer_len); + TEST_ASSERT_EQUAL(URC_OK, result); + TEST_ASSERT_EQUAL_UINT8_ARRAY(raw_cbor_psbt, buffer, buffer_len); + urc_free(buffer); + + result = urc_crypto_psbt_deserialize(raw_cbor_psbt, cbor_len, &psbt); + TEST_ASSERT_EQUAL(URC_OK, result); + TEST_ASSERT_EQUAL(raw_len, psbt.psbt_len); + TEST_ASSERT_EQUAL_UINT8_ARRAY(raw_psbt, psbt.psbt, raw_len); +} diff --git a/tests/runner.c b/tests/runner.c index 5669d4f..8a0cb7c 100644 --- a/tests/runner.c +++ b/tests/runner.c @@ -3,7 +3,6 @@ TEST_GROUP_RUNNER(parser) { RUN_TEST_CASE(parser, crypto_seed_deserialize); - RUN_TEST_CASE(parser, crypto_psbt_deserialize); RUN_TEST_CASE(parser, crypto_eckey_deserialize); RUN_TEST_CASE(parser, crypto_hdkey_deserialize_1); RUN_TEST_CASE(parser, crypto_hdkey_deserialize_2); @@ -27,10 +26,15 @@ TEST_GROUP_RUNNER(jade_rpc) { RUN_TEST_CASE(jade_rpc, parse_jade_pin_3); } +TEST_GROUP_RUNNER(psbt) { + RUN_TEST_CASE(psbt, test_vector_1); +} + static void RunAllTests(void) { RUN_TEST_GROUP(parser); RUN_TEST_GROUP(formatter); RUN_TEST_GROUP(jade_rpc); + RUN_TEST_GROUP(psbt); } int main(int argc, const char *argv[]) { return UnityMain(argc, argv, RunAllTests); }