mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-15 00:52:02 +00:00
feat(core): make chacha_drbg more robust
This commit is contained in:
parent
34f5d508b0
commit
e1a5f42c81
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -19,44 +19,108 @@
|
||||
|
||||
#include "chacha_drbg.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
@ -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__
|
||||
|
@ -4628,21 +4628,42 @@ START_TEST(test_blake2s) {
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_chacha_drbg) {
|
||||
char entropy[] = "8a09b482de30c12ee1d2eb69dd49753d4252b3d36128ee1e";
|
||||
char reseed[] = "9ec4b991f939dbb44355392d05cd793a2e281809d2ed7139";
|
||||
char expected[] =
|
||||
"4caaeb7db073d34b37b5b26f8a3863849f298dab754966e0f75526823216057c2626e044"
|
||||
"9f7ffda7c3dba8841c06af01029eebfd4d4cae951c19c9f6ff6812783e58438840883401"
|
||||
"2a05cd24c38cd22d18296aceed6829299190ebb9455eb8fd8d1cac1d";
|
||||
uint8_t result[100];
|
||||
#include <stdio.h>
|
||||
|
||||
START_TEST(test_chacha_drbg) {
|
||||
char entropy[] =
|
||||
"06032cd5eed33f39265f49ecb142c511da9aff2af71203bffaf34a9ca5bd9c0d";
|
||||
char nonce[] = "0e66f71edc43e42a45ad3c6fc6cdc4df";
|
||||
char reseed[] =
|
||||
"01920a4e669ed3a85ae8a33b35a74ad7fb2a6bb4cf395ce00334a9c9a5a5d552";
|
||||
char expected[] =
|
||||
"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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user