From 42a75a2889beb6e2b03c3b6b82701d10deb94c57 Mon Sep 17 00:00:00 2001 From: Jong-Shian Wu Date: Sat, 8 Aug 2015 05:04:34 +0800 Subject: [PATCH] make uECC_sign_deterministic conform to RFC 6979 --- uECC.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- uECC.h | 8 ++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/uECC.c b/uECC.c index 8dcd7af..03b645e 100644 --- a/uECC.c +++ b/uECC.c @@ -2594,17 +2594,38 @@ int uECC_sign_deterministic(const uint8_t private_key[uECC_BYTES], uint8_t *V = K + hash_context->result_size; uECC_word_t tries; unsigned i; + uECC_word_t tmp[uECC_N_WORDS]; // an integer converted from message_hash + uint8_t reduced_msg_hash[uECC_N_BYTES] = { 0 }; + for (i = 0; i < hash_context->result_size; ++i) { V[i] = 0x01; K[i] = 0; } + // Convert the octet string of length `uECC_BYTES` into an integer tmp. + // Since this must be done when generating an ECDSA signature, we may + // choose to refactor the codebase so that this operation is done only + // once. + vli_bytesToNative(tmp, message_hash); + + // Modular reduction: tmp <- tmp mod n (ONLY FOR RFC6979) + while (vli_cmp_n(tmp, curve_n) >= 0) { + vli_sub(tmp, tmp, curve_n); + } + + // Convert the integer tmp back to an octet string (ONLY FOR RFC6979) +#if (uECC_CURVE == uECC_secp160r1) + vli_nativeToBytes(reduced_msg_hash + 1, tmp); +#else + vli_nativeToBytes(reduced_msg_hash, tmp); +#endif + // K = HMAC_K(V || 0x00 || int2octets(x) || h(m)) HMAC_init(hash_context, K); V[hash_context->result_size] = 0x00; HMAC_update(hash_context, V, hash_context->result_size + 1); HMAC_update(hash_context, private_key, uECC_BYTES); - HMAC_update(hash_context, message_hash, uECC_BYTES); + HMAC_update(hash_context, reduced_msg_hash, uECC_N_BYTES); HMAC_finish(hash_context, K, K); update_V(hash_context, K, V); @@ -2614,13 +2635,14 @@ int uECC_sign_deterministic(const uint8_t private_key[uECC_BYTES], V[hash_context->result_size] = 0x01; HMAC_update(hash_context, V, hash_context->result_size + 1); HMAC_update(hash_context, private_key, uECC_BYTES); - HMAC_update(hash_context, message_hash, uECC_BYTES); + HMAC_update(hash_context, reduced_msg_hash, uECC_N_BYTES); HMAC_finish(hash_context, K, K); update_V(hash_context, K, V); for (tries = 0; tries < MAX_TRIES; ++tries) { - uECC_word_t T[uECC_N_WORDS]; + uECC_word_t k[uECC_N_WORDS] = { 0 }; // the RFC6979 ephemeral key in each round + uint8_t T[uECC_N_BYTES]; uint8_t *T_ptr = (uint8_t *)T; unsigned T_bytes = 0; while (T_bytes < sizeof(T)) { @@ -2629,11 +2651,38 @@ int uECC_sign_deterministic(const uint8_t private_key[uECC_BYTES], T_ptr[T_bytes] = V[i]; } } + #if (uECC_CURVE == uECC_secp160r1) - T[uECC_WORDS] &= 0x01; + // + // NOTE: + // + // The function vli_bytesToNative converts a big-endian array of + // length uECC_BYTES=20 where each unit is 1-byte long into a native + // little-endian array of length uECC_WORDS=20/5/3 where each unit is + // 1/4/8-byte long. + // + // Maybe we should have a version of vli_bytesToNative that does the + // same thing for uECC_N_BYTES=21 and uECC_N_WORDS=21/6/3? + // + // TODO: + // Before vli_bytesToNative() we should right-shift all bits in T by 7 + // so that the leftmost 161 bits in T are used in order to comform to + // RFC6979. + // + vli_bytesToNative(k, T + 1); + if (T[0] & 1) { + // k <- k + 2^160 + #if (uECC_WORD_SIZE == 1 || uECC_WORD_SIZE == 4) + k[uECC_N_WORDS - 1] = 1; + #elif (uECC_WORD_SIZE == 8) + k[uECC_N_WORDS - 1] ^= (1 << 32); + #endif + } + #else + vli_bytesToNative(k, T); #endif - if (uECC_sign_with_k(private_key, message_hash, T, signature)) { + if (uECC_sign_with_k(private_key, message_hash, k, signature)) { return 1; } diff --git a/uECC.h b/uECC.h index cf3efb8..ad24205 100644 --- a/uECC.h +++ b/uECC.h @@ -60,6 +60,14 @@ faster by about 8% but increases the code size. */ #define uECC_BYTES uECC_CONCAT(uECC_size_, uECC_CURVE) +#define uECC_n_size_1 21 /* secp160r1 */ +#define uECC_n_size_2 24 /* secp192r1 */ +#define uECC_n_size_3 32 /* secp256r1 */ +#define uECC_n_size_4 32 /* secp256k1 */ +#define uECC_n_size_5 28 /* secp224r1 */ + +#define uECC_N_BYTES uECC_CONCAT(uECC_n_size_, uECC_CURVE) + #ifdef __cplusplus extern "C" {