From f6ceb9cb547049ea968202865cf104a4b6d60c14 Mon Sep 17 00:00:00 2001 From: Robert Lukotka Date: Sat, 30 Sep 2023 14:29:16 +0200 Subject: [PATCH] Remove exceprions from public and private key code --- src/assert.h | 6 + src/diffieHellman.c | 13 ++- src/eos_utils.c | 64 ++++++----- src/eos_utils.h | 18 +-- src/getPublicKey.c | 5 +- src/hash.h | 162 +++++++++++++++------------ src/keyDerivation.c | 196 +++++++++++++++++++++++++-------- src/keyDerivation.h | 12 +- src/keyDerivation_test.c | 10 +- src/signTransaction.c | 109 +++++------------- src/signTransactionIntegrity.c | 16 +-- src/uiScreens.c | 1 + 12 files changed, 362 insertions(+), 250 deletions(-) diff --git a/src/assert.h b/src/assert.h index df23e326..28d62997 100644 --- a/src/assert.h +++ b/src/assert.h @@ -24,4 +24,10 @@ extern void assert(int cond, const char* msgStr); #define ASSERT(cond) assert((cond), _SHORTEN_(_FILE_LINE_, _MAX_ASSERT_LENGTH_)) +/** In crypto code, instead of asserts we return an error. **/ +#define CX_ASSERTION_ERROR 0x8FFFFFFF + +/** In crypto code, instead of throwing, we return an error **/ +#define CX_REJECTED_BY_POLICY 0x8FFFFFFE + #endif // H_FIO_APP_ASSERT diff --git a/src/diffieHellman.c b/src/diffieHellman.c index 667fd134..9b4ebbee 100644 --- a/src/diffieHellman.c +++ b/src/diffieHellman.c @@ -92,7 +92,7 @@ __noinline_due_to_stack__ void dh_init_aes_key(dh_aes_key_t* dhKey, BEGIN_TRY { TRY { TRACE_STACK_USAGE(); - derivePrivateKey(pathSpec, &privateKey); + CX_THROW(derivePrivateKey(pathSpec, &privateKey)); // this is how it is done... cx_err_t err = cx_ecdh_no_throw(&privateKey, @@ -101,14 +101,17 @@ __noinline_due_to_stack__ void dh_init_aes_key(dh_aes_key_t* dhKey, publicKey->W_len, basicSecret, SIZEOF(basicSecret)); - ASSERT(err == CX_OK); - sha_512_hash(basicSecret, SIZEOF(basicSecret), secret, SIZEOF(secret)); - sha_512_hash(secret, SIZEOF(secret), K, SIZEOF(K)); + CX_THROW(err); + err = sha_512_hash(basicSecret, SIZEOF(basicSecret), secret, SIZEOF(secret)); + CX_THROW(err); + err = sha_512_hash(secret, SIZEOF(secret), K, SIZEOF(K)); + CX_THROW(err); // First DH_AES_SECRET_SIZE bytes are used to compute shared secret, then DH_KM_SIZE are // used as Km for HMAC calculation STATIC_ASSERT(SIZEOF(K) == DH_AES_SECRET_SIZE + DH_KM_SIZE, "Incompatible types"); - cx_aes_init_key_no_throw(K, DH_AES_SECRET_SIZE, &dhKey->aesKey); + err = cx_aes_init_key_no_throw(K, DH_AES_SECRET_SIZE, &dhKey->aesKey); + CX_THROW(err); STATIC_ASSERT(SIZEOF(dhKey->km) == DH_KM_SIZE, "Incompatible types"); memmove(dhKey->km, K + DH_AES_SECRET_SIZE, DH_KM_SIZE); dhKey->initialized_magic = DH_AES_KEY_INITIALIZED_MAGIC; diff --git a/src/eos_utils.c b/src/eos_utils.c index c1af1efd..62c9e769 100644 --- a/src/eos_utils.c +++ b/src/eos_utils.c @@ -20,6 +20,12 @@ #include "cx.h" #include "utils.h" +#define FORWARD_CX_ERROR(call) \ + { \ + cx_err_t callResult = (call); \ + if (callResult != CX_OK) return callResult; \ + } + /** * EOS way to check if a signature is canonical :/ */ @@ -80,14 +86,14 @@ int ecdsa_der_to_sig(const uint8_t *der, uint8_t *sig) { * - V, out * - K, out */ -void rng_rfc6979(unsigned char *rnd, - unsigned char *h1, - unsigned char *x, - unsigned int x_len, - const unsigned char *q, - unsigned int q_len, - unsigned char *V, - unsigned char *K) { +cx_err_t rng_rfc6979(unsigned char *rnd, + unsigned char *h1, + unsigned char *x, + unsigned int x_len, + const unsigned char *q, + unsigned int q_len, + unsigned char *V, + unsigned char *K) { unsigned int h_len, found, i; cx_hmac_sha256_t hmac; @@ -104,32 +110,32 @@ void rng_rfc6979(unsigned char *rnd, memset(K, 0x00, h_len); // d. Set: K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1)) V[h_len] = 0; - CX_THROW(cx_hmac_sha256_init_no_throw(&hmac, K, 32)); - CX_THROW(cx_hmac_no_throw((cx_hmac_t *) &hmac, 0, V, h_len + 1, K, 32)); - CX_THROW(cx_hmac_no_throw((cx_hmac_t *) &hmac, 0, x, x_len, K, 32)); - CX_THROW(cx_hmac_no_throw((cx_hmac_t *) &hmac, CX_LAST, h1, h_len, K, 32)); + FORWARD_CX_ERROR(cx_hmac_sha256_init_no_throw(&hmac, K, 32)); + FORWARD_CX_ERROR(cx_hmac_no_throw((cx_hmac_t *) &hmac, 0, V, h_len + 1, K, 32)); + FORWARD_CX_ERROR(cx_hmac_no_throw((cx_hmac_t *) &hmac, 0, x, x_len, K, 32)); + FORWARD_CX_ERROR(cx_hmac_no_throw((cx_hmac_t *) &hmac, CX_LAST, h1, h_len, K, 32)); // e. Set: V = HMAC_K(V) - CX_THROW(cx_hmac_sha256_init_no_throw(&hmac, K, 32)); - CX_THROW(cx_hmac_no_throw((cx_hmac_t *) &hmac, CX_LAST, V, h_len, V, 32)); + FORWARD_CX_ERROR(cx_hmac_sha256_init_no_throw(&hmac, K, 32)); + FORWARD_CX_ERROR(cx_hmac_no_throw((cx_hmac_t *) &hmac, CX_LAST, V, h_len, V, 32)); // f. Set: K = HMAC_K(V || 0x01 || int2octets(x) || bits2octets(h1)) V[h_len] = 1; - CX_THROW(cx_hmac_sha256_init_no_throw(&hmac, K, 32)); - CX_THROW(cx_hmac_no_throw((cx_hmac_t *) &hmac, 0, V, h_len + 1, K, 32)); - CX_THROW(cx_hmac_no_throw((cx_hmac_t *) &hmac, 0, x, x_len, K, 32)); - CX_THROW(cx_hmac_no_throw((cx_hmac_t *) &hmac, CX_LAST, h1, h_len, K, 32)); + FORWARD_CX_ERROR(cx_hmac_sha256_init_no_throw(&hmac, K, 32)); + FORWARD_CX_ERROR(cx_hmac_no_throw((cx_hmac_t *) &hmac, 0, V, h_len + 1, K, 32)); + FORWARD_CX_ERROR(cx_hmac_no_throw((cx_hmac_t *) &hmac, 0, x, x_len, K, 32)); + FORWARD_CX_ERROR(cx_hmac_no_throw((cx_hmac_t *) &hmac, CX_LAST, h1, h_len, K, 32)); // g. Set: V = HMAC_K(V) -- - CX_THROW(cx_hmac_sha256_init_no_throw(&hmac, K, 32)); - CX_THROW(cx_hmac_no_throw((cx_hmac_t *) &hmac, CX_LAST, V, h_len, V, 32)); + FORWARD_CX_ERROR(cx_hmac_sha256_init_no_throw(&hmac, K, 32)); + FORWARD_CX_ERROR(cx_hmac_no_throw((cx_hmac_t *) &hmac, CX_LAST, V, h_len, V, 32)); // initial setup only once x = NULL; } else { // h.3 K = HMAC_K(V || 0x00) V[h_len] = 0; - CX_THROW(cx_hmac_sha256_init_no_throw(&hmac, K, 32)); - CX_THROW(cx_hmac_no_throw((cx_hmac_t *) &hmac, CX_LAST, V, h_len + 1, K, 32)); + FORWARD_CX_ERROR(cx_hmac_sha256_init_no_throw(&hmac, K, 32)); + FORWARD_CX_ERROR(cx_hmac_no_throw((cx_hmac_t *) &hmac, CX_LAST, V, h_len + 1, K, 32)); // h.3 V = HMAC_K(V) - CX_THROW(cx_hmac_sha256_init_no_throw(&hmac, K, 32)); - CX_THROW(cx_hmac_no_throw((cx_hmac_t *) &hmac, CX_LAST, V, h_len, V, 32)); + FORWARD_CX_ERROR(cx_hmac_sha256_init_no_throw(&hmac, K, 32)); + FORWARD_CX_ERROR(cx_hmac_no_throw((cx_hmac_t *) &hmac, CX_LAST, V, h_len, V, 32)); } // generate candidate @@ -145,8 +151,8 @@ void rng_rfc6979(unsigned char *rnd, if (x_len < h_len) { h_len = x_len; } - CX_THROW(cx_hmac_sha256_init_no_throw(&hmac, K, 32)); - CX_THROW(cx_hmac_no_throw((cx_hmac_t *) &hmac, CX_LAST, V, h_len, V, 32)); + FORWARD_CX_ERROR(cx_hmac_sha256_init_no_throw(&hmac, K, 32)); + FORWARD_CX_ERROR(cx_hmac_no_throw((cx_hmac_t *) &hmac, CX_LAST, V, h_len, V, 32)); memcpy(rnd, V, h_len); x_len -= h_len; } @@ -159,6 +165,8 @@ void rng_rfc6979(unsigned char *rnd, } } } + + return CX_OK; } unsigned char const BASE58ALPHABET[58] = { @@ -229,7 +237,9 @@ uint32_t compressed_public_key_to_wif(const uint8_t *publicKey, uint8_t check[20]; cx_ripemd160_t riprip; cx_ripemd160_init(&riprip); - CX_THROW(cx_hash_no_throw(&riprip.header, CX_LAST, temp, 33, check, sizeof(check))); + if (cx_hash_no_throw(&riprip.header, CX_LAST, temp, 33, check, sizeof(check)) != CX_OK) { + return 0; + } memcpy(temp + 33, check, 4); explicit_bzero(out, outLength); diff --git a/src/eos_utils.h b/src/eos_utils.h index f2de8ac1..e303af84 100644 --- a/src/eos_utils.h +++ b/src/eos_utils.h @@ -20,20 +20,22 @@ #include #include +#include "cx.h" unsigned char check_canonical(uint8_t *rs); int ecdsa_der_to_sig(const uint8_t *der, uint8_t *sig); -void rng_rfc6979(unsigned char *rnd, - unsigned char *h1, - unsigned char *x, - unsigned int x_len, - const unsigned char *q, - unsigned int q_len, - unsigned char *V, - unsigned char *K); +cx_err_t rng_rfc6979(unsigned char *rnd, + unsigned char *h1, + unsigned char *x, + unsigned int x_len, + const unsigned char *q, + unsigned int q_len, + unsigned char *V, + unsigned char *K); +// returns 0 on error uint32_t public_key_to_wif(const uint8_t *publicKey, uint32_t keyLength, char *out, diff --git a/src/getPublicKey.c b/src/getPublicKey.c index aa5357c1..8fe2392f 100644 --- a/src/getPublicKey.c +++ b/src/getPublicKey.c @@ -51,6 +51,8 @@ static void getPublicKey_ui_runStep() { SIZEOF(ctx->pubKey.W), (char*) G_io_apdu_buffer + SIZEOF(ctx->pubKey.W), MAX_WIF_PUBKEY_LENGTH); + ASSERT(wifkeylen != 0); + ASSERT(wifkeylen <= BUFFER_SIZE_PARANOIA); // we do not copy trailing 0 io_send_buf(SUCCESS, G_io_apdu_buffer, SIZEOF(ctx->pubKey.W) + wifkeylen - 1); @@ -77,7 +79,8 @@ static void runGetPublicKeyUIFlow() { { // Calculation - derivePublicKey(&ctx->pathSpec, &ctx->pubKey); + cx_err_t err = derivePublicKey(&ctx->pathSpec, &ctx->pubKey); + ASSERT(err == CX_OK); ctx->responseReadyMagic = RESPONSE_READY_MAGIC; } diff --git a/src/hash.h b/src/hash.h index 43fe1767..cfbe08eb 100644 --- a/src/hash.h +++ b/src/hash.h @@ -22,50 +22,61 @@ typedef struct { cx_sha256_t cx_ctx; } sha_256_context_t; -static __attribute__((always_inline, unused)) void sha_256_init(sha_256_context_t* ctx) { - cx_sha256_init(&ctx->cx_ctx); - ctx->initialized_magic = HASH_CONTEXT_INITIALIZED_MAGIC; +static __attribute__((always_inline, unused)) cx_err_t sha_256_init(sha_256_context_t* ctx) { + cx_err_t err = cx_sha256_init_no_throw(&ctx->cx_ctx); + if (err == CX_OK) { + ctx->initialized_magic = HASH_CONTEXT_INITIALIZED_MAGIC; + } + return err; } -static __attribute__((always_inline, unused)) void sha_256_append(sha_256_context_t* ctx, - const uint8_t* inBuffer, - size_t inSize) { - ASSERT(ctx->initialized_magic == HASH_CONTEXT_INITIALIZED_MAGIC); +static __attribute__((always_inline, unused)) cx_err_t sha_256_append(sha_256_context_t* ctx, + const uint8_t* inBuffer, + size_t inSize) { + if (ctx->initialized_magic != HASH_CONTEXT_INITIALIZED_MAGIC) { + return CX_INVALID_PARAMETER; + } TRACE_BUFFER(inBuffer, inSize); - CX_THROW(cx_hash_no_throw(&ctx->cx_ctx.header, - 0, /* Do not output the hash, yet */ - inBuffer, - inSize, - NULL, - 0)); + return cx_hash_no_throw(&ctx->cx_ctx.header, + 0, /* Do not output the hash, yet */ + inBuffer, + inSize, + NULL, + 0); } -static __attribute__((always_inline, unused)) void sha_256_finalize(sha_256_context_t* ctx, - uint8_t* outBuffer, - size_t outSize) { - ASSERT(ctx->initialized_magic == HASH_CONTEXT_INITIALIZED_MAGIC); - ASSERT(outSize == SHA_256_SIZE); - CX_THROW(cx_hash_no_throw(&ctx->cx_ctx.header, - CX_LAST, /* Output the hash */ - NULL, - 0, - outBuffer, - SHA_256_SIZE)); +static __attribute__((always_inline, unused)) cx_err_t sha_256_finalize(sha_256_context_t* ctx, + uint8_t* outBuffer, + size_t outSize) { + if (ctx->initialized_magic != HASH_CONTEXT_INITIALIZED_MAGIC || outSize != SHA_256_SIZE) { + return CX_INVALID_PARAMETER; + } + return cx_hash_no_throw(&ctx->cx_ctx.header, + CX_LAST, /* Output the hash */ + NULL, + 0, + outBuffer, + SHA_256_SIZE); } /* Convenience function to make all in one step */ -static __attribute__((always_inline, unused)) void sha_256_hash(const uint8_t* inBuffer, - size_t inSize, - uint8_t* outBuffer, - size_t outSize) { - ASSERT(inSize < BUFFER_SIZE_PARANOIA); - ASSERT(outSize == SHA_256_SIZE); +static __attribute__((always_inline, unused)) cx_err_t sha_256_hash(const uint8_t* inBuffer, + size_t inSize, + uint8_t* outBuffer, + size_t outSize) { + if (inSize >= BUFFER_SIZE_PARANOIA) { + return CX_INVALID_PARAMETER; + } sha_256_context_t ctx; - sha_256_init(&ctx); - /* Note: This could be done by single cx_hash call */ - /* But we don't really care */ - sha_256_append(&ctx, inBuffer, inSize); - sha_256_finalize(&ctx, outBuffer, outSize); + cx_err_t err = sha_256_init(&ctx); + if (err != CX_OK) { + return err; + } + err = sha_256_append(&ctx, inBuffer, inSize); + if (err != CX_OK) { + return err; + } + return sha_256_finalize(&ctx, outBuffer, outSize); } typedef struct { @@ -73,50 +84,61 @@ typedef struct { cx_sha512_t cx_ctx; } sha_512_context_t; -static __attribute__((always_inline, unused)) void sha_512_init(sha_512_context_t* ctx) { - cx_sha512_init(&ctx->cx_ctx); - ctx->initialized_magic = HASH_CONTEXT_INITIALIZED_MAGIC; +static __attribute__((always_inline, unused)) cx_err_t sha_512_init(sha_512_context_t* ctx) { + cx_err_t err = cx_sha512_init_no_throw(&ctx->cx_ctx); + if (err == CX_OK) { + ctx->initialized_magic = HASH_CONTEXT_INITIALIZED_MAGIC; + } + return err; } -static __attribute__((always_inline, unused)) void sha_512_append(sha_512_context_t* ctx, - const uint8_t* inBuffer, - size_t inSize) { - ASSERT(ctx->initialized_magic == HASH_CONTEXT_INITIALIZED_MAGIC); +static __attribute__((always_inline, unused)) cx_err_t sha_512_append(sha_512_context_t* ctx, + const uint8_t* inBuffer, + size_t inSize) { + if (ctx->initialized_magic != HASH_CONTEXT_INITIALIZED_MAGIC) { + return CX_INVALID_PARAMETER; + } TRACE_BUFFER(inBuffer, inSize); - CX_THROW(cx_hash_no_throw(&ctx->cx_ctx.header, - 0, /* Do not output the hash, yet */ - inBuffer, - inSize, - NULL, - 0)); + return cx_hash_no_throw(&ctx->cx_ctx.header, + 0, /* Do not output the hash, yet */ + inBuffer, + inSize, + NULL, + 0); } -static __attribute__((always_inline, unused)) void sha_512_finalize(sha_512_context_t* ctx, - uint8_t* outBuffer, - size_t outSize) { - ASSERT(ctx->initialized_magic == HASH_CONTEXT_INITIALIZED_MAGIC); - ASSERT(outSize == SHA_512_SIZE); - CX_THROW(cx_hash_no_throw(&ctx->cx_ctx.header, - CX_LAST, /* Output the hash */ - NULL, - 0, - outBuffer, - SHA_512_SIZE)); +static __attribute__((always_inline, unused)) cx_err_t sha_512_finalize(sha_512_context_t* ctx, + uint8_t* outBuffer, + size_t outSize) { + if (ctx->initialized_magic != HASH_CONTEXT_INITIALIZED_MAGIC || outSize != SHA_512_SIZE) { + return CX_INVALID_PARAMETER; + } + return cx_hash_no_throw(&ctx->cx_ctx.header, + CX_LAST, /* Output the hash */ + NULL, + 0, + outBuffer, + SHA_512_SIZE); } /* Convenience function to make all in one step */ -static __attribute__((always_inline, unused)) void sha_512_hash(const uint8_t* inBuffer, - size_t inSize, - uint8_t* outBuffer, - size_t outSize) { - ASSERT(inSize < BUFFER_SIZE_PARANOIA); - ASSERT(outSize == SHA_512_SIZE); +static __attribute__((always_inline, unused)) cx_err_t sha_512_hash(const uint8_t* inBuffer, + size_t inSize, + uint8_t* outBuffer, + size_t outSize) { + if (inSize >= BUFFER_SIZE_PARANOIA) { + return CX_INVALID_PARAMETER; + } sha_512_context_t ctx; - sha_512_init(&ctx); - /* Note: This could be done by single cx_hash call */ - /* But we don't really care */ - sha_512_append(&ctx, inBuffer, inSize); - sha_512_finalize(&ctx, outBuffer, outSize); + cx_err_t err = sha_512_init(&ctx); + if (err != CX_OK) { + return err; + } + err = sha_512_append(&ctx, inBuffer, inSize); + if (err != CX_OK) { + return err; + } + return sha_512_finalize(&ctx, outBuffer, outSize); } #endif // H_FIO_APP_HASH diff --git a/src/keyDerivation.c b/src/keyDerivation.c index e768ecc4..995f0cd6 100644 --- a/src/keyDerivation.c +++ b/src/keyDerivation.c @@ -8,65 +8,173 @@ #include "utils.h" #include "fio.h" #include "securityPolicy.h" +#include "eos_utils.h" #define PRIVATE_KEY_SEED_LEN 64 -__noinline_due_to_stack__ void derivePrivateKey(const bip44_path_t* pathSpec, - private_key_t* privateKey) { - ENSURE_NOT_DENIED(policyDerivePrivateKey(pathSpec)); +// Taken from EOS app. Needed to produce signatures. +static uint8_t const SECP256K1_N[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41}; + +__noinline_due_to_stack__ cx_err_t derivePrivateKey(const bip44_path_t* pathSpec, + private_key_t* privateKey) { + if (policyDerivePrivateKey(pathSpec) == POLICY_DENY) { + return CX_REJECTED_BY_POLICY; + } // Sanity check - ASSERT(pathSpec->length < ARRAY_LEN(pathSpec->path)); + if (pathSpec->length >= ARRAY_LEN(pathSpec->path)) { + return CX_ASSERTION_ERROR; + } TRACE(); uint8_t privateKeySeed[PRIVATE_KEY_SEED_LEN]; + explicit_bzero(privateKeySeed, SIZEOF(privateKeySeed)); - BEGIN_TRY { - TRY { - STATIC_ASSERT(CX_APILEVEL >= 5, "unsupported api level"); - - io_seproxyhal_io_heartbeat(); - CX_THROW(os_derive_bip32_with_seed_no_throw(HDW_NORMAL, - CX_CURVE_SECP256K1, - pathSpec->path, - pathSpec->length, - privateKeySeed, - NULL, - NULL, - 0)); - io_seproxyhal_io_heartbeat(); - - CX_THROW(cx_ecfp_init_private_key_no_throw(CX_CURVE_SECP256K1, - privateKeySeed, - 32, - privateKey)); - } - FINALLY { - explicit_bzero(privateKeySeed, SIZEOF(privateKeySeed)); - } + STATIC_ASSERT(CX_APILEVEL >= 5, "unsupported api level"); + + cx_err_t err = os_derive_bip32_with_seed_no_throw(HDW_NORMAL, + CX_CURVE_SECP256K1, + pathSpec->path, + pathSpec->length, + privateKeySeed, + NULL, + NULL, + 0); + if (err != CX_OK) { + explicit_bzero(privateKeySeed, SIZEOF(privateKeySeed)); + return err; + } + + err = cx_ecfp_init_private_key_no_throw(CX_CURVE_SECP256K1, privateKeySeed, 32, privateKey); + if (err != CX_OK) { + explicit_bzero(privateKeySeed, SIZEOF(privateKeySeed)); + explicit_bzero(privateKey, SIZEOF(*privateKey)); + return err; } - END_TRY; + + return CX_OK; } -__noinline_due_to_stack__ void derivePublicKey(const bip44_path_t* pathSpec, - public_key_t* publicKey) { +__noinline_due_to_stack__ cx_err_t derivePublicKey(const bip44_path_t* pathSpec, + public_key_t* publicKey) { private_key_t privateKey; - BEGIN_TRY { - TRY { - derivePrivateKey(pathSpec, &privateKey); - // We should do cx_ecfp_generate_pair here, but it does not work in SDK < 1.5.4, - // should work with the new SDK - io_seproxyhal_io_heartbeat(); - CX_THROW(cx_ecfp_init_public_key_no_throw(CX_CURVE_SECP256K1, NULL, 0, publicKey)); - CX_THROW(cx_ecfp_generate_pair_no_throw(CX_CURVE_SECP256K1, - publicKey, - &privateKey, - 1)); // 1 - private key preserved - io_seproxyhal_io_heartbeat(); + explicit_bzero(&privateKey, SIZEOF(privateKey)); + + cx_err_t err = derivePrivateKey(pathSpec, &privateKey); + if (err != CX_OK) { + explicit_bzero(&privateKey, SIZEOF(privateKey)); + return err; + } + + err = cx_ecfp_init_public_key_no_throw(CX_CURVE_SECP256K1, NULL, 0, publicKey); + if (err != CX_OK) { + explicit_bzero(&privateKey, SIZEOF(privateKey)); + return err; + } + + err = cx_ecfp_generate_pair_no_throw(CX_CURVE_SECP256K1, + publicKey, + &privateKey, + 1); // 1 - private key preserved + if (err != CX_OK) { + explicit_bzero(&privateKey, SIZEOF(privateKey)); + return err; + } + + return CX_OK; +} + +// This function contains code producing signatures that is taken from EOS app +// The produced signature contains both +__noinline_due_to_stack__ cx_err_t signTransaction(bip44_path_t* wittnessPath, + uint8_t hashBuf[SHA_256_SIZE], + uint8_t* signature, + size_t signatureLen) { + if (signatureLen < 200) { + return CX_ASSERTION_ERROR; + } + + // Derive keys and sign the transaction, setup + private_key_t privateKey; + explicit_bzero(&privateKey, SIZEOF(privateKey)); + + // We derive the private key + { + cx_err_t err = derivePrivateKey(wittnessPath, &privateKey); + if (err != CX_OK) { + explicit_bzero(&privateKey, SIZEOF(privateKey)); + return err; + } + TRACE("privateKey.d:"); + TRACE_BUFFER(privateKey.d, privateKey.d_len); + } + + // We sign the hash + explicit_bzero(signature, signatureLen); + uint8_t V[33]; + uint8_t K[32]; + int tries = 0; + + // Loop until a candidate matching the canonical signature is found + for (;;) { + if (tries == 0) { + cx_err_t err = rng_rfc6979(signature + 100, + hashBuf, + privateKey.d, + privateKey.d_len, + SECP256K1_N, + 32, + V, + K); + if (err != CX_OK) { + explicit_bzero(&privateKey, SIZEOF(privateKey)); + explicit_bzero(signature, signatureLen); + return err; + } + } else { + cx_err_t err = rng_rfc6979(signature + 100, hashBuf, NULL, 0, SECP256K1_N, 32, V, K); + if (err != CX_OK) { + explicit_bzero(&privateKey, SIZEOF(privateKey)); + explicit_bzero(signature, signatureLen); + return err; + } } - FINALLY { + uint32_t infos; + + size_t sig_len_ = 100; + cx_err_t err = cx_ecdsa_sign_no_throw(&privateKey, + CX_NO_CANONICAL | CX_RND_PROVIDED | CX_LAST, + CX_SHA256, + hashBuf, + 32, + signature + 100, + &sig_len_, + &infos); + if (err != CX_OK) { explicit_bzero(&privateKey, SIZEOF(privateKey)); + explicit_bzero(signature, signatureLen); + return err; + } + TRACE_BUFFER(signature + 100, 100); + + if ((infos & CX_ECCINFO_PARITY_ODD) != 0) { + signature[100] |= 0x01; + } + signature[0] = 27 + 4 + (signature[100] & 0x01); + ecdsa_der_to_sig(signature + 100, signature + 1); + TRACE_BUFFER(signature, PUBKEY_LENGTH); + + if (check_canonical(signature + 1)) { + TRACE("Try %d succesfull!", tries); + break; + } else { + TRACE("Try %d unsuccesfull!", tries); + tries++; } } - END_TRY; + + explicit_bzero(&privateKey, sizeof(privateKey)); + return CX_OK; } diff --git a/src/keyDerivation.h b/src/keyDerivation.h index 70106464..ad925f7c 100644 --- a/src/keyDerivation.h +++ b/src/keyDerivation.h @@ -5,6 +5,7 @@ #include "handlers.h" #include "bip44.h" #include "utils.h" +#include "hash.h" #define CHAIN_CODE_SIZE (32) @@ -15,11 +16,16 @@ typedef struct { uint8_t code[CHAIN_CODE_SIZE]; } chain_code_t; -__noinline_due_to_stack__ void derivePrivateKey(const bip44_path_t* pathSpec, - private_key_t* privateKey // output +__noinline_due_to_stack__ cx_err_t derivePrivateKey(const bip44_path_t* pathSpec, + private_key_t* privateKey // output ); -__noinline_due_to_stack__ void derivePublicKey(const bip44_path_t* pathSpec, public_key_t* out); +__noinline_due_to_stack__ cx_err_t derivePublicKey(const bip44_path_t* pathSpec, public_key_t* out); + +__noinline_due_to_stack__ cx_err_t signTransaction(bip44_path_t* wittnessPath, + uint8_t hashBuf[SHA_256_SIZE], + uint8_t* signature, + size_t signatureLen); #ifdef DEVEL __noinline_due_to_stack__ void run_key_derivation_test(); diff --git a/src/keyDerivation_test.c b/src/keyDerivation_test.c index 9ea3357d..1ba340aa 100644 --- a/src/keyDerivation_test.c +++ b/src/keyDerivation_test.c @@ -19,7 +19,13 @@ void testcase_derivePrivateKey(uint32_t* path, uint32_t pathLen, const char* exp PRINTF("\n"); private_key_t privateKey; - derivePrivateKey(&pathSpec, &privateKey); + cx_err_t err = derivePrivateKey(&pathSpec, &privateKey); + if (err == CX_REJECTED_BY_POLICY) { + THROW(ERR_REJECTED_BY_POLICY); + } + if (err != CX_OK) { + THROW(ERR_ASSERT); + } TRACE("%d", SIZEOF(privateKey.d)); TRACE_BUFFER(privateKey.d, SIZEOF(privateKey.d)); @@ -73,7 +79,7 @@ void testcase_derivePublicKey(uint32_t* path, uint32_t pathLen, const char* expe PRINTF("\n"); public_key_t publicKey; - derivePublicKey(&pathSpec, &publicKey); + CX_THROW(derivePublicKey(&pathSpec, &publicKey)); TRACE("%d", SIZEOF(publicKey.W)); TRACE_BUFFER(publicKey.W, SIZEOF(publicKey.W)); diff --git a/src/signTransaction.c b/src/signTransaction.c index 876616f2..a2d514b9 100644 --- a/src/signTransaction.c +++ b/src/signTransaction.c @@ -34,11 +34,6 @@ enum { TX_INIT_WAS_CALLED_INITIALIZED_MAGIC = 12346, }; -// Taken from EOS app. Needed to produce signatures. -static uint8_t const SECP256K1_N[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41}; - // Uses ctx->dataToAppendToTx, ctx->dataToAppendToTxLen to extend hash // If ctx->dhIsActive then, we extend hash with encrypted data and prepare resulting encrypted // blocks to G_io_apdu_buffer, ctx->responseLength Variables (&ctx->wittnessPath, &ctx->otherPubkey, @@ -58,7 +53,7 @@ static void processShaAndPosibleDHAndPrepareResponse() { ctx->dataToAppendToTxLen, G_io_apdu_buffer, SIZEOF(G_io_apdu_buffer)); - sha_256_append(&ctx->hashContext, G_io_apdu_buffer, ctx->responseLength); + CX_THROW(sha_256_append(&ctx->hashContext, G_io_apdu_buffer, ctx->responseLength)); VALIDATE( ctx->countedSectionDifference + ctx->responseLength >= ctx->dataToAppendToTxLen, ERR_INVALID_STATE); @@ -75,7 +70,9 @@ static void processShaAndPosibleDHAndPrepareResponse() { } END_TRY; } else { - sha_256_append(&ctx->hashContext, ctx->dataToAppendToTx, ctx->dataToAppendToTxLen); + cx_err_t err = + sha_256_append(&ctx->hashContext, ctx->dataToAppendToTx, ctx->dataToAppendToTxLen); + ASSERT(err == CX_OK); ctx->responseLength = 0; } } @@ -85,13 +82,15 @@ static void processShaAndPosibleDHAndPrepareResponse() { static void prepareOurPubkeyForDisplay() { public_key_t wittnessPathPubkey; explicit_bzero(&wittnessPathPubkey, SIZEOF(wittnessPathPubkey)); - derivePublicKey(&ctx->wittnessPath, &wittnessPathPubkey); + cx_err_t err = derivePublicKey(&ctx->wittnessPath, &wittnessPathPubkey); + ASSERT(err == CX_OK); TRACE_BUFFER(wittnessPathPubkey.W, SIZEOF(wittnessPathPubkey.W)); uint32_t outlen = public_key_to_wif(wittnessPathPubkey.W, SIZEOF(wittnessPathPubkey.W), ctx->value, SIZEOF(ctx->value)); + ASSERT(outlen != 0); ASSERT(outlen < SIZEOF(ctx->value)); ctx->value[outlen] = 0; } @@ -193,7 +192,8 @@ __noinline_due_to_stack__ void signTx_handleInitAPDU(uint8_t p2, VALIDATE(!ctx->dhIsActive, ERR_INVALID_STATE); VALIDATE(countedSectionProcess(&ctx->countedSections, ctx->dataToAppendToTxLen), ERR_INVALID_DATA); - sha_256_append(&ctx->hashContext, ctx->dataToAppendToTx, ctx->dataToAppendToTxLen); + ASSERT(sha_256_append(&ctx->hashContext, ctx->dataToAppendToTx, ctx->dataToAppendToTxLen) == + CX_OK); ctx->responseLength = 0; } @@ -690,6 +690,7 @@ __noinline_due_to_stack__ void signTx_handleStartDHEncodingAPDU( SIZEOF(ctx->otherPubkey.W), ctx->value, SIZEOF(ctx->value)); + ASSERT(outlen != 0); ASSERT(outlen < SIZEOF(ctx->value)); ctx->value[outlen] = 0; } @@ -722,7 +723,9 @@ __noinline_due_to_stack__ void signTx_handleStartDHEncodingAPDU( SIZEOF(IV), G_io_apdu_buffer, SIZEOF(G_io_apdu_buffer)); - ASSERT(ctx->responseLength == 20); // first 5 blocks + if (ctx->responseLength != 20) { // first 5 blocks + THROW(ERR_ASSERT); + } ctx->countedSectionDifference = ctx->responseLength; TRACE("CS diff %d", (int) ctx->responseLength); } @@ -732,7 +735,7 @@ __noinline_due_to_stack__ void signTx_handleStartDHEncodingAPDU( } END_TRY; - sha_256_append(&ctx->hashContext, G_io_apdu_buffer, ctx->responseLength); + ASSERT(sha_256_append(&ctx->hashContext, G_io_apdu_buffer, ctx->responseLength) == CX_OK); ctx->dhIsActive = true; } @@ -830,7 +833,8 @@ __noinline_due_to_stack__ void signTx_handleEndDHEncodingAPDU( VALIDATE(countedSectionProcess(&ctx->countedSections, ctx->countedSectionDifference), ERR_INVALID_STATE); - sha_256_append(&ctx->hashContext, G_io_apdu_buffer, ctx->responseLength); + ASSERT(sha_256_append(&ctx->hashContext, G_io_apdu_buffer, ctx->responseLength) == + CX_OK); ctx->dhIsActive = false; } @@ -917,7 +921,7 @@ __noinline_due_to_stack__ void signTx_handleFinishAPDU( uint8_t hashBuf[SHA_256_SIZE]; explicit_bzero(hashBuf, SIZEOF(hashBuf)); { - sha_256_finalize(&ctx->hashContext, hashBuf, SIZEOF(hashBuf)); + ASSERT(sha_256_finalize(&ctx->hashContext, hashBuf, SIZEOF(hashBuf)) == CX_OK); TRACE("SHA_256_finalize, resulting hash:"); TRACE_BUFFER(hashBuf, SHA_256_SIZE); } @@ -944,81 +948,19 @@ __noinline_due_to_stack__ void signTx_handleFinishAPDU( } } - // Derive keys and sign the transaction, setup - private_key_t privateKey; - explicit_bzero(&privateKey, SIZEOF(privateKey)); - BEGIN_TRY { - TRY { - // We derive the private key - { - derivePrivateKey(&ctx->wittnessPath, &privateKey); - TRACE("privateKey.d:"); - TRACE_BUFFER(privateKey.d, privateKey.d_len); - } - - // We sign the hash - // Code producing signatures is taken from EOS app - uint8_t V[33]; - uint8_t K[32]; - int tries = 0; - - // Loop until a candidate matching the canonical signature is found - // Taken from EOS app - // We use G_io_apdu_buffer to save memory (and also to minimize changes to EOS code) - // The code produces the signature right where we need it for the respons - explicit_bzero(G_io_apdu_buffer, SIZEOF(G_io_apdu_buffer)); - for (;;) { - if (tries == 0) { - rng_rfc6979(G_io_apdu_buffer + 100, - hashBuf, - privateKey.d, - privateKey.d_len, - SECP256K1_N, - 32, - V, - K); - } else { - rng_rfc6979(G_io_apdu_buffer + 100, hashBuf, NULL, 0, SECP256K1_N, 32, V, K); - } - uint32_t infos; - - size_t sig_len_ = 100; - CX_THROW(cx_ecdsa_sign_no_throw(&privateKey, - CX_NO_CANONICAL | CX_RND_PROVIDED | CX_LAST, - CX_SHA256, - hashBuf, - 32, - G_io_apdu_buffer + 100, - &sig_len_, - &infos)); - TRACE_BUFFER(G_io_apdu_buffer + 100, 100); - - if ((infos & CX_ECCINFO_PARITY_ODD) != 0) { - G_io_apdu_buffer[100] |= 0x01; - } - G_io_apdu_buffer[0] = 27 + 4 + (G_io_apdu_buffer[100] & 0x01); - ecdsa_der_to_sig(G_io_apdu_buffer + 100, G_io_apdu_buffer + 1); - TRACE_BUFFER(G_io_apdu_buffer, PUBKEY_LENGTH); - - if (check_canonical(G_io_apdu_buffer + 1)) { - TRACE("Try %d succesfull!", tries); - break; - } else { - TRACE("Try %d unsuccesfull!", tries); - tries++; - } - } - } - FINALLY { - explicit_bzero(&privateKey, sizeof(privateKey)); + cx_err_t err = + signTransaction(&ctx->wittnessPath, hashBuf, G_io_apdu_buffer, SIZEOF(G_io_apdu_buffer)); + if (err != CX_OK) { + if (err == CX_REJECTED_BY_POLICY) { + THROW(ERR_REJECTED_BY_POLICY); } + THROW(ERR_ASSERT); } - END_TRY; // We add hash to the response TRACE("ecdsa_der_to_sig_result:"); TRACE_BUFFER(G_io_apdu_buffer, PUBKEY_LENGTH); - memcpy(G_io_apdu_buffer + PUBKEY_LENGTH, hashBuf, SHA_256_SIZE); + memcpy(G_io_apdu_buffer + PUBKEY_LENGTH, hashBuf, SIZEOF(hashBuf)); signTx_handleFinish_ui_runStep(); } @@ -1066,7 +1008,8 @@ void signTransaction_handleAPDU(uint8_t p1, if (isNewCall) { explicit_bzero(ctx, SIZEOF(*ctx)); TRACE("SHA_256_init"); - sha_256_init(&ctx->hashContext); + cx_err_t err = sha_256_init(&ctx->hashContext); + ASSERT(err == CX_OK); TRACE("Integrity check init"); integrityCheckInit(&ctx->integrity); TRACE("Counted sections init"); diff --git a/src/signTransactionIntegrity.c b/src/signTransactionIntegrity.c index 7438ad95..92c95681 100644 --- a/src/signTransactionIntegrity.c +++ b/src/signTransactionIntegrity.c @@ -311,13 +311,15 @@ __noinline_due_to_stack__ void integrityCheckProcessInstruction(tx_integrity_t * uint8_t constDataLength) { ASSERT(integrity->initialized_magic == TX_INTEGRITY_HASH_INITIALIZED_MAGIC); sha_256_context_t ctx; - sha_256_init(&ctx); - sha_256_append(&ctx, integrity->integrityHash, SIZEOF(integrity->integrityHash)); - sha_256_append(&ctx, &p1, SIZEOF(p1)); - sha_256_append(&ctx, &p2, SIZEOF(p2)); - sha_256_append(&ctx, &constDataLength, SIZEOF(constDataLength)); - sha_256_append(&ctx, constData, constDataLength); - sha_256_finalize(&ctx, integrity->integrityHash, SIZEOF(integrity->integrityHash)); + ASSERT(sha_256_init(&ctx) == CX_OK); + ASSERT(sha_256_append(&ctx, integrity->integrityHash, SIZEOF(integrity->integrityHash)) == + CX_OK); + ASSERT(sha_256_append(&ctx, &p1, SIZEOF(p1)) == CX_OK); + ASSERT(sha_256_append(&ctx, &p2, SIZEOF(p2)) == CX_OK); + ASSERT(sha_256_append(&ctx, &constDataLength, SIZEOF(constDataLength)) == CX_OK); + ASSERT(sha_256_append(&ctx, constData, constDataLength) == CX_OK); + ASSERT(sha_256_finalize(&ctx, integrity->integrityHash, SIZEOF(integrity->integrityHash)) == + CX_OK); TRACE("p1: %02x. p2: %02x, constdata: %.*h", p1, p2, constDataLength, constData); TRACE_BUFFER(&integrity->integrityHash, SIZEOF(integrity->integrityHash)); diff --git a/src/uiScreens.c b/src/uiScreens.c index da61af6f..a3896f41 100644 --- a/src/uiScreens.c +++ b/src/uiScreens.c @@ -60,6 +60,7 @@ __noinline_due_to_stack__ void ui_displayPubkeyScreen(const char* screenHeader, char buffer[MAX_WIF_PUBKEY_LENGTH + 1]; uint32_t outlen = public_key_to_wif(pubkey->W, SIZEOF(pubkey->W), buffer, SIZEOF(buffer)); + ASSERT(outlen != 0); ASSERT(outlen < MAX_WIF_PUBKEY_LENGTH + 1); buffer[outlen] = 0;