Index: head/sys/dev/random/fortuna.c =================================================================== --- head/sys/dev/random/fortuna.c +++ head/sys/dev/random/fortuna.c @@ -109,7 +109,7 @@ } fs_pool[RANDOM_FORTUNA_NPOOLS]; u_int fs_reseedcount; /* ReseedCnt */ uint128_t fs_counter; /* C */ - struct randomdev_key fs_key; /* K */ + union randomdev_key fs_key; /* K */ u_int fs_minpoolsize; /* Extras */ /* Extras for the OS */ #ifdef _KERNEL @@ -271,16 +271,27 @@ { struct randomdev_hash context; uint8_t hash[RANDOM_KEYSIZE]; + const void *keymaterial; + size_t keysz; + bool seeded; RANDOM_RESEED_ASSERT_LOCK_OWNED(); + + seeded = random_fortuna_seeded(); + if (seeded) { + randomdev_getkey(&fortuna_state.fs_key, &keymaterial, &keysz); + KASSERT(keysz == RANDOM_KEYSIZE, ("%s: key size %zu not %u", + __func__, keysz, (unsigned)RANDOM_KEYSIZE)); + } + /*- * FS&K - K = Hd(K|s) where Hd(m) is H(H(0^512|m)) * - C = C + 1 */ randomdev_hash_init(&context); randomdev_hash_iterate(&context, zero_region, RANDOM_ZERO_BLOCKSIZE); - randomdev_hash_iterate(&context, &fortuna_state.fs_key.key.keyMaterial, - fortuna_state.fs_key.key.keyLen / 8); + if (seeded) + randomdev_hash_iterate(&context, keymaterial, keysz); randomdev_hash_iterate(&context, entropy_data, RANDOM_KEYSIZE*blockcount); randomdev_hash_finish(&context, hash); randomdev_hash_init(&context); Index: head/sys/dev/random/hash.h =================================================================== --- head/sys/dev/random/hash.h +++ head/sys/dev/random/hash.h @@ -29,6 +29,7 @@ #ifndef SYS_DEV_RANDOM_HASH_H_INCLUDED #define SYS_DEV_RANDOM_HASH_H_INCLUDED +#include #include /* Keys are formed from cipher blocks */ @@ -45,15 +46,22 @@ SHA256_CTX sha; }; -struct randomdev_key { - keyInstance key; /* Key schedule */ - cipherInstance cipher; /* Rijndael internal */ +union randomdev_key { + struct { + keyInstance key; /* Key schedule */ + cipherInstance cipher; /* Rijndael internal */ + }; + struct chacha_ctx chacha; }; +extern bool fortuna_chachamode; + void randomdev_hash_init(struct randomdev_hash *); void randomdev_hash_iterate(struct randomdev_hash *, const void *, size_t); void randomdev_hash_finish(struct randomdev_hash *, void *); -void randomdev_encrypt_init(struct randomdev_key *, const void *); -void randomdev_keystream(struct randomdev_key *context, uint128_t *, void *, u_int); + +void randomdev_encrypt_init(union randomdev_key *, const void *); +void randomdev_keystream(union randomdev_key *context, uint128_t *, void *, u_int); +void randomdev_getkey(union randomdev_key *, const void **, size_t *); #endif /* SYS_DEV_RANDOM_HASH_H_INCLUDED */ Index: head/sys/dev/random/hash.c =================================================================== --- head/sys/dev/random/hash.c +++ head/sys/dev/random/hash.c @@ -30,6 +30,9 @@ #ifdef _KERNEL #include +#include +#include +#include #include #else /* !_KERNEL */ #include @@ -42,14 +45,39 @@ #include "unit_test.h" #endif /* _KERNEL */ +#define CHACHA_EMBED +#define KEYSTREAM_ONLY +#define CHACHA_NONCE0_CTR128 +#include #include #include #include +#ifdef _KERNEL +#include +#endif /* This code presumes that RANDOM_KEYSIZE is twice as large as RANDOM_BLOCKSIZE */ CTASSERT(RANDOM_KEYSIZE == 2*RANDOM_BLOCKSIZE); +/* Validate that full Chacha IV is as large as the 128-bit counter */ +_Static_assert(CHACHA_STATELEN == RANDOM_BLOCKSIZE, ""); + +/* + * Experimental Chacha20-based PRF for Fortuna keystream primitive. For now, + * disabled by default. But we may enable it in the future. + * + * Benefits include somewhat faster keystream generation compared with + * unaccelerated AES-ICM. + */ +bool random_chachamode = false; +#ifdef _KERNEL +SYSCTL_BOOL(_kern_random, OID_AUTO, use_chacha20_cipher, CTLFLAG_RDTUN, + &random_chachamode, 0, + "If non-zero, use the ChaCha20 cipher for randomdev PRF. " + "If zero, use AES-ICM cipher for randomdev PRF (default)."); +#endif + /* Initialise the hash */ void randomdev_hash_init(struct randomdev_hash *context) @@ -81,11 +109,15 @@ * data. */ void -randomdev_encrypt_init(struct randomdev_key *context, const void *data) +randomdev_encrypt_init(union randomdev_key *context, const void *data) { - rijndael_cipherInit(&context->cipher, MODE_ECB, NULL); - rijndael_makeKey(&context->key, DIR_ENCRYPT, RANDOM_KEYSIZE*8, data); + if (random_chachamode) { + chacha_keysetup(&context->chacha, data, RANDOM_KEYSIZE * 8); + } else { + rijndael_cipherInit(&context->cipher, MODE_ECB, NULL); + rijndael_makeKey(&context->key, DIR_ENCRYPT, RANDOM_KEYSIZE*8, data); + } } /* @@ -95,19 +127,96 @@ * bytes are generated. */ void -randomdev_keystream(struct randomdev_key *context, uint128_t *ctr, +randomdev_keystream(union randomdev_key *context, uint128_t *ctr, void *d_out, u_int blockcount) { u_int i; - for (i = 0; i < blockcount; i++) { - /*- - * FS&K - r = r|E(K,C) - * - C = C + 1 + if (random_chachamode) { + uint128_t lectr; + + /* + * Chacha always encodes and increments the counter little + * endian. So on BE machines, we must provide a swapped + * counter to chacha, and swap the output too. */ - rijndael_blockEncrypt(&context->cipher, &context->key, - (void *)ctr, RANDOM_BLOCKSIZE * 8, d_out); - d_out = (char *)d_out + RANDOM_BLOCKSIZE; - uint128_increment(ctr); + le128enc(&lectr, *ctr); + + chacha_ivsetup(&context->chacha, NULL, (const void *)&lectr); + chacha_encrypt_bytes(&context->chacha, NULL, d_out, + RANDOM_BLOCKSIZE * blockcount); + + /* + * Decode Chacha-updated LE counter to native endian and store + * it back in the caller's in-out parameter. + */ + chacha_ctrsave(&context->chacha, (void *)&lectr); + *ctr = le128dec(&lectr); + } else { + for (i = 0; i < blockcount; i++) { + /*- + * FS&K - r = r|E(K,C) + * - C = C + 1 + */ + rijndael_blockEncrypt(&context->cipher, &context->key, + (void *)ctr, RANDOM_BLOCKSIZE * 8, d_out); + d_out = (char *)d_out + RANDOM_BLOCKSIZE; + uint128_increment(ctr); + } } +} + +/* + * Fetch a pointer to the relevant key material and its size. + * + * This API is expected to only be used only for reseeding, where the + * endianness does not matter; the goal is to simply incorporate the key + * material into the hash iterator that will produce key'. + * + * Do not expect the buffer pointed to by this API to match the exact + * endianness, etc, as the key material that was supplied to + * randomdev_encrypt_init(). + */ +void +randomdev_getkey(union randomdev_key *context, const void **keyp, size_t *szp) +{ + + if (!random_chachamode) { + *keyp = &context->key.keyMaterial; + *szp = context->key.keyLen / 8; + return; + } + + /* Chacha20 mode */ + *keyp = (const void *)&context->chacha.input[4]; + + /* Sanity check keysize */ + if (context->chacha.input[0] == U8TO32_LITTLE(sigma) && + context->chacha.input[1] == U8TO32_LITTLE(&sigma[4]) && + context->chacha.input[2] == U8TO32_LITTLE(&sigma[8]) && + context->chacha.input[3] == U8TO32_LITTLE(&sigma[12])) { + *szp = 32; + return; + } + +#if 0 + /* + * Included for the sake of completeness; as-implemented, Fortuna + * doesn't need or use 128-bit Chacha20. + */ + if (context->chacha->input[0] == U8TO32_LITTLE(tau) && + context->chacha->input[1] == U8TO32_LITTLE(&tau[4]) && + context->chacha->input[2] == U8TO32_LITTLE(&tau[8]) && + context->chacha->input[3] == U8TO32_LITTLE(&tau[12])) { + *szp = 16; + return; + } +#endif + +#ifdef _KERNEL + panic("%s: Invalid chacha20 keysize: %16D\n", __func__, + (void *)context->chacha.input, " "); +#else + raise(SIGKILL); +#endif } Index: head/sys/dev/random/uint128.h =================================================================== --- head/sys/dev/random/uint128.h +++ head/sys/dev/random/uint128.h @@ -29,6 +29,8 @@ #ifndef SYS_DEV_RANDOM_UINT128_H_INCLUDED #define SYS_DEV_RANDOM_UINT128_H_INCLUDED +#include + /* This whole thing is a crock :-( * * Everyone knows you always need the __uint128_t types! @@ -63,13 +65,49 @@ #endif } +static __inline bool +uint128_equals(uint128_t a, uint128_t b) +{ +#ifdef USE_REAL_UINT128_T + return (a == b); +#else + return (a.u128t_word0 == b.u128t_word0 && + a.u128t_word1 == b.u128t_word1); +#endif +} + static __inline int uint128_is_zero(uint128_t big_uint) { + return (uint128_equals(big_uint, UINT128_ZERO)); +} + +static __inline uint128_t +le128dec(const void *pp) +{ + const uint8_t *p = pp; + #ifdef USE_REAL_UINT128_T - return (big_uint == UINT128_ZERO); + return (((uint128_t)le64dec(p + 8) << 64) | le64dec(p)); #else - return (big_uint.u128t_word0 == 0UL && big_uint.u128t_word1 == 0UL); + return ((uint128_t){ + .u128t_word0 = le64dec(p), + .u128t_word1 = le64dec(p + 8), + }); +#endif +} + +static __inline void +le128enc(void *pp, uint128_t u) +{ + uint8_t *p = pp; + +#ifdef USE_REAL_UINT128_T + le64enc(p, (uint64_t)(u & UINT64_MAX)); + le64enc(p + 8, (uint64_t)(u >> 64)); +#else + le64enc(p, u.u128t_word0); + le64enc(p + 8, u.u128t_word1); #endif }