diff --git a/core/embed/trezorhal/random_delays.c b/core/embed/trezorhal/random_delays.c index 47d930fda..d8994b591 100644 --- a/core/embed/trezorhal/random_delays.c +++ b/core/embed/trezorhal/random_delays.c @@ -59,7 +59,7 @@ static secbool rdi_disabled = sectrue; static void rdi_reseed(void) { uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]; random_buffer(entropy, CHACHA_DRBG_SEED_LENGTH); - chacha_drbg_reseed(&drbg_ctx, entropy); + chacha_drbg_reseed(&drbg_ctx, entropy, sizeof(entropy), NULL, 0); } static void buffer_refill(void) { @@ -128,7 +128,7 @@ void rdi_start(void) { if (rdi_disabled == sectrue) { // if rdi disabled uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]; random_buffer(entropy, CHACHA_DRBG_SEED_LENGTH); - chacha_drbg_init(&drbg_ctx, entropy); + chacha_drbg_init(&drbg_ctx, entropy, sizeof(entropy), NULL, 0); buffer_refill(); buffer_index = 0; refresh_session_delay = true; diff --git a/crypto/chacha20poly1305/chacha_merged.c b/crypto/chacha20poly1305/chacha_merged.c index 7989f0c01..f5c37fa8a 100644 --- a/crypto/chacha20poly1305/chacha_merged.c +++ b/crypto/chacha20poly1305/chacha_merged.c @@ -59,6 +59,12 @@ void ECRYPT_ivsetup(ECRYPT_ctx *x,const u8 *iv) x->input[15] = U8TO32_LITTLE(iv + 4); } +void ECRYPT_ctrsetup(ECRYPT_ctx *x,const u8 *ctr) +{ + x->input[12] = U8TO32_LITTLE(ctr + 0); + x->input[13] = U8TO32_LITTLE(ctr + 4); +} + void ECRYPT_encrypt_bytes(ECRYPT_ctx *x,const u8 *m,u8 *c,u32 bytes) { u32 x0 = 0, x1 = 0, x2 = 0, x3 = 0, x4 = 0, x5 = 0, x6 = 0, x7 = 0, x8 = 0, x9 = 0, x10 = 0, x11 = 0, x12 = 0, x13 = 0, x14 = 0, x15 = 0; diff --git a/crypto/chacha20poly1305/ecrypt-sync.h b/crypto/chacha20poly1305/ecrypt-sync.h index 39be5c9bc..efce9dde2 100644 --- a/crypto/chacha20poly1305/ecrypt-sync.h +++ b/crypto/chacha20poly1305/ecrypt-sync.h @@ -90,12 +90,21 @@ void ECRYPT_keysetup( * IV setup. After having called ECRYPT_keysetup(), the user is * allowed to call ECRYPT_ivsetup() different times in order to * encrypt/decrypt different messages with the same key but different - * IV's. + * IV's. ECRYPT_ivsetup() also sets block counter to zero. */ void ECRYPT_ivsetup( ECRYPT_ctx* ctx, const u8* iv); +/* + * Block counter setup. It is used only for special purposes, + * since block counter is usually initialized with ECRYPT_ivsetup. + * ECRYPT_ctrsetup has to be called after ECRYPT_ivsetup. + */ +void ECRYPT_ctrsetup( + ECRYPT_ctx* ctx, + const u8* ctr); + /* * Encryption/decryption of arbitrary length messages. * diff --git a/crypto/chacha_drbg.c b/crypto/chacha_drbg.c index 4f90ad5fa..663b1a355 100644 --- a/crypto/chacha_drbg.c +++ b/crypto/chacha_drbg.c @@ -19,44 +19,108 @@ #include "chacha_drbg.h" +#include #include +#include + +#include "chacha20poly1305/ecrypt-portable.h" +#include "memzero.h" +#include "sha2.h" + +#define CHACHA_DRBG_KEY_LENGTH 32 +#define CHACHA_DRBG_COUNTER_LENGTH 8 +#define CHACHA_DRBG_IV_LENGTH 8 +#define CHACHA_DRBG_SEED_LENGTH \ + (CHACHA_DRBG_KEY_LENGTH + CHACHA_DRBG_COUNTER_LENGTH + CHACHA_DRBG_IV_LENGTH) #define MAX(a, b) (a) > (b) ? (a) : (b) -void chacha_drbg_init(CHACHA_DRBG_CTX *ctx, - const uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]) { +static void derivation_function(const uint8_t *input1, size_t input1_length, + const uint8_t *input2, size_t input2_length, + uint8_t *output, size_t output_length) { + // Implementation of Hash_df from NIST SP 800-90A + uint32_t block_count = (output_length - 1) / SHA256_DIGEST_LENGTH + 1; + size_t partial_block_length = output_length % SHA256_DIGEST_LENGTH; + assert(block_count <= 255); + + uint32_t output_length_bits = output_length * 8; +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE32(output_length_bits, output_length_bits); +#endif + + SHA256_CTX ctx = {0}; + + for (uint8_t counter = 1; counter <= block_count; counter++) { + sha256_Init(&ctx); + sha256_Update(&ctx, &counter, sizeof(counter)); + sha256_Update(&ctx, (uint8_t *)&output_length_bits, + sizeof(output_length_bits)); + sha256_Update(&ctx, input1, input1_length); + sha256_Update(&ctx, input2, input2_length); + + if (counter != block_count || partial_block_length == 0) { + sha256_Final(&ctx, output); + output += SHA256_DIGEST_LENGTH; + } else { // last block is partial + uint8_t digest[SHA256_DIGEST_LENGTH] = {0}; + sha256_Final(&ctx, digest); + memcpy(output, digest, partial_block_length); + memzero(digest, sizeof(digest)); + } + } + + memzero(&ctx, sizeof(ctx)); +} + +void chacha_drbg_init(CHACHA_DRBG_CTX *ctx, const uint8_t *entropy, + size_t entropy_length, const uint8_t *nonce, + size_t nonce_length) { uint8_t buffer[MAX(CHACHA_DRBG_KEY_LENGTH, CHACHA_DRBG_IV_LENGTH)] = {0}; ECRYPT_keysetup(&ctx->chacha_ctx, buffer, CHACHA_DRBG_KEY_LENGTH * 8, CHACHA_DRBG_IV_LENGTH * 8); ECRYPT_ivsetup(&ctx->chacha_ctx, buffer); - chacha_drbg_reseed(ctx, entropy); + chacha_drbg_reseed(ctx, entropy, entropy_length, nonce, nonce_length); } static void chacha_drbg_update(CHACHA_DRBG_CTX *ctx, const uint8_t data[CHACHA_DRBG_SEED_LENGTH]) { - uint8_t buffer[CHACHA_DRBG_SEED_LENGTH] = {0}; + uint8_t seed[CHACHA_DRBG_SEED_LENGTH] = {0}; if (data) - ECRYPT_encrypt_bytes(&ctx->chacha_ctx, data, buffer, - CHACHA_DRBG_SEED_LENGTH); + ECRYPT_encrypt_bytes(&ctx->chacha_ctx, data, seed, CHACHA_DRBG_SEED_LENGTH); else - ECRYPT_keystream_bytes(&ctx->chacha_ctx, buffer, CHACHA_DRBG_SEED_LENGTH); + ECRYPT_keystream_bytes(&ctx->chacha_ctx, seed, CHACHA_DRBG_SEED_LENGTH); - ECRYPT_keysetup(&ctx->chacha_ctx, buffer, CHACHA_DRBG_KEY_LENGTH * 8, + ECRYPT_keysetup(&ctx->chacha_ctx, seed, CHACHA_DRBG_KEY_LENGTH * 8, CHACHA_DRBG_IV_LENGTH * 8); - ECRYPT_ivsetup(&ctx->chacha_ctx, buffer + CHACHA_DRBG_KEY_LENGTH); + + ECRYPT_ivsetup(&ctx->chacha_ctx, + seed + CHACHA_DRBG_KEY_LENGTH + CHACHA_DRBG_COUNTER_LENGTH); + + ECRYPT_ctrsetup(&ctx->chacha_ctx, seed + CHACHA_DRBG_KEY_LENGTH); + + memzero(seed, sizeof(seed)); } void chacha_drbg_generate(CHACHA_DRBG_CTX *ctx, uint8_t *output, - uint8_t output_length) { + size_t output_length) { + assert(output_length < 65536); + assert(ctx->reseed_counter + 1 != 0); + ECRYPT_keystream_bytes(&ctx->chacha_ctx, output, output_length); chacha_drbg_update(ctx, NULL); ctx->reseed_counter++; } -void chacha_drbg_reseed(CHACHA_DRBG_CTX *ctx, - const uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]) { - chacha_drbg_update(ctx, entropy); +void chacha_drbg_reseed(CHACHA_DRBG_CTX *ctx, const uint8_t *entropy, + size_t entropy_length, const uint8_t *additional_input, + size_t additional_input_length) { + uint8_t seed[CHACHA_DRBG_SEED_LENGTH] = {0}; + derivation_function(entropy, entropy_length, additional_input, + additional_input_length, seed, sizeof(seed)); + chacha_drbg_update(ctx, seed); + memzero(seed, sizeof(seed)); + ctx->reseed_counter = 1; } diff --git a/crypto/chacha_drbg.h b/crypto/chacha_drbg.h index 0f8979eaf..83e2d0b42 100644 --- a/crypto/chacha_drbg.h +++ b/crypto/chacha_drbg.h @@ -21,23 +21,34 @@ #define __CHACHA_DRBG__ #include "chacha20poly1305/chacha20poly1305.h" +#include "sha2.h" -// Very fast deterministic random bit generator inspired by CTR_DRBG in NIST SP -// 800-90A +// A very fast deterministic random bit generator based on CTR_DRBG in NIST SP +// 800-90A. Chacha is used instead of a block cipher in the counter mode, SHA256 +// is used as a derivation function. The highest supported security strength is +// at least 256 bits. Reseeding is left up to caller. -#define CHACHA_DRBG_KEY_LENGTH 16 -#define CHACHA_DRBG_IV_LENGTH 8 -#define CHACHA_DRBG_SEED_LENGTH (CHACHA_DRBG_KEY_LENGTH + CHACHA_DRBG_IV_LENGTH) +// Length of inputs of chacha_drbg_init (entropy and nonce) or +// chacha_drbg_reseed (entropy and additional_input) that fill exactly +// block_count blocks of hash function in derivation_function. There is no need +// the input to have this length, it's just an optimalization. +#define CHACHA_DRBG_OPTIMAL_RESEED_LENGTH(block_count) \ + ((block_count)*SHA256_BLOCK_LENGTH - 1 - 4 - 9) +// 1 = sizeof(counter), 4 = sizeof(output_length) in +// derivation_function, 9 is length of SHA256 padding of message +// aligned to bytes typedef struct _CHACHA_DRBG_CTX { ECRYPT_ctx chacha_ctx; uint32_t reseed_counter; } CHACHA_DRBG_CTX; -void chacha_drbg_init(CHACHA_DRBG_CTX *ctx, - const uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]); -void chacha_drbg_reseed(CHACHA_DRBG_CTX *ctx, - const uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]); +void chacha_drbg_init(CHACHA_DRBG_CTX *ctx, const uint8_t *entropy, + size_t entropy_length, const uint8_t *nonce, + size_t nonce_length); void chacha_drbg_generate(CHACHA_DRBG_CTX *ctx, uint8_t *output, - uint8_t output_length); + size_t output_length); +void chacha_drbg_reseed(CHACHA_DRBG_CTX *ctx, const uint8_t *entropy, + size_t entropy_length, const uint8_t *additional_input, + size_t additional_input_length); #endif // __CHACHA_DRBG__ diff --git a/crypto/tests/test_check.c b/crypto/tests/test_check.c index e2ffd5291..e26313d17 100644 --- a/crypto/tests/test_check.c +++ b/crypto/tests/test_check.c @@ -4628,21 +4628,42 @@ START_TEST(test_blake2s) { } END_TEST +#include + START_TEST(test_chacha_drbg) { - char entropy[] = "8a09b482de30c12ee1d2eb69dd49753d4252b3d36128ee1e"; - char reseed[] = "9ec4b991f939dbb44355392d05cd793a2e281809d2ed7139"; + char entropy[] = + "06032cd5eed33f39265f49ecb142c511da9aff2af71203bffaf34a9ca5bd9c0d"; + char nonce[] = "0e66f71edc43e42a45ad3c6fc6cdc4df"; + char reseed[] = + "01920a4e669ed3a85ae8a33b35a74ad7fb2a6bb4cf395ce00334a9c9a5a5d552"; char expected[] = - "4caaeb7db073d34b37b5b26f8a3863849f298dab754966e0f75526823216057c2626e044" - "9f7ffda7c3dba8841c06af01029eebfd4d4cae951c19c9f6ff6812783e58438840883401" - "2a05cd24c38cd22d18296aceed6829299190ebb9455eb8fd8d1cac1d"; - uint8_t result[100]; + "e172c5d18f3e8c77e9f66f9e1c24560772117161a9a0a237ab490b0769ad5d910f5dfb36" + "22edc06c18be0495c52588b200893d90fd80ff2149ead0c45d062c90f5890149c0f9591c" + "41bf4110865129a0fe524f210cca1340bd16f71f57906946cbaaf1fa863897d70d203b5a" + "f9996f756eec08861ee5875f9d915adcddc38719"; + uint8_t result[128]; + uint8_t null_bytes[128] = {0}; + uint8_t nonce_bytes[16]; + memcpy(nonce_bytes, fromhex(nonce), sizeof(nonce_bytes)); CHACHA_DRBG_CTX ctx; - chacha_drbg_init(&ctx, fromhex(entropy)); - chacha_drbg_reseed(&ctx, fromhex(reseed)); + chacha_drbg_init(&ctx, fromhex(entropy), strlen(entropy) / 2, nonce_bytes, + strlen(nonce) / 2); + chacha_drbg_reseed(&ctx, fromhex(reseed), strlen(reseed) / 2, NULL, 0); chacha_drbg_generate(&ctx, result, sizeof(result)); chacha_drbg_generate(&ctx, result, sizeof(result)); ck_assert_mem_eq(result, fromhex(expected), sizeof(result)); + + for (size_t i = 0; i <= sizeof(result); ++i) { + chacha_drbg_init(&ctx, fromhex(entropy), strlen(entropy) / 2, nonce_bytes, + strlen(nonce) / 2); + chacha_drbg_reseed(&ctx, fromhex(reseed), strlen(reseed) / 2, NULL, 0); + chacha_drbg_generate(&ctx, result, sizeof(result) - 13); + memset(result, 0, sizeof(result)); + chacha_drbg_generate(&ctx, result, i); + ck_assert_mem_eq(result, fromhex(expected), i); + ck_assert_mem_eq(result + i, null_bytes, sizeof(result) - i); + } } END_TEST