diff --git a/src/apdu/dispatcher.h b/src/apdu/dispatcher.h index 9b9c001..6a67f43 100644 --- a/src/apdu/dispatcher.h +++ b/src/apdu/dispatcher.h @@ -14,6 +14,10 @@ * Parameter 1 for first APDU number. */ #define P1_START 0x00 + +#define P1_OUTPUTS 0x01 + +#define P1_INPUTS 0x02 /** * Parameter 1 for maximum APDU number. */ diff --git a/src/constants.h b/src/constants.h index 3041783..beb922c 100644 --- a/src/constants.h +++ b/src/constants.h @@ -24,7 +24,7 @@ /** * Maximum transaction length (bytes). */ -#define MAX_TRANSACTION_LEN 1410 +#define MAX_TRANSACTION_LEN 128 /** * Maximum signature length (bytes). @@ -42,3 +42,8 @@ * Cannot be any longer than 35 in any case */ #define MAX_INPUT_SCRIPT_PUBLIC_KEY_LEN 35 + +/** + * The signing key used for sighash + */ +#define SIGNING_KEY "TransactionSigningHash" diff --git a/src/crypto.c b/src/crypto.c index 15e24e9..9cdd6d7 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -6,6 +6,8 @@ #include "globals.h" +#include "sighash.h" + int crypto_derive_private_key(cx_ecfp_private_key_t *private_key, uint8_t chain_code[static 32], const uint32_t *bip32_path, @@ -51,15 +53,15 @@ void crypto_init_public_key(cx_ecfp_private_key_t *private_key, int crypto_sign_message(void) { cx_ecfp_private_key_t private_key = {0}; uint8_t chain_code[32] = {0}; - // uint32_t info = 0; + uint32_t info = 0; int sig_len = 0; - // FIXME: Forced 44'/111111'/0'/0/0 + // 44'/111111'/0'/ address_type / address_index G_context.bip32_path[0] = 0x8000002C; G_context.bip32_path[1] = 0x8001b207; G_context.bip32_path[2] = 0x80000000; - G_context.bip32_path[3] = 0x00000000; - G_context.bip32_path[4] = 0x00000000; + G_context.bip32_path[3] = G_context.tx_info.transaction.tx_inputs[0].derivation_path[0]; + G_context.bip32_path[4] = G_context.tx_info.transaction.tx_inputs[0].derivation_path[1]; G_context.bip32_path_len = 5; @@ -74,37 +76,15 @@ int crypto_sign_message(void) { BEGIN_TRY { TRY { - // FIXME: implement signing here: - // from BTC: - // https://github.com/LedgerHQ/app-bitcoin-new/blob/b2c624769c3b863b38dd133e8facabb3d7b5b76c/src/handler/sign_psbt.c - // err = cx_ecschnorr_sign_no_throw(&private_key, - // CX_ECSCHNORR_BIP0340 | CX_RND_TRNG, - // CX_SHA256, - // sighash, - // 32, - // sig, - // &sig_len); - // from doc: - // https://developers.ledger.com/docs/embedded-app/crypto-api/lcx__ecschnorr_8h/#a2aa2454ece11c17373539d7178d26a98 - // static int cx_ecschnorr_sign ( - // const cx_ecfp_private_key_t * pvkey, - // int mode, - // cx_md_t hashID, - // const unsigned char * msg, - // unsigned int msg_len, - // unsigned char * sig, - // size_t sig_len, - // unsigned int * info - // ) - - // sig_len = cx_ecschnorr_sign(&private_key, - // CX_ECSCHNORR_BIP0340 | CX_RND_TRNG, - // CX_SHA256, - // "somemessagefixme", - // sizeof("somemessagefixme"), - // G_context.tx_info.signature, - // sizeof(G_context.tx_info.signature), - // &info); + calc_sighash(&G_context.tx_info.transaction, G_context.tx_info.transaction.tx_inputs, G_context.sighash); + sig_len = cx_ecschnorr_sign(&private_key, + CX_ECSCHNORR_BIP0340 | CX_RND_TRNG, + CX_SHA256, + G_context.sighash, + 32, + G_context.tx_info.signature, + sizeof(G_context.tx_info.signature), + &info); PRINTF("Signature: %.*H\n", sig_len, G_context.tx_info.signature); } CATCH_OTHER(e) { diff --git a/src/handler/sign_tx.c b/src/handler/sign_tx.c index dbb8d03..9282091 100644 --- a/src/handler/sign_tx.c +++ b/src/handler/sign_tx.c @@ -7,6 +7,7 @@ #include "cx.h" #include "sign_tx.h" +#include "../apdu/dispatcher.h" #include "../sw.h" #include "../globals.h" #include "../crypto.h" @@ -15,8 +16,8 @@ #include "../transaction/types.h" #include "../transaction/deserialize.h" -int handler_sign_tx(buffer_t *cdata, uint8_t chunk, bool more) { - if (chunk == 0) { // first APDU, parse BIP32 path +int handler_sign_tx(buffer_t *cdata, uint8_t type, bool more) { + if (type == 0) { // first APDU, parse BIP32 path explicit_bzero(&G_context, sizeof(G_context)); G_context.req_type = CONFIRM_TRANSACTION; G_context.state = STATE_NONE; @@ -40,12 +41,41 @@ int handler_sign_tx(buffer_t *cdata, uint8_t chunk, bool more) { if (G_context.tx_info.raw_tx_len + cdata->size > sizeof(G_context.tx_info.raw_tx)) { return io_send_sw(SW_WRONG_TX_LENGTH); } - if (!buffer_move(cdata, - G_context.tx_info.raw_tx + G_context.tx_info.raw_tx_len, - cdata->size)) { - return io_send_sw(SW_TX_PARSING_FAIL); + + // Parse as we go + if (type == P1_OUTPUTS) { + // Outputs + if (G_context.tx_info.transaction.tx_output_len >= sizeof(G_context.tx_info.transaction.tx_outputs)) { + // Too many outputs! + return io_send_sw(SW_TX_PARSING_FAIL); + } + + parser_status_e err = transaction_output_deserialize(cdata, &G_context.tx_info.transaction.tx_outputs[G_context.tx_info.transaction.tx_output_len]); + + if (err != PARSING_OK) { + return io_send_sw(err); + } else { + G_context.tx_info.transaction.tx_output_len++; + } + + } else if (type == P1_INPUTS) { + // Inputs + if (G_context.tx_info.transaction.tx_input_len >= sizeof(G_context.tx_info.transaction.tx_inputs)) { + // Too many inputs! + return io_send_sw(SW_TX_PARSING_FAIL); + } + + parser_status_e err = transaction_input_deserialize(cdata, &G_context.tx_info.transaction.tx_inputs[G_context.tx_info.transaction.tx_input_len]); + + if (err < 0) { + return io_send_sw(SW_TX_PARSING_FAIL); + } else { + G_context.tx_info.transaction.tx_input_len++; + } + + } else { + return io_send_sw(SW_WRONG_P1P2); } - G_context.tx_info.raw_tx_len += cdata->size; if (more) { // more APDUs with transaction part are expected. diff --git a/src/handler/sign_tx.h b/src/handler/sign_tx.h index 93f1db6..29cfde4 100644 --- a/src/handler/sign_tx.h +++ b/src/handler/sign_tx.h @@ -14,12 +14,12 @@ * * @param[in,out] cdata * Command data with BIP32 path and raw transaction serialized. - * @param[in] chunk - * Index number of the APDU chunk. + * @param[in] type + * Type of data we are getting * @param[in] more * Whether more APDU chunk to be received or not. * * @return zero or positive integer if success, negative integer otherwise. * */ -int handler_sign_tx(buffer_t *cdata, uint8_t chunk, bool more); +int handler_sign_tx(buffer_t *cdata, uint8_t type, bool more); diff --git a/src/import/blake2-impl.h b/src/import/blake2-impl.h new file mode 100644 index 0000000..730cf7d --- /dev/null +++ b/src/import/blake2-impl.h @@ -0,0 +1,158 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2_IMPL_H +#define BLAKE2_IMPL_H + +#include +#include + +#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) + #if defined(_MSC_VER) + #define BLAKE2_INLINE __inline + #elif defined(__GNUC__) + #define BLAKE2_INLINE __inline__ + #else + #define BLAKE2_INLINE + #endif +#else + #define BLAKE2_INLINE inline +#endif + +static BLAKE2_INLINE uint32_t load32( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint32_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return (( uint32_t )( p[0] ) << 0) | + (( uint32_t )( p[1] ) << 8) | + (( uint32_t )( p[2] ) << 16) | + (( uint32_t )( p[3] ) << 24) ; +#endif +} + +static BLAKE2_INLINE uint64_t load64( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint64_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return (( uint64_t )( p[0] ) << 0) | + (( uint64_t )( p[1] ) << 8) | + (( uint64_t )( p[2] ) << 16) | + (( uint64_t )( p[3] ) << 24) | + (( uint64_t )( p[4] ) << 32) | + (( uint64_t )( p[5] ) << 40) | + (( uint64_t )( p[6] ) << 48) | + (( uint64_t )( p[7] ) << 56) ; +#endif +} + +static BLAKE2_INLINE uint16_t load16( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint16_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return ( uint16_t )((( uint32_t )( p[0] ) << 0) | + (( uint32_t )( p[1] ) << 8)); +#endif +} + +static BLAKE2_INLINE void store16( void *dst, uint16_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + *p++ = ( uint8_t )w; w >>= 8; + *p++ = ( uint8_t )w; +#endif +} + +static BLAKE2_INLINE void store32( void *dst, uint32_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); +#endif +} + +static BLAKE2_INLINE void store64( void *dst, uint64_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); + p[4] = (uint8_t)(w >> 32); + p[5] = (uint8_t)(w >> 40); + p[6] = (uint8_t)(w >> 48); + p[7] = (uint8_t)(w >> 56); +#endif +} + +static BLAKE2_INLINE uint64_t load48( const void *src ) +{ + const uint8_t *p = ( const uint8_t * )src; + return (( uint64_t )( p[0] ) << 0) | + (( uint64_t )( p[1] ) << 8) | + (( uint64_t )( p[2] ) << 16) | + (( uint64_t )( p[3] ) << 24) | + (( uint64_t )( p[4] ) << 32) | + (( uint64_t )( p[5] ) << 40) ; +} + +static BLAKE2_INLINE void store48( void *dst, uint64_t w ) +{ + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); + p[4] = (uint8_t)(w >> 32); + p[5] = (uint8_t)(w >> 40); +} + +static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c ) +{ + return ( w >> c ) | ( w << ( 32 - c ) ); +} + +static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c ) +{ + return ( w >> c ) | ( w << ( 64 - c ) ); +} + +/* prevents compiler optimizing out memset() */ +static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n) { + memset(v, 0, n); +} + +#endif diff --git a/src/import/blake2b.c b/src/import/blake2b.c new file mode 100644 index 0000000..bad3d4e --- /dev/null +++ b/src/import/blake2b.c @@ -0,0 +1,379 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include + +#include "blake2b.h" +#include "blake2-impl.h" + +static const uint64_t blake2b_IV[8] = +{ + 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL +}; + +static const uint8_t blake2b_sigma[12][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } +}; + + +static void blake2b_set_lastnode( blake2b_state *S ) +{ + S->f[1] = (uint64_t)-1; +} + +/* Some helper functions, not necessarily useful */ +static int blake2b_is_lastblock( const blake2b_state *S ) +{ + return S->f[0] != 0; +} + +static void blake2b_set_lastblock( blake2b_state *S ) +{ + if( S->last_node ) blake2b_set_lastnode( S ); + + S->f[0] = (uint64_t)-1; +} + +static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) +{ + S->t[0] += inc; + S->t[1] += ( S->t[0] < inc ); +} + +static void blake2b_init0( blake2b_state *S ) +{ + size_t i; + memset( S, 0, sizeof( blake2b_state ) ); + + for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i]; +} + +/* init xors IV with input parameter block */ +int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) +{ + const uint8_t *p = ( const uint8_t * )( P ); + size_t i; + + blake2b_init0( S ); + + /* IV XOR ParamBlock */ + for( i = 0; i < 8; ++i ) + S->h[i] ^= load64( p + sizeof( S->h[i] ) * i ); + + S->outlen = P->digest_length; + return 0; +} + + + +int blake2b_init( blake2b_state *S, size_t outlen ) +{ + blake2b_param P[1]; + + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store32( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + memset( P->reserved, 0, sizeof( P->reserved ) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2b_init_param( S, P ); +} + + +int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) +{ + blake2b_param P[1]; + + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; + + if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store32( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + memset( P->reserved, 0, sizeof( P->reserved ) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + + if( blake2b_init_param( S, P ) < 0 ) return -1; + + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset( block, 0, BLAKE2B_BLOCKBYTES ); + memcpy( block, key, keylen ); + blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); + secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + +#define G(r,i,a,b,c,d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2*i+0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2*i+1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while(0) + +#define ROUND(r) \ + do { \ + G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ + G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ + G(r,2,v[ 2],v[ 6],v[10],v[14]); \ + G(r,3,v[ 3],v[ 7],v[11],v[15]); \ + G(r,4,v[ 0],v[ 5],v[10],v[15]); \ + G(r,5,v[ 1],v[ 6],v[11],v[12]); \ + G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ + G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ + } while(0) + +static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) +{ + uint64_t m[16]; + uint64_t v[16]; + size_t i; + + for( i = 0; i < 16; ++i ) { + m[i] = load64( block + i * sizeof( m[i] ) ); + } + + for( i = 0; i < 8; ++i ) { + v[i] = S->h[i]; + } + + v[ 8] = blake2b_IV[0]; + v[ 9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + + ROUND( 0 ); + ROUND( 1 ); + ROUND( 2 ); + ROUND( 3 ); + ROUND( 4 ); + ROUND( 5 ); + ROUND( 6 ); + ROUND( 7 ); + ROUND( 8 ); + ROUND( 9 ); + ROUND( 10 ); + ROUND( 11 ); + + for( i = 0; i < 8; ++i ) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } +} + +#undef G +#undef ROUND + +int blake2b_update( blake2b_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + if( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = BLAKE2B_BLOCKBYTES - left; + if( inlen > fill ) + { + S->buflen = 0; + memcpy( S->buf + left, in, fill ); /* Fill buffer */ + blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); + blake2b_compress( S, S->buf ); /* Compress */ + in += fill; inlen -= fill; + while(inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress( S, in ); + in += BLAKE2B_BLOCKBYTES; + inlen -= BLAKE2B_BLOCKBYTES; + } + } + memcpy( S->buf + S->buflen, in, inlen ); + S->buflen += inlen; + } + return 0; +} + +int blake2b_final( blake2b_state *S, void *out, size_t outlen ) +{ + uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; + size_t i; + + if( out == NULL || outlen < S->outlen ) + return -1; + + if( blake2b_is_lastblock( S ) ) + return -1; + + blake2b_increment_counter( S, S->buflen ); + blake2b_set_lastblock( S ); + memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ + blake2b_compress( S, S->buf ); + + for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ + store64( buffer + sizeof( S->h[i] ) * i, S->h[i] ); + + memcpy( out, buffer, S->outlen ); + secure_zero_memory(buffer, sizeof(buffer)); + return 0; +} + +/* inlen, at least, should be uint64_t. Others can be size_t. */ +int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +{ + blake2b_state S[1]; + + /* Verify parameters */ + if ( NULL == in && inlen > 0 ) return -1; + + if ( NULL == out ) return -1; + + if( NULL == key && keylen > 0 ) return -1; + + if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; + + if( keylen > BLAKE2B_KEYBYTES ) return -1; + + if( keylen > 0 ) + { + if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; + } + else + { + if( blake2b_init( S, outlen ) < 0 ) return -1; + } + + blake2b_update( S, ( const uint8_t * )in, inlen ); + blake2b_final( S, out, outlen ); + return 0; +} + +int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) { + return blake2b(out, outlen, in, inlen, key, keylen); +} + +#if defined(SUPERCOP) +int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) +{ + return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 ); +} +#endif + +#if defined(BLAKE2B_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2B_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; + + for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) + key[i] = ( uint8_t )i; + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + buf[i] = ( uint8_t )i; + + /* Test simple API */ + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); + + if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2b_state S; + uint8_t * p = buf; + size_t mlen = i; + int err = 0; + + if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2b_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2b_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/src/import/blake2b.h b/src/import/blake2b.h new file mode 100644 index 0000000..72152ee --- /dev/null +++ b/src/import/blake2b.h @@ -0,0 +1,157 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2_H +#define BLAKE2_H + +#include +#include + +#include "cx.h" + +#if defined(_MSC_VER) +#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) +#else +#define BLAKE2_PACKED(x) x __attribute__((packed)) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + + // enum blake2s_constant + // { + // BLAKE2S_BLOCKBYTES = 64, + // BLAKE2S_OUTBYTES = 32, + // BLAKE2S_KEYBYTES = 32, + // BLAKE2S_SALTBYTES = 8, + // BLAKE2S_PERSONALBYTES = 8 + // }; + + // enum blake2b_constant + // { + // BLAKE2B_BLOCKBYTES = 128, + // BLAKE2B_OUTBYTES = 64, + // BLAKE2B_KEYBYTES = 64, + // BLAKE2B_SALTBYTES = 16, + // BLAKE2B_PERSONALBYTES = 16 + // }; + + // typedef struct blake2s_state__ + // { + // uint32_t h[8]; + // uint32_t t[2]; + // uint32_t f[2]; + // uint8_t buf[BLAKE2S_BLOCKBYTES]; + // size_t buflen; + // size_t outlen; + // uint8_t last_node; + // } blake2s_state; + + // typedef struct blake2b_state__ + // { + // uint64_t h[8]; + // uint64_t t[2]; + // uint64_t f[2]; + // uint8_t buf[BLAKE2B_BLOCKBYTES]; + // size_t buflen; + // size_t outlen; + // uint8_t last_node; + // } blake2b_state; + + // typedef struct blake2sp_state__ + // { + // blake2s_state S[8][1]; + // blake2s_state R[1]; + // uint8_t buf[8 * BLAKE2S_BLOCKBYTES]; + // size_t buflen; + // size_t outlen; + // } blake2sp_state; + + // typedef struct blake2bp_state__ + // { + // blake2b_state S[4][1]; + // blake2b_state R[1]; + // uint8_t buf[4 * BLAKE2B_BLOCKBYTES]; + // size_t buflen; + // size_t outlen; + // } blake2bp_state; + + + // BLAKE2_PACKED(struct blake2s_param__ + // { + // uint8_t digest_length; /* 1 */ + // uint8_t key_length; /* 2 */ + // uint8_t fanout; /* 3 */ + // uint8_t depth; /* 4 */ + // uint32_t leaf_length; /* 8 */ + // uint32_t node_offset; /* 12 */ + // uint16_t xof_length; /* 14 */ + // uint8_t node_depth; /* 15 */ + // uint8_t inner_length; /* 16 */ + // /* uint8_t reserved[0]; */ + // uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ + // uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ + // }); + + // typedef struct blake2s_param__ blake2s_param; + + BLAKE2_PACKED(struct blake2b_param__ + { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint32_t xof_length; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ + }); + + typedef struct blake2b_param__ blake2b_param; + + // typedef struct blake2xs_state__ + // { + // blake2s_state S[1]; + // blake2s_param P[1]; + // } blake2xs_state; + + // typedef struct blake2xb_state__ + // { + // blake2b_state S[1]; + // blake2b_param P[1]; + // } blake2xb_state; + + /* Padded structs result in a compile-time error */ + enum { + // BLAKE2_DUMMY_1 = 1/(sizeof(blake2s_param) == BLAKE2S_OUTBYTES), + BLAKE2_DUMMY_2 = 1/(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) + }; + + /* Streaming API */ + int blake2b_init( blake2b_state *S, size_t outlen ); + int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2b_init_param( blake2b_state *S, const blake2b_param *P ); + int blake2b_update( blake2b_state *S, const void *in, size_t inlen ); + int blake2b_final( blake2b_state *S, void *out, size_t outlen ); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/src/sighash.c b/src/sighash.c index 4247cfa..891fd4a 100644 --- a/src/sighash.c +++ b/src/sighash.c @@ -1,29 +1,22 @@ #include #include +#include -#include "cx.h" +#include "./import/blake2-impl.h" +#include "./import/blake2b.h" #include "./transaction/types.h" #include "./common/buffer.h" #include "./common/write.h" +#include "./crypto.h" +#include "globals.h" +#include "./constants.h" -const cx_hash_info_t cx_blake2b_info; -const char* signing_key = "TransactionSigningHash"; +// const cx_hash_info_t cx_blake2b_info; +uint8_t outer_buffer[32] = {0}; +uint8_t inner_buffer[32] = {0}; -static void hash_update(cx_blake2b_t* hash, uint8_t* data, size_t len) { - // cx_hash(hash, 0, data, len, NULL, 0); - cx_blake2b_update(hash, data, len); -} - -static void hash_finalize(cx_blake2b_t* hash, uint8_t* out) { - // cx_hash(hash, CX_LAST, NULL, 0, out, 32); - cx_blake2b_final(hash, out); -} - -static cx_err_t cx_blake2b_init3_no_throw(cx_blake2b_t* hash, - size_t size, - uint8_t* key, - size_t key_len) { +static int hash_init(blake2b_state* hash, size_t size, uint8_t* key, size_t key_len) { if (key == NULL && key_len != 0) { goto err; } @@ -31,161 +24,201 @@ static cx_err_t cx_blake2b_init3_no_throw(cx_blake2b_t* hash, if (size % 8 != 0 || size < 8 || size > 512) { goto err; } - memset(hash, 0, sizeof(cx_blake2b_t)); + memset(hash, 0, sizeof(blake2b_state)); size = size / 8; - hash->output_size = size; - hash->header.info = &cx_blake2b_info; + // hash->output_size = size; + // hash->header.info = &cx_blake2b_info; - if (blake2b_init_key(&hash->ctx, size, key, key_len) < 0) { + if (blake2b_init_key(hash, size, key, key_len) < 0) { goto err; } - return CX_OK; + return 0; err: - return CX_INVALID_PARAMETER; + return -1; +} + +static void hash_update(blake2b_state* hash, uint8_t* data, size_t len) { + // cx_hash(hash, 0, data, len, NULL, 0); + // cx_blake2b_update(hash, data, len); + blake2b_update(hash, data, len); +} + +static void hash_finalize(blake2b_state* hash, uint8_t* out) { + // cx_hash(hash, CX_LAST, NULL, 0, out, 32); + // cx_blake2b_final(hash, out); + blake2b_final(hash, out, 32); } static void calc_prev_outputs_hash(transaction_t* tx, uint8_t* out_hash) { - cx_blake2b_t inner_hash_writer; - cx_blake2b_init3_no_throw(&inner_hash_writer, 256, (uint8_t*) signing_key, 22); + blake2b_state inner_hash_writer; + hash_init(&inner_hash_writer, 256, (uint8_t*) SIGNING_KEY, 22); for (size_t i = 0; i < tx->tx_input_len; i++) { - uint8_t output_index_le[4] = {tx->tx_inputs[i].index, 0x00, 0x00, 0x00}; - + memset(inner_buffer, 0, sizeof(inner_buffer)); + write_u32_le(inner_buffer, 0, tx->tx_inputs[i].index); hash_update(&inner_hash_writer, tx->tx_inputs[i].tx_id, 32); - hash_update(&inner_hash_writer, output_index_le, 4); + hash_update(&inner_hash_writer, inner_buffer, 4); } hash_finalize(&inner_hash_writer, out_hash); } static void calc_sequences_hash(transaction_t* tx, uint8_t* out_hash) { - cx_blake2b_t inner_hash_writer; - cx_blake2b_init3_no_throw(&inner_hash_writer, 256, (uint8_t*) signing_key, 22); - - uint8_t curr_sequences_hash[8] = {0}; + blake2b_state inner_hash_writer; + hash_init(&inner_hash_writer, 256, (uint8_t*) SIGNING_KEY, 22); for (size_t i = 0; i < tx->tx_input_len; i++) { - write_u64_le(curr_sequences_hash, 0, tx->tx_inputs[i].sequence); - hash_update(&inner_hash_writer, curr_sequences_hash, 8); - memset(curr_sequences_hash, 0, sizeof(curr_sequences_hash)); + memset(inner_buffer, 0, sizeof(inner_buffer)); + write_u64_le(inner_buffer, 0, tx->tx_inputs[i].sequence); + hash_update(&inner_hash_writer, inner_buffer, 8); + memset(inner_buffer, 0, sizeof(inner_buffer)); } hash_finalize(&inner_hash_writer, out_hash); } static void calc_sig_op_count_hash(transaction_t* tx, uint8_t* out_hash) { - cx_blake2b_t inner_hash_writer; - cx_blake2b_init3_no_throw(&inner_hash_writer, 256, (uint8_t*) signing_key, 22); + blake2b_state inner_hash_writer; + hash_init(&inner_hash_writer, 256, (uint8_t*) SIGNING_KEY, 22); for (size_t i = 0; i < tx->tx_input_len; i++) { - uint8_t dummy_sig_op_count[1] = {0x01}; - - hash_update(&inner_hash_writer, dummy_sig_op_count, 1); + memset(inner_buffer, 1, 1); + hash_update(&inner_hash_writer, inner_buffer, 1); + memset(inner_buffer, 0, sizeof(inner_buffer)); } hash_finalize(&inner_hash_writer, out_hash); } static void calc_outputs_hash(transaction_t* tx, uint8_t* out_hash) { - cx_blake2b_t inner_hash_writer; - cx_blake2b_init3_no_throw(&inner_hash_writer, 256, (uint8_t*) signing_key, 22); - - uint8_t buf[8] = {0}; + blake2b_state inner_hash_writer; + hash_init(&inner_hash_writer, 256, (uint8_t*) SIGNING_KEY, 22); for (size_t i = 0; i < tx->tx_output_len; i++) { - memset(buf, 0, sizeof(buf)); + memset(inner_buffer, 0, sizeof(inner_buffer)); - write_u64_le(buf, 0, tx->tx_outputs[i].value); - hash_update(&inner_hash_writer, buf, 8); // Write the output value - memset(buf, 0, sizeof(buf)); + write_u64_le(inner_buffer, 0, tx->tx_outputs[i].value); + hash_update(&inner_hash_writer, inner_buffer, 8); // Write the output value + memset(inner_buffer, 0, sizeof(inner_buffer)); - hash_update(&inner_hash_writer, buf, 2); // Write the output script version, assume 0 + hash_update(&inner_hash_writer, inner_buffer, 2); // Write the output script version, assume 0 // First byte is always the length of the following public key // Last byte is always 0xac (op code for normal transactions) uint8_t script_len = tx->tx_outputs[i].script_public_key[0] + 2; - write_u64_le(buf, 0, script_len); // Write the number of bytes of the script public key - hash_update(&inner_hash_writer, buf, 8); + write_u64_le(inner_buffer, 0, script_len); // Write the number of bytes of the script public key + hash_update(&inner_hash_writer, inner_buffer, 8); hash_update(&inner_hash_writer, tx->tx_outputs[i].script_public_key, script_len); } hash_finalize(&inner_hash_writer, out_hash); } -void calc_sighash(transaction_t* tx, transaction_input_t* txin, uint8_t* out_hash) { - uint8_t buf[32] = {0}; +static bool calc_txin_script_public_key(transaction_input_t* txin, uint8_t* out_hash) { + cx_ecfp_private_key_t private_key = {0}; + cx_ecfp_public_key_t public_key = {0}; + + G_context.bip32_path[0] = 0x8000002C; + G_context.bip32_path[1] = 0x8001b207; + G_context.bip32_path[2] = 0x80000000; + G_context.bip32_path[3] = txin->derivation_path[0]; + G_context.bip32_path[4] = txin->derivation_path[1]; + + G_context.bip32_path_len = 5; + + // derive private key according to BIP32 path + int error = crypto_derive_private_key(&private_key, + G_context.pk_info.chain_code, + G_context.bip32_path, + G_context.bip32_path_len); + if (error != 0) { + // TODO: Do something here + // return io_send_sw(error); + } + // generate corresponding public key + crypto_init_public_key(&private_key, &public_key, G_context.pk_info.raw_public_key); - cx_blake2b_t sighash; + out_hash[0] = 0x20; + memmove(out_hash + 1, G_context.pk_info.raw_public_key, 32); + out_hash[33] = 0xac; - cx_blake2b_init3_no_throw(&sighash, 256, (uint8_t*) signing_key, 22); + return true; +} + +void calc_sighash(transaction_t* tx, transaction_input_t* txin, uint8_t* out_hash) { + blake2b_state sighash; + + hash_init(&sighash, 256, (uint8_t*) SIGNING_KEY, 22); // Write version, little endian, 2 bytes - write_u16_le(buf, 0, tx->version); - hash_update(&sighash, buf, 2); - memset(buf, 0, sizeof(buf)); + write_u16_le(outer_buffer, 0, tx->version); + hash_update(&sighash, outer_buffer, 2); + memset(outer_buffer, 0, sizeof(outer_buffer)); // Write previous outputs hash - calc_prev_outputs_hash(tx, buf); - hash_update(&sighash, buf, 32); - memset(buf, 0, sizeof(buf)); + calc_prev_outputs_hash(tx, outer_buffer); + hash_update(&sighash, outer_buffer, 32); + memset(outer_buffer, 0, sizeof(outer_buffer)); // Write sequence hash - calc_sequences_hash(tx, buf); - hash_update(&sighash, buf, 32); - memset(buf, 0, sizeof(buf)); + calc_sequences_hash(tx, outer_buffer); + hash_update(&sighash, outer_buffer, 32); + memset(outer_buffer, 0, sizeof(outer_buffer)); // Write sig op count hash - calc_sig_op_count_hash(tx, buf); - hash_update(&sighash, buf, 32); - memset(buf, 0, sizeof(buf)); + calc_sig_op_count_hash(tx, outer_buffer); + hash_update(&sighash, outer_buffer, 32); + memset(outer_buffer, 0, sizeof(outer_buffer)); // Write Hash of the outpoint hash_update(&sighash, txin->tx_id, 32); - write_u32_le(buf, 0, txin->index); - hash_update(&sighash, buf, 4); - memset(buf, 0, sizeof(buf)); + write_u32_le(outer_buffer, 0, txin->index); + hash_update(&sighash, outer_buffer, 4); + memset(outer_buffer, 0, sizeof(outer_buffer)); - hash_update(&sighash, buf, 2); // Write input script version, assume 0 + hash_update(&sighash, outer_buffer, 2); // Write input script version, assume 0 // Write input's script_public_key. Length as uint64_t followed by script // count (1 byte) + public key (32/33 byte) + op (1 byte) - uint8_t script_len = txin->script_public_key[0] + 2; - write_u64_le(buf, 0, script_len); - hash_update(&sighash, buf, 8); - hash_update(&sighash, txin->script_public_key, script_len); - memset(buf, 0, sizeof(buf)); + uint64_t script_len = 34; + write_u64_le(outer_buffer, 0, script_len); + hash_update(&sighash, outer_buffer, 8); + + calc_txin_script_public_key(txin, outer_buffer); + hash_update(&sighash, outer_buffer, script_len); + memset(outer_buffer, 0, sizeof(outer_buffer)); // Write input's value - write_u64_le(buf, 0, txin->value); - hash_update(&sighash, buf, 8); - memset(buf, 0, sizeof(buf)); + write_u64_le(outer_buffer, 0, txin->value); + hash_update(&sighash, outer_buffer, 8); + memset(outer_buffer, 0, sizeof(outer_buffer)); // Write input's sequence number - write_u64_le(buf, 0, txin->sequence); - hash_update(&sighash, buf, 8); - memset(buf, 0, sizeof(buf)); + write_u64_le(outer_buffer, 0, txin->sequence); + hash_update(&sighash, outer_buffer, 8); + memset(outer_buffer, 0, sizeof(outer_buffer)); // Write sigopcount, assume 1 - buf[0] = 0x01; - hash_update(&sighash, buf, 1); - memset(buf, 0, sizeof(buf)); + outer_buffer[0] = 0x01; + hash_update(&sighash, outer_buffer, 1); + memset(outer_buffer, 0, sizeof(outer_buffer)); // Write outputs hash - calc_outputs_hash(tx, buf); - hash_update(&sighash, buf, 32); - memset(buf, 0, sizeof(buf)); + calc_outputs_hash(tx, outer_buffer); + hash_update(&sighash, outer_buffer, 32); + memset(outer_buffer, 0, sizeof(outer_buffer)); // Write last bits of data, assuming 0 - hash_update(&sighash, buf, 8); // Write locktime of 0 - hash_update(&sighash, buf, 20); // Write subnetwork Id, assume zero hash - hash_update(&sighash, buf, 8); // Write gas, assume 0 - hash_update(&sighash, buf, 32); // Write payload hash, assume 0 + hash_update(&sighash, outer_buffer, 8); // Write locktime of 0 + hash_update(&sighash, outer_buffer, 20); // Write subnetwork Id, assume zero hash + hash_update(&sighash, outer_buffer, 8); // Write gas, assume 0 + hash_update(&sighash, outer_buffer, 32); // Write payload hash, assume 0 // Write sighash type, assume SigHashAll => 0x01 - buf[0] = 0x01; - hash_update(&sighash, buf, 1); + outer_buffer[0] = 0x01; + hash_update(&sighash, outer_buffer, 1); hash_finalize(&sighash, out_hash); } diff --git a/src/transaction/deserialize.c b/src/transaction/deserialize.c index 4da9ddb..6283b8d 100644 --- a/src/transaction/deserialize.c +++ b/src/transaction/deserialize.c @@ -1,44 +1,83 @@ +#include #include "deserialize.h" #include "utils.h" #include "types.h" #include "../common/buffer.h" -int deserialize_output(buffer_t *buf, transaction_output_t *txout) { +parser_status_e transaction_output_deserialize(buffer_t *buf, transaction_output_t *txout) { + // 8 bytes if (!buffer_read_u64(buf, &txout->value, BE)) { - return -70; + return OUTPUTS_PARSING_ERROR; } - txout->script_public_key = (uint8_t *) (buf->ptr + buf->offset); + size_t script_len = (size_t) *(buf->ptr + buf->offset); + // Can only be length 32 or 33. Fail it otherwise: + if (script_len == 0x20 || script_len == 0x21) { + if (!buffer_can_read(buf, script_len + 2)) { + return OUTPUTS_PARSING_ERROR; + } + + if (*(buf->ptr + buf->offset + script_len + 1) != 0xac) { + return OUTPUTS_PARSING_ERROR; + } + + memcpy(txout->script_public_key, buf->ptr + buf->offset, script_len + 2); + } else { + return OUTPUTS_PARSING_ERROR; + } - if (!buffer_seek_cur(buf, 34)) { - return -80; + if (!buffer_seek_cur(buf, script_len + 2)) { + return OUTPUTS_PARSING_ERROR; } - return 1; + // Total: 8 + 32|33 = 40|41 bytes + return buf->size - buf->offset == 0 ? PARSING_OK : OUTPUTS_PARSING_ERROR; } -int deserialize_input(buffer_t *buf, transaction_input_t *txin) { +parser_status_e transaction_input_deserialize(buffer_t *buf, transaction_input_t *txin) { + // 8 bytes if (!buffer_read_u64(buf, &txin->value, BE)) { - return 0; + return INPUTS_PARSING_ERROR; } - txin->tx_id = (uint8_t *) (buf->ptr + buf->offset); + if (!buffer_can_read(buf, 32)) { + // Not enough input + return INPUTS_PARSING_ERROR; + } + + // 32 bytes + memcpy(txin->tx_id, buf->ptr, 32); if (!buffer_seek_cur(buf, 32)) { - return 0; + return INPUTS_PARSING_ERROR; } uint8_t address_type = -1; - buffer_read_u8(buf, &address_type); + // 1 byte + if (!buffer_read_u8(buf, &address_type)) { + return INPUTS_PARSING_ERROR; + } uint32_t address_index = -1; - buffer_read_u32(buf, &address_index, BE); + // 4 bytes + if (!buffer_read_u32(buf, &address_index, BE)) { + return INPUTS_PARSING_ERROR; + } if (address_type < 0 || address_index < 0) { - return 0; + return INPUTS_PARSING_ERROR; } - return 1; + txin->derivation_path[0] = (uint32_t) address_type; + txin->derivation_path[1] = address_index; + + // 1 byte + if (!buffer_read_u8(buf, &txin->index)) { + return INPUTS_PARSING_ERROR; + } + + // Total: 46 bytes + return buf->size - buf->offset == 0 ? PARSING_OK : INPUTS_PARSING_ERROR; } parser_status_e transaction_deserialize(buffer_t *buf, transaction_t *tx) { @@ -52,10 +91,8 @@ parser_status_e transaction_deserialize(buffer_t *buf, transaction_t *tx) { return OUTPUTS_LENGTH_PARSING_ERROR; } - tx->tx_output_len = n_output; - - // Must be 1 or 2 outputs - if (tx->tx_output_len < 1 || tx->tx_output_len > 2) { + // Must be 1 or 2 outputs, must match the number of outputs we parsed + if (tx->tx_output_len < 1 || tx->tx_output_len > 2 || n_output != tx->tx_output_len) { return OUTPUTS_LENGTH_PARSING_ERROR; } @@ -64,25 +101,11 @@ parser_status_e transaction_deserialize(buffer_t *buf, transaction_t *tx) { return INPUTS_LENGTH_PARSING_ERROR; } - tx->tx_input_len = n_input; - - if (tx->tx_input_len < 1 || tx->tx_input_len > 2) { + // Must be at least 1, must match the number of inputs we parsed + if (tx->tx_input_len < 1 || n_input != tx->tx_input_len) { return INPUTS_LENGTH_PARSING_ERROR; } - for (int i = 0; i < (int) tx->tx_output_len && i < 2; i++) { - int res = deserialize_output(buf, &tx->tx_outputs[i]); - if (res < 0) { - return res; - } - } - - for (int i = 0; i < (int) tx->tx_input_len; i++) { - if (!deserialize_input(buf, &tx->tx_inputs[i])) { - return INPUTS_PARSING_ERROR; - } - } - uint32_t lastbits = 0; int diff = buf->size - buf->offset; if (buf->size - buf->offset == 4) { diff --git a/src/transaction/deserialize.h b/src/transaction/deserialize.h index e8ebe4e..2dde780 100644 --- a/src/transaction/deserialize.h +++ b/src/transaction/deserialize.h @@ -15,3 +15,7 @@ * */ parser_status_e transaction_deserialize(buffer_t *buf, transaction_t *tx); + +parser_status_e transaction_output_deserialize(buffer_t *buf, transaction_output_t *txout); + +parser_status_e transaction_input_deserialize(buffer_t *buf, transaction_input_t *txin); diff --git a/src/transaction/types.h b/src/transaction/types.h index 84692e4..9145656 100644 --- a/src/transaction/types.h +++ b/src/transaction/types.h @@ -19,6 +19,10 @@ typedef enum { INPUTS_LENGTH_PARSING_ERROR = -9 } parser_status_e; +typedef enum { + SIGHASH_PARSING_OK = 1 +} sighash_status_e; + typedef enum { RECEIVE = 0, // For receive addresses CHANGE = 1 // For change addresses @@ -37,18 +41,19 @@ typedef struct { } utxo_entry_t; typedef struct { + // uint8_t* address_type; + // uint32_t* address_index; + // uint8_t sig_op_count; uint32_t derivation_path[2]; uint64_t sequence; - uint8_t sig_op_count; uint64_t value; - uint8_t* script_public_key; - uint8_t* tx_id; // 32 bytes + uint8_t tx_id[32]; // 32 bytes uint8_t index; // check if uint8_t might suffice. in practice, we don't need 32bits for index } transaction_input_t; typedef struct { uint64_t value; - uint8_t* script_public_key; // In hex: 20 + public_key_hex + ac + uint8_t script_public_key[35]; // In hex: 20 + public_key_hex + ac (34/35 bytes total) } transaction_output_t; typedef struct { diff --git a/src/types.h b/src/types.h index 8773260..9705ba8 100644 --- a/src/types.h +++ b/src/types.h @@ -88,4 +88,5 @@ typedef struct { request_type_e req_type; /// user request uint32_t bip32_path[MAX_BIP32_PATH]; /// BIP32 path uint8_t bip32_path_len; /// length of BIP32 path + uint8_t sighash[32]; } global_ctx_t; diff --git a/src/ui/action/validate.c b/src/ui/action/validate.c index c3fb6f8..75d6ef3 100644 --- a/src/ui/action/validate.c +++ b/src/ui/action/validate.c @@ -22,7 +22,7 @@ void validate_transaction(bool choice) { int error = crypto_sign_message(); if (error != 0) { G_context.state = STATE_NONE; - io_send_sw(SW_SIGNATURE_FAIL); + io_send_sw(error); } else { helper_send_response_sig(); } diff --git a/tests/application_client/kaspa_command_sender.py b/tests/application_client/kaspa_command_sender.py index 916c3c7..935f871 100644 --- a/tests/application_client/kaspa_command_sender.py +++ b/tests/application_client/kaspa_command_sender.py @@ -15,6 +15,8 @@ class P1(IntEnum): # Parameter 1 for first APDU number. P1_START = 0x00 + P1_OUTPUTS = 0x01 + P1_INPUTS = 0x02 # Parameter 1 for maximum APDU number. P1_MAX = 0x03 # Parameter 1 for screen confirmation for GET_PUBLIC_KEY. @@ -111,7 +113,7 @@ def sign_tx(self, transaction: Transaction) -> Generator[None, None, None]: for txoutput in transaction.outputs: self.backend.exchange(cla=CLA, ins=InsType.SIGN_TX, - p1=1, + p1=P1.P1_OUTPUTS, p2=P2.P2_MORE, data=txoutput.serialize()) @@ -120,14 +122,14 @@ def sign_tx(self, transaction: Transaction) -> Generator[None, None, None]: if i < len(transaction.inputs) - 1: self.backend.exchange(cla=CLA, ins=InsType.SIGN_TX, - p1=2, + p1=P1.P1_INPUTS, p2=P2.P2_MORE, data=txinput.serialize()) # Last input, we'll end here with self.backend.exchange_async(cla=CLA, ins=InsType.SIGN_TX, - p1=2, + p1=P1.P1_INPUTS, p2=P2.P2_LAST, data=txinput.serialize()) as response: diff --git a/tests/application_client/kaspa_transaction.py b/tests/application_client/kaspa_transaction.py index ade8829..dfc48bf 100644 --- a/tests/application_client/kaspa_transaction.py +++ b/tests/application_client/kaspa_transaction.py @@ -11,19 +11,22 @@ class TransactionInput: def __init__(self, value: int, tx_id: str, + index: int, address_type: int, address_index: int): self.value: int = value # 8 bytes self.tx_id: bytes = bytes.fromhex(tx_id) # 32 bytes self.address_type: int = address_type # 1 byte self.address_index:int = address_index # 4 bytes + self.index: int = index # 1 byte def serialize(self) -> bytes: return b"".join([ self.value.to_bytes(8, byteorder="big"), self.tx_id, self.address_type.to_bytes(1, byteorder="big"), - self.address_index.to_bytes(4, byteorder="big") + self.address_index.to_bytes(4, byteorder="big"), + self.index.to_bytes(1, byteorder="big") ]) @classmethod diff --git a/tests/snapshots/nanos/test_sign_tx_long_tx/00001.png b/tests/snapshots/nanos/test_sign_tx_long_tx/00001.png index d085b9a..fea2d47 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_long_tx/00001.png and b/tests/snapshots/nanos/test_sign_tx_long_tx/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_long_tx/00002.png b/tests/snapshots/nanos/test_sign_tx_long_tx/00002.png index ef16fe9..75c39aa 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_long_tx/00002.png and b/tests/snapshots/nanos/test_sign_tx_long_tx/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_long_tx/00003.png b/tests/snapshots/nanos/test_sign_tx_long_tx/00003.png index f9b2b00..990dd21 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_long_tx/00003.png and b/tests/snapshots/nanos/test_sign_tx_long_tx/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_long_tx/00004.png b/tests/snapshots/nanos/test_sign_tx_long_tx/00004.png index efffc68..044c844 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_long_tx/00004.png and b/tests/snapshots/nanos/test_sign_tx_long_tx/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_long_tx/00005.png b/tests/snapshots/nanos/test_sign_tx_long_tx/00005.png index 89d50d9..ddddd35 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_long_tx/00005.png and b/tests/snapshots/nanos/test_sign_tx_long_tx/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00001.png b/tests/snapshots/nanos/test_sign_tx_refused/00001.png index d085b9a..fea2d47 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_refused/00001.png and b/tests/snapshots/nanos/test_sign_tx_refused/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00002.png b/tests/snapshots/nanos/test_sign_tx_refused/00002.png index ef16fe9..75c39aa 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_refused/00002.png and b/tests/snapshots/nanos/test_sign_tx_refused/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00003.png b/tests/snapshots/nanos/test_sign_tx_refused/00003.png index f9b2b00..990dd21 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_refused/00003.png and b/tests/snapshots/nanos/test_sign_tx_refused/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00004.png b/tests/snapshots/nanos/test_sign_tx_refused/00004.png index efffc68..044c844 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_refused/00004.png and b/tests/snapshots/nanos/test_sign_tx_refused/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00005.png b/tests/snapshots/nanos/test_sign_tx_refused/00005.png index 89d50d9..ddddd35 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_refused/00005.png and b/tests/snapshots/nanos/test_sign_tx_refused/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00001.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00001.png index d085b9a..fea2d47 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_short_tx/00001.png and b/tests/snapshots/nanos/test_sign_tx_short_tx/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00002.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00002.png index ef16fe9..75c39aa 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_short_tx/00002.png and b/tests/snapshots/nanos/test_sign_tx_short_tx/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00003.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00003.png index f9b2b00..990dd21 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_short_tx/00003.png and b/tests/snapshots/nanos/test_sign_tx_short_tx/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00004.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00004.png index efffc68..044c844 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_short_tx/00004.png and b/tests/snapshots/nanos/test_sign_tx_short_tx/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00005.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00005.png index 89d50d9..ddddd35 100644 Binary files a/tests/snapshots/nanos/test_sign_tx_short_tx/00005.png and b/tests/snapshots/nanos/test_sign_tx_short_tx/00005.png differ diff --git a/tests/test_sign_cmd.py b/tests/test_sign_cmd.py index 3342362..ddb68cb 100644 --- a/tests/test_sign_cmd.py +++ b/tests/test_sign_cmd.py @@ -23,19 +23,20 @@ def test_sign_tx_short_tx(firmware, backend, navigator, test_name): # Create the transaction that will be sent to the device for signing transaction = Transaction( - version=1, + version=0, inputs=[ TransactionInput( - value=100000, - tx_id="e119d53514c1b0e2efce7a89e3d1d5d6cd73582ea20687641c8fdccb6060a9ad", + value=1100000, + tx_id="40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70", address_type=0, - address_index=0 + address_index=0, + index=0 ) ], outputs=[ TransactionOutput( - value=90000, - script_public_key="20e9edf67a325868ecc7cd8519e6ca5265e65b7d10f56066461ceabf0c2bc1c5adac" + value=1090000, + script_public_key="2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac" ) ] ) @@ -77,19 +78,20 @@ def test_sign_tx_long_tx(firmware, backend, navigator, test_name): _, public_key, _, _ = unpack_get_public_key_response(rapdu.data) transaction = Transaction( - version=1, + version=0, inputs=[ TransactionInput( - value=100000, - tx_id="e119d53514c1b0e2efce7a89e3d1d5d6cd73582ea20687641c8fdccb6060a9ad", + value=1100000, + tx_id="40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70", address_type=0, - address_index=0 + address_index=0, + index=0 ) ], outputs=[ TransactionOutput( - value=90000, - script_public_key="20e9edf67a325868ecc7cd8519e6ca5265e65b7d10f56066461ceabf0c2bc1c5adac" + value=1090000, + script_public_key="2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac" ) ] ) @@ -124,19 +126,20 @@ def test_sign_tx_refused(firmware, backend, navigator, test_name): _, pub_key, _, _ = unpack_get_public_key_response(rapdu.data) transaction = Transaction( - version=1, + version=0, inputs=[ TransactionInput( - value=100000, - tx_id="e119d53514c1b0e2efce7a89e3d1d5d6cd73582ea20687641c8fdccb6060a9ad", + value=1100000, + tx_id="40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70", address_type=0, - address_index=0 + address_index=0, + index=0 ) ], outputs=[ TransactionOutput( - value=90000, - script_public_key="20e9edf67a325868ecc7cd8519e6ca5265e65b7d10f56066461ceabf0c2bc1c5adac" + value=1090000, + script_public_key="2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac" ) ] ) diff --git a/unit-tests/CMakeLists.txt b/unit-tests/CMakeLists.txt index 46f4bf3..2d640db 100644 --- a/unit-tests/CMakeLists.txt +++ b/unit-tests/CMakeLists.txt @@ -33,7 +33,7 @@ if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt. ") endif() -add_compile_definitions(TEST HAVE_HASH HAVE_BLAKE2 HAVE_ECC USB_SEGMENT_SIZE=64) +add_compile_definitions(TEST HAVE_HASH HAVE_BLAKE2 HAVE_ECC USB_SEGMENT_SIZE=64 IO_SEPROXYHAL_BUFFER_SIZE_B=128) include_directories(../src) # include_directories(mock_includes) @@ -55,7 +55,7 @@ add_executable(test_tx_parser test_tx_parser.c) add_executable(test_tx_utils test_tx_utils.c) add_library(address SHARED ../src/address.c) -add_library(cx_blake2b SHARED /opt/ledger-secure-sdk/lib_cxng/src/cx_blake2b.c) +add_library(blake2b SHARED ../src/import/blake2b.c) add_library(cashaddr SHARED ../src/cashaddr.c) add_library(base58 SHARED ../src/common/base58.c) add_library(bip32 SHARED ../src/common/bip32.c) @@ -75,7 +75,7 @@ target_link_libraries(test_base58 PUBLIC cmocka gcov base58) target_link_libraries(test_bip32 PUBLIC cmocka gcov bip32 read) target_link_libraries(test_buffer PUBLIC cmocka gcov buffer bip32 varint write read) target_link_libraries(test_format PUBLIC cmocka gcov format) -target_link_libraries(test_sighash PUBLIC cmocka gcov sighash cx_blake2b write) +target_link_libraries(test_sighash PUBLIC cmocka gcov sighash blake2b write) target_link_libraries(test_write PUBLIC cmocka gcov write) target_link_libraries(test_apdu_parser PUBLIC cmocka gcov apdu_parser) target_link_libraries(test_tx_parser PUBLIC diff --git a/unit-tests/mock_includes/ux.h b/unit-tests/mock_includes/ux.h new file mode 100644 index 0000000..0e7a95c --- /dev/null +++ b/unit-tests/mock_includes/ux.h @@ -0,0 +1,3 @@ +#define CX_CURVE_256K1 0x21 + +typedef struct ux_state_s ux_state_t; \ No newline at end of file diff --git a/unit-tests/test_sighash.c b/unit-tests/test_sighash.c index 93883ee..83c755b 100644 --- a/unit-tests/test_sighash.c +++ b/unit-tests/test_sighash.c @@ -6,14 +6,14 @@ #include +#include "./import/blake2-impl.h" +#include "./import/blake2b.h" #include "sighash.h" -#include "cx.h" #include "./transaction/types.h" +#include "types.h" /* Start hacks */ -void os_longjmp(unsigned int exception) { -// longjmp(try_context_get()->jmp_buf, exception); -} +void os_longjmp(unsigned int exception) {} struct cx_xblake_s { cx_blake2b_t blake2b; @@ -28,6 +28,26 @@ union cx_u { cx_xblake_t blake; }; union cx_u G_cx; + +global_ctx_t G_context; + +int crypto_derive_private_key(void *private_key, + uint8_t chain_code[static 32], + const uint32_t *bip32_path, + uint8_t bip32_path_len) { + return 0; +} + +void crypto_init_public_key(void *private_key, + void *public_key, + uint8_t raw_public_key[static 64]) { + uint8_t input_public_key[32] = {0xe9, 0xed, 0xf6, 0x7a, 0x32, 0x58, 0x68, 0xec, + 0xc7, 0xcd, 0x85, 0x19, 0xe6, 0xca, 0x52, 0x65, + 0xe6, 0x5b, 0x7d, 0x10, 0xf5, 0x60, 0x66, 0x46, + 0x1c, 0xea, 0xbf, 0x0c, 0x2b, 0xc1, 0xc5, 0xad}; + memcpy(raw_public_key, input_public_key, sizeof(input_public_key)); +} + /* End hacks */ static void test_sighash(void **state) { @@ -35,13 +55,6 @@ static void test_sighash(void **state) { 0x99, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; - - uint8_t input_script_public_key[34] = {0x20, - 0xe9, 0xed, 0xf6, 0x7a, 0x32, 0x58, 0x68, 0xec, - 0xc7, 0xcd, 0x85, 0x19, 0xe6, 0xca, 0x52, 0x65, - 0xe6, 0x5b, 0x7d, 0x10, 0xf5, 0x60, 0x66, 0x46, - 0x1c, 0xea, 0xbf, 0x0c, 0x2b, 0xc1, 0xc5, 0xad, - 0xac}; uint8_t output_script_public_key[34] = {0x20, 0xc6, 0x2c, 0xf3, 0x0e, 0x4e, 0x57, 0xc5, 0x92, @@ -55,12 +68,11 @@ static void test_sighash(void **state) { transaction_output_t txout; transaction_t tx; - txin.tx_id = input_prev_tx_id; + memcpy(txin.tx_id, input_prev_tx_id, sizeof(input_prev_tx_id)); txin.index = 1; txin.value = 2; - txin.script_public_key = input_script_public_key; - txout.script_public_key = output_script_public_key; + memcpy(txout.script_public_key, output_script_public_key, sizeof(output_script_public_key)); txout.value = txin.value; // Assume no fee tx.version = 1; diff --git a/unit-tests/test_tx_parser.c b/unit-tests/test_tx_parser.c index 36e53a1..5316329 100644 --- a/unit-tests/test_tx_parser.c +++ b/unit-tests/test_tx_parser.c @@ -15,40 +15,107 @@ static void test_tx_serialization(void **state) { (void) state; transaction_t tx; + tx.tx_output_len = 2; + tx.tx_input_len = 3; + // clang-format off uint8_t raw_tx[] = { // header - 0x00, 0x01, 0x01, 0x01, + 0x00, 0x01, 0x02, 0x03 + }; + + buffer_t buf = {.ptr = raw_tx, .size = sizeof(raw_tx), .offset = 0}; + + parser_status_e status = transaction_deserialize(&buf, &tx); + + assert_int_equal(status, PARSING_OK); + assert_int_equal(tx.version, 1); + + // uint8_t output[350]; + // int length = transaction_serialize(&tx, output, sizeof(output)); + // assert_int_equal(length, sizeof(raw_tx)); + // assert_memory_equal(raw_tx, output, sizeof(raw_tx)); +} + +static void test_tx_input_serialization(void **state) { + (void) state; + + transaction_input_t txin; + + // clang-format off + uint8_t raw_tx[] = { // Input 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x82, 0xb8, 0x20, 0xe9, 0xed, 0xf6, 0x7a, 0x32, 0x58, 0x68, 0xec, 0xc7, 0xcd, 0x85, 0x19, 0xe6, 0xca, 0x52, 0x65, 0xe6, 0x5b, 0x7d, 0x10, 0xf5, 0x60, 0x66, 0x46, 0x1c, 0xea, 0xbf, 0x0c, 0x2b, 0xc1, 0xc5, - 0xad, 0xac, + 0xad, 0xac, 0x00, 0x00, 0x00, 0x00 + }; + + buffer_t buf = {.ptr = raw_tx, .size = sizeof(raw_tx), .offset = 0}; + + parser_status_e status = transaction_input_deserialize(&buf, &txin); + + assert_int_equal(status, PARSING_OK); + +} + +static void test_tx_output_serialization_32_bytes(void **state) { + (void) state; + + transaction_output_t txout; + + // clang-format off + uint8_t raw_tx[] = { // Output 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x86, 0xa0, + 0x20, 0xe1, 0x19, 0xd5, 0x35, 0x14, 0xc1, 0xb0, 0xe2, 0xef, 0xce, 0x7a, 0x89, 0xe3, 0xd1, 0xd5, 0xd6, 0xcd, 0x73, 0x58, 0x2e, 0xa2, 0x06, 0x87, 0x64, 0x1c, 0x8f, 0xdc, 0xcb, 0x60, 0x60, 0xa9, 0xad, - 0x00, 0x00, 0x00, 0x00, 0x00 + 0xac }; buffer_t buf = {.ptr = raw_tx, .size = sizeof(raw_tx), .offset = 0}; - parser_status_e status = transaction_deserialize(&buf, &tx); + parser_status_e status = transaction_output_deserialize(&buf, &txout); + + assert_int_equal(status, PARSING_OK); + +} + +static void test_tx_output_serialization_33_bytes(void **state) { + (void) state; + + transaction_output_t txout; + + // clang-format off + uint8_t raw_tx[] = { + // Output + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x86, 0xa0, + 0x21, + 0xe1, 0x19, 0xd5, 0x35, 0x14, 0xc1, 0xb0, 0xe2, + 0xef, 0xce, 0x7a, 0x89, 0xe3, 0xd1, 0xd5, 0xd6, + 0xcd, 0x73, 0x58, 0x2e, 0xa2, 0x06, 0x87, 0x64, + 0x1c, 0x8f, 0xdc, 0xcb, 0x60, 0x60, 0xa9, 0xad, + 0x00, 0xac + }; + + buffer_t buf = {.ptr = raw_tx, .size = sizeof(raw_tx), .offset = 0}; + + parser_status_e status = transaction_output_deserialize(&buf, &txout); assert_int_equal(status, PARSING_OK); - // uint8_t output[350]; - // int length = transaction_serialize(&tx, output, sizeof(output)); - // assert_int_equal(length, sizeof(raw_tx)); - // assert_memory_equal(raw_tx, output, sizeof(raw_tx)); } int main() { - const struct CMUnitTest tests[] = {cmocka_unit_test(test_tx_serialization)}; + const struct CMUnitTest tests[] = {cmocka_unit_test(test_tx_serialization), + cmocka_unit_test(test_tx_input_serialization), + cmocka_unit_test(test_tx_output_serialization_32_bytes), + cmocka_unit_test(test_tx_output_serialization_33_bytes)}; return cmocka_run_group_tests(tests, NULL, NULL); }