1
0
mirror of https://github.com/taigrr/arduinolibs synced 2025-01-18 04:33:12 -08:00

Hide the ChaCha20 state inside other buffers

This commit is contained in:
Rhys Weatherley 2016-08-25 16:14:10 +10:00
parent d2ef8c3dbc
commit a495d367c5

View File

@ -140,6 +140,13 @@
/** @cond */ /** @cond */
typedef struct
{
uint32_t input[16];
uint32_t output[16];
} NewHopeChaChaState;
// The following is public domain code from the reference C version of // The following is public domain code from the reference C version of
// New Hope at https://cryptojedi.org/crypto/#newhope. This part of // New Hope at https://cryptojedi.org/crypto/#newhope. This part of
// the Arduino port remains public domain. Original authors: // the Arduino port remains public domain. Original authors:
@ -598,26 +605,19 @@ static int16_t LDDecode(int32_t xi0, int32_t xi1, int32_t xi2, int32_t xi3)
return t&1; return t&1;
} }
static void crypto_chacha20_set_key(uint32_t *block, const unsigned char *k, const unsigned char *n); static void helprec(NewHopeChaChaState *chacha, uint16_t *c, const uint16_t *v, unsigned char nonce)
static void helprec(uint16_t *c, const uint16_t *v, const unsigned char *seed, unsigned char nonce)
{ {
int32_t v0[4], v1[4], v_tmp[4], k; int32_t v0[4], v1[4], v_tmp[4], k;
unsigned char rbit; unsigned char rbit;
unsigned char *rand; unsigned char *rand;
unsigned char n[8];
uint32_t input[16];
uint32_t output[16];
int i; int i;
for(i=0;i<7;i++) chacha->input[12] = 0;
n[i] = 0; chacha->input[13] = 0;
n[7] = nonce; chacha->input[14] = 0;
chacha->input[15] = (((uint32_t)nonce) << 24); // Assumes little-endian.
//crypto_stream_chacha20(rand,32,n,seed); ChaCha::hashCore(chacha->output, chacha->input, 20);
crypto_chacha20_set_key(input, seed, n); rand = (unsigned char *)chacha->output;
ChaCha::hashCore(output, input, 20);
rand = (unsigned char *)output;
for(i=0; i<256; i++) for(i=0; i<256; i++)
{ {
@ -641,8 +641,7 @@ static void helprec(uint16_t *c, const uint16_t *v, const unsigned char *seed, u
c[768+i] = ( -k + 2*v_tmp[3]) & 3; c[768+i] = ( -k + 2*v_tmp[3]) & 3;
} }
clean(input); clean(&chacha, sizeof(chacha));
clean(output);
} }
static void rec(unsigned char *key, const uint16_t *v, const uint16_t *c) static void rec(unsigned char *key, const uint16_t *v, const uint16_t *c)
@ -853,17 +852,17 @@ static void batcher84(uint16_t *x)
oddeven_merge_sort_range(x, 0, 127); oddeven_merge_sort_range(x, 0, 127);
} }
// Formats the ChaCha20 input block using a key and nonce. // Formats the ChaCha20 input block using a key.
static void crypto_chacha20_set_key(uint32_t *block, const unsigned char *k, const unsigned char *n) static void crypto_chacha20_set_key(uint32_t *block, const unsigned char *k)
{ {
static const char tag256[] = "expand 32-byte k"; static const char tag256[] PROGMEM = "expand 32-byte k";
#if defined(__AVR__)
memcpy_P(block, tag256, 16);
#else
memcpy(block, tag256, 16); memcpy(block, tag256, 16);
#endif
memcpy(block + 4, k, 32); memcpy(block + 4, k, 32);
memset(block + 12, 0, 8); memset(block + 12, 0, 8);
if (n)
memcpy(block + 14, n, 8);
else
memset(block + 14, 0, 8);
} }
static void poly_uniform(SHAKE128 *shake, uint16_t *a, const unsigned char *seed) static void poly_uniform(SHAKE128 *shake, uint16_t *a, const unsigned char *seed)
@ -901,10 +900,8 @@ static void poly_uniform_torref(SHAKE128 *shake, uint16_t *a, const unsigned cha
} while (discardtopoly(a)); } while (discardtopoly(a));
} }
static void poly_getnoise(uint16_t *r, const unsigned char *seed, unsigned char nonce) static void poly_getnoise(uint16_t *r, NewHopeChaChaState *chacha, unsigned char nonce)
{ {
uint32_t input[16];
uint32_t buf[16];
int i, j; int i, j;
uint32_t a, b; uint32_t a, b;
@ -914,27 +911,29 @@ static void poly_getnoise(uint16_t *r, const unsigned char *seed, unsigned char
// as it will be just as random in both directions. It's only a // as it will be just as random in both directions. It's only a
// problem for verifying fixed test vectors. // problem for verifying fixed test vectors.
crypto_chacha20_set_key(input, seed, 0); chacha->input[12] = 0;
input[14] = nonce; // Assumes little-endian. chacha->input[13] = 0;
chacha->input[14] = nonce; // Assumes little-endian.
chacha->input[15] = 0;
for (i = 0; i < PARAM_N; ++i) { for (i = 0; i < PARAM_N; ++i) {
// Generate a new block of random data if necessary. // Generate a new block of random data if necessary.
j = i % 16; j = i % 16;
if (j == 0) { if (j == 0) {
ChaCha::hashCore(buf, input, 20); ChaCha::hashCore(chacha->output, chacha->input, 20);
++(input[12]); // Assumes little-endian. ++(chacha->input[12]); // Assumes little-endian.
} }
// This is a slightly more efficient way to count bits than in // This is a slightly more efficient way to count bits than in
// the reference C implementation. The technique is from: // the reference C implementation. The technique is from:
// https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
a = buf[j] & 0xFFFF; // Assumes little-endian. a = chacha->output[j] & 0xFFFF; // Assumes little-endian.
a = a - ((a >> 1) & 0x5555); a = a - ((a >> 1) & 0x5555);
a = (a & 0x3333) + ((a >> 2) & 0x3333); a = (a & 0x3333) + ((a >> 2) & 0x3333);
a = ((a >> 4) + a) & 0x0F0F; a = ((a >> 4) + a) & 0x0F0F;
a = ((a >> 8) + a) & 0x00FF; a = ((a >> 8) + a) & 0x00FF;
b = (buf[j] >> 16) & 0xFFFF; // Assumes little-endian. b = (chacha->output[j] >> 16) & 0xFFFF; // Assumes little-endian.
b = b - ((b >> 1) & 0x5555); b = b - ((b >> 1) & 0x5555);
b = (b & 0x3333) + ((b >> 2) & 0x3333); b = (b & 0x3333) + ((b >> 2) & 0x3333);
b = ((b >> 4) + b) & 0x0F0F; b = ((b >> 4) + b) & 0x0F0F;
@ -943,8 +942,7 @@ static void poly_getnoise(uint16_t *r, const unsigned char *seed, unsigned char
r[i] = a + PARAM_Q - b; r[i] = a + PARAM_Q - b;
} }
clean(input); clean(&chacha, sizeof(chacha));
clean(buf);
} }
/** @endcond */ /** @endcond */
@ -975,6 +973,12 @@ static void poly_getnoise(uint16_t *r, const unsigned char *seed, unsigned char
#define INIT_OBJ(type, name) \ #define INIT_OBJ(type, name) \
type *name = new (state.name##_x) type type *name = new (state.name##_x) type
#if defined(__AVR__)
#define NEWHOPE_BYTE_ALIGNED 1
#else
#define NEWHOPE_BYTE_ALIGNED 0
#endif
/** @endcond */ /** @endcond */
/** /**
@ -1014,10 +1018,17 @@ void NewHope::keygen(uint8_t send[NEWHOPE_SENDABYTES], NewHopePrivateKey &sk,
}; };
ALLOC_OBJ(SHA3_256, sha3); // SHA3 object for hashing the seed. ALLOC_OBJ(SHA3_256, sha3); // SHA3 object for hashing the seed.
} state; } state;
// Hide the ChaCha state and the noise seed inside "send".
#if NEWHOPE_BYTE_ALIGNED
#define chacha (*((NewHopeChaChaState *)send))
#else
NewHopeChaChaState chacha;
#endif
#if NEWHOPE_SMALL_FOOTPRINT #if NEWHOPE_SMALL_FOOTPRINT
#define noiseseed (sk.seed) #define noiseseed (sk.seed)
#else #else
uint8_t noiseseed[32]; #define noiseseed (send + sizeof(NewHopeChaChaState))
#endif #endif
if (!random_seed) { if (!random_seed) {
@ -1037,17 +1048,19 @@ void NewHope::keygen(uint8_t send[NEWHOPE_SENDABYTES], NewHopePrivateKey &sk,
else else
poly_uniform_torref(shake, state.a_ext, send + POLY_BYTES); poly_uniform_torref(shake, state.a_ext, send + POLY_BYTES);
crypto_chacha20_set_key(chacha.input, noiseseed);
#if NEWHOPE_SMALL_FOOTPRINT #if NEWHOPE_SMALL_FOOTPRINT
poly_getnoise(state.pk, noiseseed, 0); poly_getnoise(state.pk, &chacha, 0);
poly_ntt(state.pk); poly_ntt(state.pk);
poly_pointwise(state.pk, state.pk, state.a); poly_pointwise(state.pk, state.pk, state.a);
#else #else
poly_getnoise(sk.coeffs, noiseseed, 0); poly_getnoise(sk.coeffs, &chacha, 0);
poly_ntt(sk.coeffs); poly_ntt(sk.coeffs);
poly_pointwise(state.pk, sk.coeffs, state.a); poly_pointwise(state.pk, sk.coeffs, state.a);
#endif #endif
poly_getnoise(state.a, noiseseed, 1); poly_getnoise(state.a, &chacha, 1);
poly_ntt(state.a); poly_ntt(state.a);
poly_add(state.pk, state.a, state.pk); poly_add(state.pk, state.a, state.pk);
@ -1055,11 +1068,11 @@ void NewHope::keygen(uint8_t send[NEWHOPE_SENDABYTES], NewHopePrivateKey &sk,
poly_tobytes(send, state.pk); poly_tobytes(send, state.pk);
clean(&state, sizeof(state)); clean(&state, sizeof(state));
#if NEWHOPE_SMALL_FOOTPRINT #if !NEWHOPE_BYTE_ALIGNED
#undef noiseseed clean(&chacha, sizeof(chacha));
#else
clean(noiseseed, sizeof(noiseseed));
#endif #endif
#undef noiseseed
#undef chacha
} }
/** /**
@ -1067,6 +1080,8 @@ void NewHope::keygen(uint8_t send[NEWHOPE_SENDABYTES], NewHopePrivateKey &sk,
* *
* \param shared_key The shared secret key. * \param shared_key The shared secret key.
* \param send The public key value for Bob to be sent to Alice. * \param send The public key value for Bob to be sent to Alice.
* This is allowed to be the same pointer as \a received to replace
* the received value from Alice with the new value to send for Bob.
* \param received The public key value that was received from Alice. * \param received The public key value that was received from Alice.
* \param variant The variant of the New Hope algorithm to use, usually Ref. * \param variant The variant of the New Hope algorithm to use, usually Ref.
* \param random_seed Points to 32 bytes of random data to use to generate * \param random_seed Points to 32 bytes of random data to use to generate
@ -1077,6 +1092,11 @@ void NewHope::keygen(uint8_t send[NEWHOPE_SENDABYTES], NewHopePrivateKey &sk,
* and then it can be discarded. Bob can immediately start encrypting * and then it can be discarded. Bob can immediately start encrypting
* session traffic with \a shared_key or some transformed version of it. * session traffic with \a shared_key or some transformed version of it.
* *
* It is assumed that if \a send and \a received overlap, then they are
* the same pointer. The bytes at the end of \a send may be used for
* temporary storage while the leading bytes of \a send / \a received
* are being processed.
*
* \sa shareda(), keygen() * \sa shareda(), keygen()
*/ */
void NewHope::sharedb(uint8_t shared_key[NEWHOPE_SHAREDBYTES], void NewHope::sharedb(uint8_t shared_key[NEWHOPE_SHAREDBYTES],
@ -1101,9 +1121,18 @@ void NewHope::sharedb(uint8_t shared_key[NEWHOPE_SHAREDBYTES],
uint16_t a_ext[84 * 16]; // Value of "a" for torref uniform. uint16_t a_ext[84 * 16]; // Value of "a" for torref uniform.
ALLOC_OBJ(SHAKE128, shake); // SHAKE128 object for poly_uniform(). ALLOC_OBJ(SHAKE128, shake); // SHAKE128 object for poly_uniform().
}; };
ALLOC_OBJ(SHA3_256, sha3); // SHA3 object for hashing the seed. ALLOC_OBJ(SHA3_256, sha3); // SHA3 object for hashing the result.
} state; } state;
unsigned char noiseseed[32];
// Hide the ChaCha state and the noise seed inside "send".
// Put them at the end of the "send" buffer in case "received"
// overlaps with the start of "send".
#if NEWHOPE_BYTE_ALIGNED
#define chacha (*((NewHopeChaChaState *)(send + NEWHOPE_SENDABYTES)))
#else
NewHopeChaChaState chacha;
#endif
#define noiseseed (send + NEWHOPE_SENDABYTES + sizeof(NewHopeChaChaState))
if (!random_seed) if (!random_seed)
RNG.rand(noiseseed, 32); RNG.rand(noiseseed, 32);
@ -1116,12 +1145,14 @@ void NewHope::sharedb(uint8_t shared_key[NEWHOPE_SHAREDBYTES],
else else
poly_uniform_torref(shake, state.a_ext, received + POLY_BYTES); poly_uniform_torref(shake, state.a_ext, received + POLY_BYTES);
poly_getnoise(state.v, noiseseed, 0); crypto_chacha20_set_key(chacha.input, noiseseed);
poly_getnoise(state.v, &chacha, 0);
poly_ntt(state.v); poly_ntt(state.v);
poly_pointwise(state.bp, state.a, state.v); poly_pointwise(state.bp, state.a, state.v);
poly_getnoise(state.a, noiseseed, 1); poly_getnoise(state.a, &chacha, 1);
poly_ntt(state.a); poly_ntt(state.a);
poly_add(state.bp, state.bp, state.a); poly_add(state.bp, state.bp, state.a);
@ -1131,10 +1162,10 @@ void NewHope::sharedb(uint8_t shared_key[NEWHOPE_SHAREDBYTES],
poly_pointwise(state.v, state.a, state.v); poly_pointwise(state.v, state.a, state.v);
poly_invntt(state.v); poly_invntt(state.v);
poly_getnoise(state.a, noiseseed, 2); poly_getnoise(state.a, &chacha, 2);
poly_add(state.v, state.v, state.a); poly_add(state.v, state.v, state.a);
helprec(state.a, state.v, noiseseed, 3); helprec(&chacha, state.a, state.v, 3);
encode_b(send, state.bp, state.a); encode_b(send, state.bp, state.a);
@ -1145,7 +1176,11 @@ void NewHope::sharedb(uint8_t shared_key[NEWHOPE_SHAREDBYTES],
sha3->finalize(shared_key, 32); sha3->finalize(shared_key, 32);
clean(&state, sizeof(state)); clean(&state, sizeof(state));
clean(noiseseed, sizeof(noiseseed)); #if !NEWHOPE_BYTE_ALIGNED
clean(&chacha, sizeof(chacha));
#endif
#undef noiseseed
#undef chacha
} }
/** /**
@ -1173,19 +1208,25 @@ void NewHope::shareda(uint8_t shared_key[NEWHOPE_SHAREDBYTES],
uint16_t v[PARAM_N]; // Value of "v" as a "poly" object. uint16_t v[PARAM_N]; // Value of "v" as a "poly" object.
uint16_t bp[PARAM_N]; // Value of "bp" as a "poly" object. uint16_t bp[PARAM_N]; // Value of "bp" as a "poly" object.
}; };
struct {
uint16_t v_alt[PARAM_N];
ALLOC_OBJ(NewHopeChaChaState, chacha);
};
ALLOC_OBJ(SHA3_256, sha3); // SHA3 object for hashing the result. ALLOC_OBJ(SHA3_256, sha3); // SHA3 object for hashing the result.
} state; } state;
poly_frombytes(state.bp, received);
#if NEWHOPE_SMALL_FOOTPRINT #if NEWHOPE_SMALL_FOOTPRINT
// Re-create the full private key for Alice from the seed. // Re-create the full private key for Alice from the seed.
poly_getnoise(state.v, sk.seed, 0); INIT_OBJ(NewHopeChaChaState, chacha);
crypto_chacha20_set_key(chacha->input, sk.seed);
poly_getnoise(state.v, chacha, 0);
poly_ntt(state.v); poly_ntt(state.v);
poly_frombytes(state.bp, received);
poly_pointwise(state.v, state.v, state.bp); poly_pointwise(state.v, state.v, state.bp);
poly_invntt(state.v); poly_invntt(state.v);
#else #else
// Alice's full private key was supplied. // Alice's full private key was supplied.
poly_frombytes(state.bp, received);
poly_pointwise(state.v, sk.coeffs, state.bp); poly_pointwise(state.v, sk.coeffs, state.bp);
poly_invntt(state.v); poly_invntt(state.v);
#endif #endif