Skip to content

Commit

Permalink
psbt: add deserialize/serialize functions + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
gministeri committed May 21, 2024
1 parent eb8be0b commit c1a6b23
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 44 deletions.
8 changes: 5 additions & 3 deletions include/urc/crypto_psbt.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
3 changes: 3 additions & 0 deletions src/bip8539.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down
94 changes: 82 additions & 12 deletions src/psbt.c
Original file line number Diff line number Diff line change
@@ -1,46 +1,116 @@

#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;

CHECK_IS_TYPE(iter, byte_string, result, exit)
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;
}
}
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_executable(
helpers.c
formatter.c
parser.c
psbt.c
runner.c
jade_rpc.c
)
Expand Down
8 changes: 2 additions & 6 deletions tests/fuzzy/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
22 changes: 0 additions & 22 deletions tests/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
49 changes: 49 additions & 0 deletions tests/psbt.c
Original file line number Diff line number Diff line change
@@ -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);
}
6 changes: 5 additions & 1 deletion tests/runner.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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); }

0 comments on commit c1a6b23

Please sign in to comment.