mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-15 09:02: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) {
|
static void rdi_reseed(void) {
|
||||||
uint8_t entropy[CHACHA_DRBG_SEED_LENGTH];
|
uint8_t entropy[CHACHA_DRBG_SEED_LENGTH];
|
||||||
random_buffer(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) {
|
static void buffer_refill(void) {
|
||||||
@ -128,7 +128,7 @@ void rdi_start(void) {
|
|||||||
if (rdi_disabled == sectrue) { // if rdi disabled
|
if (rdi_disabled == sectrue) { // if rdi disabled
|
||||||
uint8_t entropy[CHACHA_DRBG_SEED_LENGTH];
|
uint8_t entropy[CHACHA_DRBG_SEED_LENGTH];
|
||||||
random_buffer(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_refill();
|
||||||
buffer_index = 0;
|
buffer_index = 0;
|
||||||
refresh_session_delay = true;
|
refresh_session_delay = true;
|
||||||
|
@ -59,6 +59,12 @@ void ECRYPT_ivsetup(ECRYPT_ctx *x,const u8 *iv)
|
|||||||
x->input[15] = U8TO32_LITTLE(iv + 4);
|
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)
|
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;
|
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
|
* IV setup. After having called ECRYPT_keysetup(), the user is
|
||||||
* allowed to call ECRYPT_ivsetup() different times in order to
|
* allowed to call ECRYPT_ivsetup() different times in order to
|
||||||
* encrypt/decrypt different messages with the same key but different
|
* 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(
|
void ECRYPT_ivsetup(
|
||||||
ECRYPT_ctx* ctx,
|
ECRYPT_ctx* ctx,
|
||||||
const u8* iv);
|
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.
|
* Encryption/decryption of arbitrary length messages.
|
||||||
*
|
*
|
||||||
|
@ -19,44 +19,108 @@
|
|||||||
|
|
||||||
#include "chacha_drbg.h"
|
#include "chacha_drbg.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdint.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)
|
#define MAX(a, b) (a) > (b) ? (a) : (b)
|
||||||
|
|
||||||
void chacha_drbg_init(CHACHA_DRBG_CTX *ctx,
|
static void derivation_function(const uint8_t *input1, size_t input1_length,
|
||||||
const uint8_t entropy[CHACHA_DRBG_SEED_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};
|
uint8_t buffer[MAX(CHACHA_DRBG_KEY_LENGTH, CHACHA_DRBG_IV_LENGTH)] = {0};
|
||||||
ECRYPT_keysetup(&ctx->chacha_ctx, buffer, CHACHA_DRBG_KEY_LENGTH * 8,
|
ECRYPT_keysetup(&ctx->chacha_ctx, buffer, CHACHA_DRBG_KEY_LENGTH * 8,
|
||||||
CHACHA_DRBG_IV_LENGTH * 8);
|
CHACHA_DRBG_IV_LENGTH * 8);
|
||||||
ECRYPT_ivsetup(&ctx->chacha_ctx, buffer);
|
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,
|
static void chacha_drbg_update(CHACHA_DRBG_CTX *ctx,
|
||||||
const uint8_t data[CHACHA_DRBG_SEED_LENGTH]) {
|
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)
|
if (data)
|
||||||
ECRYPT_encrypt_bytes(&ctx->chacha_ctx, data, buffer,
|
ECRYPT_encrypt_bytes(&ctx->chacha_ctx, data, seed, CHACHA_DRBG_SEED_LENGTH);
|
||||||
CHACHA_DRBG_SEED_LENGTH);
|
|
||||||
else
|
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);
|
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,
|
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);
|
ECRYPT_keystream_bytes(&ctx->chacha_ctx, output, output_length);
|
||||||
chacha_drbg_update(ctx, NULL);
|
chacha_drbg_update(ctx, NULL);
|
||||||
ctx->reseed_counter++;
|
ctx->reseed_counter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void chacha_drbg_reseed(CHACHA_DRBG_CTX *ctx,
|
void chacha_drbg_reseed(CHACHA_DRBG_CTX *ctx, const uint8_t *entropy,
|
||||||
const uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]) {
|
size_t entropy_length, const uint8_t *additional_input,
|
||||||
chacha_drbg_update(ctx, entropy);
|
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;
|
ctx->reseed_counter = 1;
|
||||||
}
|
}
|
||||||
|
@ -21,23 +21,34 @@
|
|||||||
#define __CHACHA_DRBG__
|
#define __CHACHA_DRBG__
|
||||||
|
|
||||||
#include "chacha20poly1305/chacha20poly1305.h"
|
#include "chacha20poly1305/chacha20poly1305.h"
|
||||||
|
#include "sha2.h"
|
||||||
|
|
||||||
// Very fast deterministic random bit generator inspired by CTR_DRBG in NIST SP
|
// A very fast deterministic random bit generator based on CTR_DRBG in NIST SP
|
||||||
// 800-90A
|
// 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
|
// Length of inputs of chacha_drbg_init (entropy and nonce) or
|
||||||
#define CHACHA_DRBG_IV_LENGTH 8
|
// chacha_drbg_reseed (entropy and additional_input) that fill exactly
|
||||||
#define CHACHA_DRBG_SEED_LENGTH (CHACHA_DRBG_KEY_LENGTH + CHACHA_DRBG_IV_LENGTH)
|
// 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 {
|
typedef struct _CHACHA_DRBG_CTX {
|
||||||
ECRYPT_ctx chacha_ctx;
|
ECRYPT_ctx chacha_ctx;
|
||||||
uint32_t reseed_counter;
|
uint32_t reseed_counter;
|
||||||
} CHACHA_DRBG_CTX;
|
} CHACHA_DRBG_CTX;
|
||||||
|
|
||||||
void chacha_drbg_init(CHACHA_DRBG_CTX *ctx,
|
void chacha_drbg_init(CHACHA_DRBG_CTX *ctx, const uint8_t *entropy,
|
||||||
const uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]);
|
size_t entropy_length, const uint8_t *nonce,
|
||||||
void chacha_drbg_reseed(CHACHA_DRBG_CTX *ctx,
|
size_t nonce_length);
|
||||||
const uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]);
|
|
||||||
void chacha_drbg_generate(CHACHA_DRBG_CTX *ctx, uint8_t *output,
|
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__
|
#endif // __CHACHA_DRBG__
|
||||||
|
@ -4628,21 +4628,42 @@ START_TEST(test_blake2s) {
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
START_TEST(test_chacha_drbg) {
|
#include <stdio.h>
|
||||||
char entropy[] = "8a09b482de30c12ee1d2eb69dd49753d4252b3d36128ee1e";
|
|
||||||
char reseed[] = "9ec4b991f939dbb44355392d05cd793a2e281809d2ed7139";
|
|
||||||
char expected[] =
|
|
||||||
"4caaeb7db073d34b37b5b26f8a3863849f298dab754966e0f75526823216057c2626e044"
|
|
||||||
"9f7ffda7c3dba8841c06af01029eebfd4d4cae951c19c9f6ff6812783e58438840883401"
|
|
||||||
"2a05cd24c38cd22d18296aceed6829299190ebb9455eb8fd8d1cac1d";
|
|
||||||
uint8_t result[100];
|
|
||||||
|
|
||||||
|
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_CTX ctx;
|
||||||
chacha_drbg_init(&ctx, fromhex(entropy));
|
chacha_drbg_init(&ctx, fromhex(entropy), strlen(entropy) / 2, nonce_bytes,
|
||||||
chacha_drbg_reseed(&ctx, fromhex(reseed));
|
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));
|
||||||
chacha_drbg_generate(&ctx, result, sizeof(result));
|
chacha_drbg_generate(&ctx, result, sizeof(result));
|
||||||
ck_assert_mem_eq(result, fromhex(expected), 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
|
END_TEST
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user