feat(core): make chacha_drbg more robust

pull/1625/head
Ondřej Vejpustek 3 years ago
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
#include <stdio.h>
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

Loading…
Cancel
Save