diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index c49eeec0f..216e0c171 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -309,6 +309,8 @@ add_library( hkdf/hkdf.c hpke/hpke.c hrss/hrss.c + kyber/kyber512.c + kyber/kyber768.c lhash/lhash.c mem.c obj/obj.c diff --git a/crypto/kyber/kyber.c b/crypto/kyber/kyber.c new file mode 100644 index 000000000..346d4daec --- /dev/null +++ b/crypto/kyber/kyber.c @@ -0,0 +1,2252 @@ +// Taken from round 3 public domain reference implementation +// +// https://github.com/pq-crystals/kyber +// 8e00ec73035147d18b27d06048dff322f8de1f29 +// +// with some small modifications: +// +// - Merged into one file. +// - Removed 90s version. +// - Seeds are passed as paramters. +// - Changed the API to be more BoringSSL-like +// +// TODO +// +// - Optimizations +// +// The majority of Kyber's time is spent in keccak: generating the matrix +// A, hashing the public key, et cetera. This can be sped up dramatically +// by using a multiway keccak implementation such as f1600x4 on AVX2. +// +// Also the NTT and other operations can be sped up with SIMD. This is +// more complex and the gains are more modest. See the avx2 reference +// implementation or https://github.com/cloudflare/circl/tree/main/pke/kyber +// +// - Option to keep A stored in private key. + +#ifndef KYBER_K +#error "Don't compile this file direcly" +#endif + +#include +#include + +#include +#include +#include + +#if (KYBER_K == 2) +#define KYBER_NAMESPACE(s) KYBER512_##s +#elif (KYBER_K == 3) +#define KYBER_NAMESPACE(s) KYBER768_##s +#elif (KYBER_K == 4) +#define KYBER_NAMESPACE(s) KYBER1024_##s +#else +#error "KYBER_K must be in {2,3,4}" +#endif + +#define public_key KYBER_NAMESPACE(public_key) +#define private_key KYBER_NAMESPACE(private_key) + +#define generate_key KYBER_NAMESPACE(generate_key) +#define encap KYBER_NAMESPACE(encap) +#define decap KYBER_NAMESPACE(decap) +#define marshal_public_key KYBER_NAMESPACE(marshal_public_key) +#define parse_public_key KYBER_NAMESPACE(parse_public_key) + + +// +// params.h +// +#define KYBER_N 256 +#define KYBER_Q 3329 + +#define KYBER_SYMBYTES 32 /* size in bytes of hashes, and seeds */ +#define KYBER_SSBYTES 32 /* size in bytes of shared key */ + +#define KYBER_POLYBYTES 384 +#define KYBER_POLYVECBYTES (KYBER_K * KYBER_POLYBYTES) + +#if KYBER_K == 2 +#define KYBER_ETA1 3 +#define KYBER_POLYCOMPRESSEDBYTES 128 +#define KYBER_POLYVECCOMPRESSEDBYTES (KYBER_K * 320) +#elif KYBER_K == 3 +#define KYBER_ETA1 2 +#define KYBER_POLYCOMPRESSEDBYTES 128 +#define KYBER_POLYVECCOMPRESSEDBYTES (KYBER_K * 320) +#elif KYBER_K == 4 +#define KYBER_ETA1 2 +#define KYBER_POLYCOMPRESSEDBYTES 160 +#define KYBER_POLYVECCOMPRESSEDBYTES (KYBER_K * 352) +#endif + +#define KYBER_ETA2 2 + +#define KYBER_INDCPA_MSGBYTES (KYBER_SYMBYTES) +#define KYBER_INDCPA_PUBLICKEYBYTES (KYBER_POLYVECBYTES + KYBER_SYMBYTES) +#define KYBER_INDCPA_SECRETKEYBYTES (KYBER_POLYVECBYTES) +#define KYBER_INDCPA_BYTES (KYBER_POLYVECCOMPRESSEDBYTES + KYBER_POLYCOMPRESSEDBYTES) + +#define KYBER_PUBLICKEYBYTES (KYBER_INDCPA_PUBLICKEYBYTES) +/* 32 bytes of additional space to save H(pk) */ +#define KYBER_SECRETKEYBYTES (KYBER_INDCPA_SECRETKEYBYTES + KYBER_INDCPA_PUBLICKEYBYTES + 2*KYBER_SYMBYTES) +#define KYBER_CIPHERTEXTBYTES (KYBER_INDCPA_BYTES) + +// +// verify.h +// +static int verify(const uint8_t *a, const uint8_t *b, size_t len); +static void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b); + +// +// reduce.h +// +#define MONT -1044 // 2^16 mod q +#define QINV -3327 // q^-1 mod 2^16 + +static int16_t montgomery_reduce(int32_t a); +static int16_t barrett_reduce(int16_t a); + +// +// ntt.h +// +static void ntt(int16_t poly[256]); +static void invntt(int16_t poly[256]); +static void basemul(int16_t r[2], const int16_t a[2], const int16_t b[2], int16_t zeta); + +// +// poly.h +// + +/* + * Elements of R_q = Z_q[X]/(X^n + 1). Represents polynomial + * coeffs[0] + X*coeffs[1] + X^2*xoeffs[2] + ... + X^{n-1}*coeffs[n-1] + */ +typedef struct{ + int16_t coeffs[KYBER_N]; +} poly; + +static void poly_compress(uint8_t r[KYBER_POLYCOMPRESSEDBYTES], const poly *a); +static void poly_decompress(poly *r, const uint8_t a[KYBER_POLYCOMPRESSEDBYTES]); + +static void poly_tobytes(uint8_t r[KYBER_POLYBYTES], const poly *a); +static void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES]); + +static void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES]); +static void poly_tomsg(uint8_t msg[KYBER_INDCPA_MSGBYTES], const poly *r); + +static void poly_getnoise_eta1(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce); +static void poly_getnoise_eta2(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce); + +static void poly_ntt(poly *r); +static void poly_invntt_tomont(poly *r); +static void poly_basemul_montgomery(poly *r, const poly *a, const poly *b); +static void poly_tomont(poly *r); + +static void poly_reduce(poly *r); + +static void poly_add(poly *r, const poly *a, const poly *b); +static void poly_sub(poly *r, const poly *a, const poly *b); + +// +// cbd.h +// +static void poly_cbd_eta1(poly *r, const uint8_t buf[KYBER_ETA1*KYBER_N/4]); +static void poly_cbd_eta2(poly *r, const uint8_t buf[KYBER_ETA2*KYBER_N/4]); + +// +// polyvec.h +// + +typedef struct{ + poly vec[KYBER_K]; +} polyvec; + +static void polyvec_compress(uint8_t r[KYBER_POLYVECCOMPRESSEDBYTES], const polyvec *a); +static void polyvec_decompress(polyvec *r, const uint8_t a[KYBER_POLYVECCOMPRESSEDBYTES]); + +static void polyvec_tobytes(uint8_t r[KYBER_POLYVECBYTES], const polyvec *a); +static void polyvec_frombytes(polyvec *r, const uint8_t a[KYBER_POLYVECBYTES]); + +static void polyvec_ntt(polyvec *r); +static void polyvec_invntt_tomont(polyvec *r); + +static void polyvec_basemul_acc_montgomery(poly *r, const polyvec *a, const polyvec *b); + +static void polyvec_reduce(polyvec *r); + +static void polyvec_add(polyvec *r, const polyvec *a, const polyvec *b); + +// +// indcpa.h +// + +static void gen_matrix(polyvec *a, const uint8_t seed[KYBER_SYMBYTES], int transposed); +static void indcpa_keypair(uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], + uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES], + const uint8_t seed[KYBER_SYMBYTES]); + +static void indcpa_enc(uint8_t c[KYBER_INDCPA_BYTES], + const uint8_t m[KYBER_INDCPA_MSGBYTES], + const uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES], + const uint8_t coins[KYBER_SYMBYTES]); + +static void indcpa_dec(uint8_t m[KYBER_INDCPA_MSGBYTES], + const uint8_t c[KYBER_INDCPA_BYTES], + const uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES]); + +// +// fips202.h +// + +#define SHAKE128_RATE 168 +#define SHAKE256_RATE 136 +#define SHA3_256_RATE 136 +#define SHA3_512_RATE 72 + +typedef struct { + uint64_t s[25]; + unsigned int pos; +} keccak_state; + +static void shake128_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen); +static void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state); + +static void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state); +static void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen); +static void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state); + +static void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen); +static void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen); +static void sha3_512(uint8_t h[64], const uint8_t *in, size_t inlen); + +// +// symmetric.h +// + +typedef keccak_state xof_state; + +static void kyber_shake128_absorb(keccak_state *s, + const uint8_t seed[KYBER_SYMBYTES], + uint8_t x, + uint8_t y); + +static void kyber_shake256_prf(uint8_t *out, size_t outlen, const uint8_t key[KYBER_SYMBYTES], uint8_t nonce); + +#define XOF_BLOCKBYTES SHAKE128_RATE + +#define hash_h(OUT, IN, INBYTES) sha3_256(OUT, IN, INBYTES) +#define hash_g(OUT, IN, INBYTES) sha3_512(OUT, IN, INBYTES) +#define xof_absorb(STATE, SEED, X, Y) kyber_shake128_absorb(STATE, SEED, X, Y) +#define xof_squeezeblocks(OUT, OUTBLOCKS, STATE) shake128_squeezeblocks(OUT, OUTBLOCKS, STATE) +#define prf(OUT, OUTBYTES, KEY, NONCE) kyber_shake256_prf(OUT, OUTBYTES, KEY, NONCE) +#define kdf(OUT, IN, INBYTES) shake256(OUT, KYBER_SSBYTES, IN, INBYTES) + + +// +// verify.c +// + +/************************************************* +* Name: verify +* +* Description: Compare two arrays for equality in constant time. +* +* Arguments: const uint8_t *a: pointer to first byte array +* const uint8_t *b: pointer to second byte array +* size_t len: length of the byte arrays +* +* Returns 0 if the byte arrays are equal, 1 otherwise +**************************************************/ +static int verify(const uint8_t *a, const uint8_t *b, size_t len) +{ + size_t i; + uint8_t r = 0; + + for(i=0;i> 63; +} + +/************************************************* +* Name: cmov +* +* Description: Copy len bytes from x to r if b is 1; +* don't modify x if b is 0. Requires b to be in {0,1}; +* assumes two's complement representation of negative integers. +* Runs in constant time. +* +* Arguments: uint8_t *r: pointer to output byte array +* const uint8_t *x: pointer to input byte array +* size_t len: Amount of bytes to be copied +* uint8_t b: Condition bit; has to be in {0,1} +**************************************************/ +static void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b) +{ + size_t i; + + b = -b; + for(i=0;i> 16; + return t; +} + +/************************************************* +* Name: barrett_reduce +* +* Description: Barrett reduction; given a 16-bit integer a, computes +* centered representative congruent to a mod q in {-(q-1)/2,...,(q-1)/2} +* +* Arguments: - int16_t a: input integer to be reduced +* +* Returns: integer in {-(q-1)/2,...,(q-1)/2} congruent to a modulo q. +**************************************************/ +static int16_t barrett_reduce(int16_t a) { + int16_t t; + const int16_t v = ((1<<26) + KYBER_Q/2)/KYBER_Q; + + t = ((int32_t)v*a + (1<<25)) >> 26; + t *= KYBER_Q; + return a - t; +} + +// +// cbd.c +// + +/************************************************* +* Name: load32_littleendian +* +* Description: load 4 bytes into a 32-bit integer +* in little-endian order +* +* Arguments: - const uint8_t *x: pointer to input byte array +* +* Returns 32-bit unsigned integer loaded from x +**************************************************/ +static uint32_t load32_littleendian(const uint8_t x[4]) +{ + uint32_t r; + r = (uint32_t)x[0]; + r |= (uint32_t)x[1] << 8; + r |= (uint32_t)x[2] << 16; + r |= (uint32_t)x[3] << 24; + return r; +} + +/************************************************* +* Name: load24_littleendian +* +* Description: load 3 bytes into a 32-bit integer +* in little-endian order. +* This function is only needed for Kyber-512 +* +* Arguments: - const uint8_t *x: pointer to input byte array +* +* Returns 32-bit unsigned integer loaded from x (most significant byte is zero) +**************************************************/ +#if KYBER_ETA1 == 3 +static uint32_t load24_littleendian(const uint8_t x[3]) +{ + uint32_t r; + r = (uint32_t)x[0]; + r |= (uint32_t)x[1] << 8; + r |= (uint32_t)x[2] << 16; + return r; +} +#endif + + +/************************************************* +* Name: cbd2 +* +* Description: Given an array of uniformly random bytes, compute +* polynomial with coefficients distributed according to +* a centered binomial distribution with parameter eta=2 +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *buf: pointer to input byte array +**************************************************/ +static void cbd2(poly *r, const uint8_t buf[2*KYBER_N/4]) +{ + unsigned int i,j; + uint32_t t,d; + int16_t a,b; + + for(i=0;i>1) & 0x55555555; + + for(j=0;j<8;j++) { + a = (d >> (4*j+0)) & 0x3; + b = (d >> (4*j+2)) & 0x3; + r->coeffs[8*i+j] = a - b; + } + } +} + +/************************************************* +* Name: cbd3 +* +* Description: Given an array of uniformly random bytes, compute +* polynomial with coefficients distributed according to +* a centered binomial distribution with parameter eta=3. +* This function is only needed for Kyber-512 +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *buf: pointer to input byte array +**************************************************/ +#if KYBER_ETA1 == 3 +static void cbd3(poly *r, const uint8_t buf[3*KYBER_N/4]) +{ + unsigned int i,j; + uint32_t t,d; + int16_t a,b; + + for(i=0;i>1) & 0x00249249; + d += (t>>2) & 0x00249249; + + for(j=0;j<4;j++) { + a = (d >> (6*j+0)) & 0x7; + b = (d >> (6*j+3)) & 0x7; + r->coeffs[4*i+j] = a - b; + } + } +} +#endif + +static void poly_cbd_eta1(poly *r, const uint8_t buf[KYBER_ETA1*KYBER_N/4]) +{ +#if KYBER_ETA1 == 2 + cbd2(r, buf); +#elif KYBER_ETA1 == 3 + cbd3(r, buf); +#else +#error "This implementation requires eta1 in {2,3}" +#endif +} + +static void poly_cbd_eta2(poly *r, const uint8_t buf[KYBER_ETA2*KYBER_N/4]) +{ +#if KYBER_ETA2 == 2 + cbd2(r, buf); +#else +#error "This implementation requires eta2 = 2" +#endif +} + +// +// ntt.c +// + +/* Code to generate zetas and zetas_inv used in the number-theoretic transform: + +#define KYBER_ROOT_OF_UNITY 17 + +static const uint8_t tree[128] = { + 0, 64, 32, 96, 16, 80, 48, 112, 8, 72, 40, 104, 24, 88, 56, 120, + 4, 68, 36, 100, 20, 84, 52, 116, 12, 76, 44, 108, 28, 92, 60, 124, + 2, 66, 34, 98, 18, 82, 50, 114, 10, 74, 42, 106, 26, 90, 58, 122, + 6, 70, 38, 102, 22, 86, 54, 118, 14, 78, 46, 110, 30, 94, 62, 126, + 1, 65, 33, 97, 17, 81, 49, 113, 9, 73, 41, 105, 25, 89, 57, 121, + 5, 69, 37, 101, 21, 85, 53, 117, 13, 77, 45, 109, 29, 93, 61, 125, + 3, 67, 35, 99, 19, 83, 51, 115, 11, 75, 43, 107, 27, 91, 59, 123, + 7, 71, 39, 103, 23, 87, 55, 119, 15, 79, 47, 111, 31, 95, 63, 127 +}; + +void init_ntt() { + unsigned int i; + int16_t tmp[128]; + + tmp[0] = MONT; + for(i=1;i<128;i++) + tmp[i] = fqmul(tmp[i-1],MONT*KYBER_ROOT_OF_UNITY % KYBER_Q); + + for(i=0;i<128;i++) { + zetas[i] = tmp[tree[i]]; + if(zetas[i] > KYBER_Q/2) + zetas[i] -= KYBER_Q; + if(zetas[i] < -KYBER_Q/2) + zetas[i] += KYBER_Q; + } +} +*/ + +static const int16_t zetas[128] = { + -1044, -758, -359, -1517, 1493, 1422, 287, 202, + -171, 622, 1577, 182, 962, -1202, -1474, 1468, + 573, -1325, 264, 383, -829, 1458, -1602, -130, + -681, 1017, 732, 608, -1542, 411, -205, -1571, + 1223, 652, -552, 1015, -1293, 1491, -282, -1544, + 516, -8, -320, -666, -1618, -1162, 126, 1469, + -853, -90, -271, 830, 107, -1421, -247, -951, + -398, 961, -1508, -725, 448, -1065, 677, -1275, + -1103, 430, 555, 843, -1251, 871, 1550, 105, + 422, 587, 177, -235, -291, -460, 1574, 1653, + -246, 778, 1159, -147, -777, 1483, -602, 1119, + -1590, 644, -872, 349, 418, 329, -156, -75, + 817, 1097, 603, 610, 1322, -1285, -1465, 384, + -1215, -136, 1218, -1335, -874, 220, -1187, -1659, + -1185, -1530, -1278, 794, -1510, -854, -870, 478, + -108, -308, 996, 991, 958, -1460, 1522, 1628 +}; + +/************************************************* +* Name: fqmul +* +* Description: Multiplication followed by Montgomery reduction +* +* Arguments: - int16_t a: first factor +* - int16_t b: second factor +* +* Returns 16-bit integer congruent to a*b*R^{-1} mod q +**************************************************/ +static int16_t fqmul(int16_t a, int16_t b) { + return montgomery_reduce((int32_t)a*b); +} + +/************************************************* +* Name: ntt +* +* Description: Inplace number-theoretic transform (NTT) in Rq. +* input is in standard order, output is in bitreversed order +* +* Arguments: - int16_t r[256]: pointer to input/output vector of elements of Zq +**************************************************/ +static void ntt(int16_t r[256]) { + unsigned int len, start, j, k; + int16_t t, zeta; + + k = 1; + for(len = 128; len >= 2; len >>= 1) { + for(start = 0; start < 256; start = j + len) { + zeta = zetas[k++]; + for(j = start; j < start + len; j++) { + t = fqmul(zeta, r[j + len]); + r[j + len] = r[j] - t; + r[j] = r[j] + t; + } + } + } +} + +/************************************************* +* Name: invntt_tomont +* +* Description: Inplace inverse number-theoretic transform in Rq and +* multiplication by Montgomery factor 2^16. +* Input is in bitreversed order, output is in standard order +* +* Arguments: - int16_t r[256]: pointer to input/output vector of elements of Zq +**************************************************/ +static void invntt(int16_t r[256]) { + unsigned int start, len, j, k; + int16_t t, zeta; + const int16_t f = 1441; // mont^2/128 + + k = 127; + for(len = 2; len <= 128; len <<= 1) { + for(start = 0; start < 256; start = j + len) { + zeta = zetas[k--]; + for(j = start; j < start + len; j++) { + t = r[j]; + r[j] = barrett_reduce(t + r[j + len]); + r[j + len] = r[j + len] - t; + r[j + len] = fqmul(zeta, r[j + len]); + } + } + } + + for(j = 0; j < 256; j++) + r[j] = fqmul(r[j], f); +} + +/************************************************* +* Name: basemul +* +* Description: Multiplication of polynomials in Zq[X]/(X^2-zeta) +* used for multiplication of elements in Rq in NTT domain +* +* Arguments: - int16_t r[2]: pointer to the output polynomial +* - const int16_t a[2]: pointer to the first factor +* - const int16_t b[2]: pointer to the second factor +* - int16_t zeta: integer defining the reduction polynomial +**************************************************/ +static void basemul(int16_t r[2], const int16_t a[2], const int16_t b[2], int16_t zeta) +{ + r[0] = fqmul(a[1], b[1]); + r[0] = fqmul(r[0], zeta); + r[0] += fqmul(a[0], b[0]); + r[1] = fqmul(a[0], b[1]); + r[1] += fqmul(a[1], b[0]); +} + +// +// poly.c +// + +/************************************************* +* Name: poly_compress +* +* Description: Compression and subsequent serialization of a polynomial +* +* Arguments: - uint8_t *r: pointer to output byte array +* (of length KYBER_POLYCOMPRESSEDBYTES) +* - const poly *a: pointer to input polynomial +**************************************************/ +static void poly_compress(uint8_t r[KYBER_POLYCOMPRESSEDBYTES], const poly *a) +{ + unsigned int i,j; + int16_t u; + uint8_t t[8]; + +#if (KYBER_POLYCOMPRESSEDBYTES == 128) + for(i=0;icoeffs[8*i+j]; + u += (u >> 15) & KYBER_Q; + t[j] = ((((uint16_t)u << 4) + KYBER_Q/2)/KYBER_Q) & 15; + } + + r[0] = t[0] | (t[1] << 4); + r[1] = t[2] | (t[3] << 4); + r[2] = t[4] | (t[5] << 4); + r[3] = t[6] | (t[7] << 4); + r += 4; + } +#elif (KYBER_POLYCOMPRESSEDBYTES == 160) + for(i=0;icoeffs[8*i+j]; + u += (u >> 15) & KYBER_Q; + t[j] = ((((uint32_t)u << 5) + KYBER_Q/2)/KYBER_Q) & 31; + } + + r[0] = (t[0] >> 0) | (t[1] << 5); + r[1] = (t[1] >> 3) | (t[2] << 2) | (t[3] << 7); + r[2] = (t[3] >> 1) | (t[4] << 4); + r[3] = (t[4] >> 4) | (t[5] << 1) | (t[6] << 6); + r[4] = (t[6] >> 2) | (t[7] << 3); + r += 5; + } +#else +#error "KYBER_POLYCOMPRESSEDBYTES needs to be in {128, 160}" +#endif +} + +/************************************************* +* Name: poly_decompress +* +* Description: De-serialization and subsequent decompression of a polynomial; +* approximate inverse of poly_compress +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *a: pointer to input byte array +* (of length KYBER_POLYCOMPRESSEDBYTES bytes) +**************************************************/ +static void poly_decompress(poly *r, const uint8_t a[KYBER_POLYCOMPRESSEDBYTES]) +{ + unsigned int i; + +#if (KYBER_POLYCOMPRESSEDBYTES == 128) + for(i=0;icoeffs[2*i+0] = (((uint16_t)(a[0] & 15)*KYBER_Q) + 8) >> 4; + r->coeffs[2*i+1] = (((uint16_t)(a[0] >> 4)*KYBER_Q) + 8) >> 4; + a += 1; + } +#elif (KYBER_POLYCOMPRESSEDBYTES == 160) + unsigned int j; + uint8_t t[8]; + for(i=0;i> 0); + t[1] = (a[0] >> 5) | (a[1] << 3); + t[2] = (a[1] >> 2); + t[3] = (a[1] >> 7) | (a[2] << 1); + t[4] = (a[2] >> 4) | (a[3] << 4); + t[5] = (a[3] >> 1); + t[6] = (a[3] >> 6) | (a[4] << 2); + t[7] = (a[4] >> 3); + a += 5; + + for(j=0;j<8;j++) + r->coeffs[8*i+j] = ((uint32_t)(t[j] & 31)*KYBER_Q + 16) >> 5; + } +#else +#error "KYBER_POLYCOMPRESSEDBYTES needs to be in {128, 160}" +#endif +} + +/************************************************* +* Name: poly_tobytes +* +* Description: Serialization of a polynomial +* +* Arguments: - uint8_t *r: pointer to output byte array +* (needs space for KYBER_POLYBYTES bytes) +* - const poly *a: pointer to input polynomial +**************************************************/ +static void poly_tobytes(uint8_t r[KYBER_POLYBYTES], const poly *a) +{ + unsigned int i; + uint16_t t0, t1; + + for(i=0;icoeffs[2*i]; + t0 += ((int16_t)t0 >> 15) & KYBER_Q; + t1 = a->coeffs[2*i+1]; + t1 += ((int16_t)t1 >> 15) & KYBER_Q; + r[3*i+0] = (t0 >> 0); + r[3*i+1] = (t0 >> 8) | (t1 << 4); + r[3*i+2] = (t1 >> 4); + } +} + +/************************************************* +* Name: poly_frombytes +* +* Description: De-serialization of a polynomial; +* inverse of poly_tobytes +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *a: pointer to input byte array +* (of KYBER_POLYBYTES bytes) +**************************************************/ +static void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES]) +{ + unsigned int i; + for(i=0;icoeffs[2*i] = ((a[3*i+0] >> 0) | ((uint16_t)a[3*i+1] << 8)) & 0xFFF; + r->coeffs[2*i+1] = ((a[3*i+1] >> 4) | ((uint16_t)a[3*i+2] << 4)) & 0xFFF; + } +} + +/************************************************* +* Name: poly_frommsg +* +* Description: Convert 32-byte message to polynomial +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *msg: pointer to input message +**************************************************/ +static void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES]) +{ + unsigned int i,j; + int16_t mask; + +#if (KYBER_INDCPA_MSGBYTES != KYBER_N/8) +#error "KYBER_INDCPA_MSGBYTES must be equal to KYBER_N/8 bytes!" +#endif + + for(i=0;i> j)&1); + r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2); + } + } +} + +/************************************************* +* Name: poly_tomsg +* +* Description: Convert polynomial to 32-byte message +* +* Arguments: - uint8_t *msg: pointer to output message +* - const poly *a: pointer to input polynomial +**************************************************/ +static void poly_tomsg(uint8_t msg[KYBER_INDCPA_MSGBYTES], const poly *a) +{ + unsigned int i,j; + uint16_t t; + + for(i=0;icoeffs[8*i+j]; + t += ((int16_t)t >> 15) & KYBER_Q; + t = (((t << 1) + KYBER_Q/2)/KYBER_Q) & 1; + msg[i] |= t << j; + } + } +} + +/************************************************* +* Name: poly_getnoise_eta1 +* +* Description: Sample a polynomial deterministically from a seed and a nonce, +* with output polynomial close to centered binomial distribution +* with parameter KYBER_ETA1 +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *seed: pointer to input seed +* (of length KYBER_SYMBYTES bytes) +* - uint8_t nonce: one-byte input nonce +**************************************************/ +static void poly_getnoise_eta1(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce) +{ + uint8_t buf[KYBER_ETA1*KYBER_N/4]; + prf(buf, sizeof(buf), seed, nonce); + poly_cbd_eta1(r, buf); +} + +/************************************************* +* Name: poly_getnoise_eta2 +* +* Description: Sample a polynomial deterministically from a seed and a nonce, +* with output polynomial close to centered binomial distribution +* with parameter KYBER_ETA2 +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *seed: pointer to input seed +* (of length KYBER_SYMBYTES bytes) +* - uint8_t nonce: one-byte input nonce +**************************************************/ +static void poly_getnoise_eta2(poly *r, const uint8_t seed[KYBER_SYMBYTES], uint8_t nonce) +{ + uint8_t buf[KYBER_ETA2*KYBER_N/4]; + prf(buf, sizeof(buf), seed, nonce); + poly_cbd_eta2(r, buf); +} + + +/************************************************* +* Name: poly_ntt +* +* Description: Computes negacyclic number-theoretic transform (NTT) of +* a polynomial in place; +* inputs assumed to be in normal order, output in bitreversed order +* +* Arguments: - uint16_t *r: pointer to in/output polynomial +**************************************************/ +static void poly_ntt(poly *r) +{ + ntt(r->coeffs); + poly_reduce(r); +} + +/************************************************* +* Name: poly_invntt_tomont +* +* Description: Computes inverse of negacyclic number-theoretic transform (NTT) +* of a polynomial in place; +* inputs assumed to be in bitreversed order, output in normal order +* +* Arguments: - uint16_t *a: pointer to in/output polynomial +**************************************************/ +static void poly_invntt_tomont(poly *r) +{ + invntt(r->coeffs); +} + +/************************************************* +* Name: poly_basemul_montgomery +* +* Description: Multiplication of two polynomials in NTT domain +* +* Arguments: - poly *r: pointer to output polynomial +* - const poly *a: pointer to first input polynomial +* - const poly *b: pointer to second input polynomial +**************************************************/ +static void poly_basemul_montgomery(poly *r, const poly *a, const poly *b) +{ + unsigned int i; + for(i=0;icoeffs[4*i], &a->coeffs[4*i], &b->coeffs[4*i], zetas[64+i]); + basemul(&r->coeffs[4*i+2], &a->coeffs[4*i+2], &b->coeffs[4*i+2], -zetas[64+i]); + } +} + +/************************************************* +* Name: poly_tomont +* +* Description: Inplace conversion of all coefficients of a polynomial +* from normal domain to Montgomery domain +* +* Arguments: - poly *r: pointer to input/output polynomial +**************************************************/ +static void poly_tomont(poly *r) +{ + unsigned int i; + const int16_t f = (1ULL << 32) % KYBER_Q; + for(i=0;icoeffs[i] = montgomery_reduce((int32_t)r->coeffs[i]*f); +} + +/************************************************* +* Name: poly_reduce +* +* Description: Applies Barrett reduction to all coefficients of a polynomial +* for details of the Barrett reduction see comments in reduce.c +* +* Arguments: - poly *r: pointer to input/output polynomial +**************************************************/ +static void poly_reduce(poly *r) +{ + unsigned int i; + for(i=0;icoeffs[i] = barrett_reduce(r->coeffs[i]); +} + +/************************************************* +* Name: poly_add +* +* Description: Add two polynomials; no modular reduction is performed +* +* Arguments: - poly *r: pointer to output polynomial +* - const poly *a: pointer to first input polynomial +* - const poly *b: pointer to second input polynomial +**************************************************/ +static void poly_add(poly *r, const poly *a, const poly *b) +{ + unsigned int i; + for(i=0;icoeffs[i] = a->coeffs[i] + b->coeffs[i]; +} + +/************************************************* +* Name: poly_sub +* +* Description: Subtract two polynomials; no modular reduction is performed +* +* Arguments: - poly *r: pointer to output polynomial +* - const poly *a: pointer to first input polynomial +* - const poly *b: pointer to second input polynomial +**************************************************/ +static void poly_sub(poly *r, const poly *a, const poly *b) +{ + unsigned int i; + for(i=0;icoeffs[i] = a->coeffs[i] - b->coeffs[i]; +} + +// +// polyvec.c +// + +/************************************************* +* Name: polyvec_compress +* +* Description: Compress and serialize vector of polynomials +* +* Arguments: - uint8_t *r: pointer to output byte array +* (needs space for KYBER_POLYVECCOMPRESSEDBYTES) +* - const polyvec *a: pointer to input vector of polynomials +**************************************************/ +static void polyvec_compress(uint8_t r[KYBER_POLYVECCOMPRESSEDBYTES], const polyvec *a) +{ + unsigned int i,j,k; + +#if (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 352)) + uint16_t t[8]; + for(i=0;ivec[i].coeffs[8*j+k]; + t[k] += ((int16_t)t[k] >> 15) & KYBER_Q; + t[k] = ((((uint32_t)t[k] << 11) + KYBER_Q/2)/KYBER_Q) & 0x7ff; + } + + r[ 0] = (t[0] >> 0); + r[ 1] = (t[0] >> 8) | (t[1] << 3); + r[ 2] = (t[1] >> 5) | (t[2] << 6); + r[ 3] = (t[2] >> 2); + r[ 4] = (t[2] >> 10) | (t[3] << 1); + r[ 5] = (t[3] >> 7) | (t[4] << 4); + r[ 6] = (t[4] >> 4) | (t[5] << 7); + r[ 7] = (t[5] >> 1); + r[ 8] = (t[5] >> 9) | (t[6] << 2); + r[ 9] = (t[6] >> 6) | (t[7] << 5); + r[10] = (t[7] >> 3); + r += 11; + } + } +#elif (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 320)) + uint16_t t[4]; + for(i=0;ivec[i].coeffs[4*j+k]; + t[k] += ((int16_t)t[k] >> 15) & KYBER_Q; + t[k] = ((((uint32_t)t[k] << 10) + KYBER_Q/2)/ KYBER_Q) & 0x3ff; + } + + r[0] = (t[0] >> 0); + r[1] = (t[0] >> 8) | (t[1] << 2); + r[2] = (t[1] >> 6) | (t[2] << 4); + r[3] = (t[2] >> 4) | (t[3] << 6); + r[4] = (t[3] >> 2); + r += 5; + } + } +#else +#error "KYBER_POLYVECCOMPRESSEDBYTES needs to be in {320*KYBER_K, 352*KYBER_K}" +#endif +} + +/************************************************* +* Name: polyvec_decompress +* +* Description: De-serialize and decompress vector of polynomials; +* approximate inverse of polyvec_compress +* +* Arguments: - polyvec *r: pointer to output vector of polynomials +* - const uint8_t *a: pointer to input byte array +* (of length KYBER_POLYVECCOMPRESSEDBYTES) +**************************************************/ +static void polyvec_decompress(polyvec *r, const uint8_t a[KYBER_POLYVECCOMPRESSEDBYTES]) +{ + unsigned int i,j,k; + +#if (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 352)) + uint16_t t[8]; + for(i=0;i> 0) | ((uint16_t)a[ 1] << 8); + t[1] = (a[1] >> 3) | ((uint16_t)a[ 2] << 5); + t[2] = (a[2] >> 6) | ((uint16_t)a[ 3] << 2) | ((uint16_t)a[4] << 10); + t[3] = (a[4] >> 1) | ((uint16_t)a[ 5] << 7); + t[4] = (a[5] >> 4) | ((uint16_t)a[ 6] << 4); + t[5] = (a[6] >> 7) | ((uint16_t)a[ 7] << 1) | ((uint16_t)a[8] << 9); + t[6] = (a[8] >> 2) | ((uint16_t)a[ 9] << 6); + t[7] = (a[9] >> 5) | ((uint16_t)a[10] << 3); + a += 11; + + for(k=0;k<8;k++) + r->vec[i].coeffs[8*j+k] = ((uint32_t)(t[k] & 0x7FF)*KYBER_Q + 1024) >> 11; + } + } +#elif (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 320)) + uint16_t t[4]; + for(i=0;i> 0) | ((uint16_t)a[1] << 8); + t[1] = (a[1] >> 2) | ((uint16_t)a[2] << 6); + t[2] = (a[2] >> 4) | ((uint16_t)a[3] << 4); + t[3] = (a[3] >> 6) | ((uint16_t)a[4] << 2); + a += 5; + + for(k=0;k<4;k++) + r->vec[i].coeffs[4*j+k] = ((uint32_t)(t[k] & 0x3FF)*KYBER_Q + 512) >> 10; + } + } +#else +#error "KYBER_POLYVECCOMPRESSEDBYTES needs to be in {320*KYBER_K, 352*KYBER_K}" +#endif +} + +/************************************************* +* Name: polyvec_tobytes +* +* Description: Serialize vector of polynomials +* +* Arguments: - uint8_t *r: pointer to output byte array +* (needs space for KYBER_POLYVECBYTES) +* - const polyvec *a: pointer to input vector of polynomials +**************************************************/ +static void polyvec_tobytes(uint8_t r[KYBER_POLYVECBYTES], const polyvec *a) +{ + unsigned int i; + for(i=0;ivec[i]); +} + +/************************************************* +* Name: polyvec_frombytes +* +* Description: De-serialize vector of polynomials; +* inverse of polyvec_tobytes +* +* Arguments: - uint8_t *r: pointer to output byte array +* - const polyvec *a: pointer to input vector of polynomials +* (of length KYBER_POLYVECBYTES) +**************************************************/ +static void polyvec_frombytes(polyvec *r, const uint8_t a[KYBER_POLYVECBYTES]) +{ + unsigned int i; + for(i=0;ivec[i], a+i*KYBER_POLYBYTES); +} + +/************************************************* +* Name: polyvec_ntt +* +* Description: Apply forward NTT to all elements of a vector of polynomials +* +* Arguments: - polyvec *r: pointer to in/output vector of polynomials +**************************************************/ +static void polyvec_ntt(polyvec *r) +{ + unsigned int i; + for(i=0;ivec[i]); +} + +/************************************************* +* Name: polyvec_invntt_tomont +* +* Description: Apply inverse NTT to all elements of a vector of polynomials +* and multiply by Montgomery factor 2^16 +* +* Arguments: - polyvec *r: pointer to in/output vector of polynomials +**************************************************/ +static void polyvec_invntt_tomont(polyvec *r) +{ + unsigned int i; + for(i=0;ivec[i]); +} + +/************************************************* +* Name: polyvec_basemul_acc_montgomery +* +* Description: Multiply elements of a and b in NTT domain, accumulate into r, +* and multiply by 2^-16. +* +* Arguments: - poly *r: pointer to output polynomial +* - const polyvec *a: pointer to first input vector of polynomials +* - const polyvec *b: pointer to second input vector of polynomials +**************************************************/ +static void polyvec_basemul_acc_montgomery(poly *r, const polyvec *a, const polyvec *b) +{ + unsigned int i; + poly t; + + poly_basemul_montgomery(r, &a->vec[0], &b->vec[0]); + for(i=1;ivec[i], &b->vec[i]); + poly_add(r, r, &t); + } + + poly_reduce(r); +} + +/************************************************* +* Name: polyvec_reduce +* +* Description: Applies Barrett reduction to each coefficient +* of each element of a vector of polynomials; +* for details of the Barrett reduction see comments in reduce.c +* +* Arguments: - polyvec *r: pointer to input/output polynomial +**************************************************/ +static void polyvec_reduce(polyvec *r) +{ + unsigned int i; + for(i=0;ivec[i]); +} + +/************************************************* +* Name: polyvec_add +* +* Description: Add vectors of polynomials +* +* Arguments: - polyvec *r: pointer to output vector of polynomials +* - const polyvec *a: pointer to first input vector of polynomials +* - const polyvec *b: pointer to second input vector of polynomials +**************************************************/ +static void polyvec_add(polyvec *r, const polyvec *a, const polyvec *b) +{ + unsigned int i; + for(i=0;ivec[i], &a->vec[i], &b->vec[i]); +} + +// +// indcpa.c +// + +/************************************************* +* Name: pack_pk +* +* Description: Serialize the public key as concatenation of the +* serialized vector of polynomials pk +* and the public seed used to generate the matrix A. +* +* Arguments: uint8_t *r: pointer to the output serialized public key +* polyvec *pk: pointer to the input public-key polyvec +* const uint8_t *seed: pointer to the input public seed +**************************************************/ +static void pack_pk(uint8_t r[KYBER_INDCPA_PUBLICKEYBYTES], + polyvec *pk, + const uint8_t seed[KYBER_SYMBYTES]) +{ + size_t i; + polyvec_tobytes(r, pk); + for(i=0;i> 0) | ((uint16_t)buf[pos+1] << 8)) & 0xFFF; + val1 = ((buf[pos+1] >> 4) | ((uint16_t)buf[pos+2] << 4)) & 0xFFF; + pos += 3; + + if(val0 < KYBER_Q) + r[ctr++] = val0; + if(ctr < len && val1 < KYBER_Q) + r[ctr++] = val1; + } + + return ctr; +} + +#define gen_a(A,B) gen_matrix(A,B,0) +#define gen_at(A,B) gen_matrix(A,B,1) + +/************************************************* +* Name: gen_matrix +* +* Description: Deterministically generate matrix A (or the transpose of A) +* from a seed. Entries of the matrix are polynomials that look +* uniformly random. Performs rejection sampling on output of +* a XOF +* +* Arguments: - polyvec *a: pointer to ouptput matrix A +* - const uint8_t *seed: pointer to input seed +* - int transposed: boolean deciding whether A or A^T is generated +**************************************************/ +#define GEN_MATRIX_NBLOCKS ((12*KYBER_N/8*(1 << 12)/KYBER_Q + XOF_BLOCKBYTES)/XOF_BLOCKBYTES) +// Not static for benchmarking +static void gen_matrix(polyvec *a, const uint8_t seed[KYBER_SYMBYTES], int transposed) +{ + unsigned int ctr, i, j, k; + unsigned int buflen, off; + uint8_t buf[GEN_MATRIX_NBLOCKS*XOF_BLOCKBYTES+2]; + xof_state state; + + for(i=0;i> (64-offset))) + +/************************************************* +* Name: load64 +* +* Description: Load 8 bytes into uint64_t in little-endian order +* +* Arguments: - const uint8_t *x: pointer to input byte array +* +* Returns the loaded 64-bit unsigned integer +**************************************************/ +static uint64_t load64(const uint8_t x[8]) { + unsigned int i; + uint64_t r = 0; + + for(i=0;i<8;i++) + r |= (uint64_t)x[i] << 8*i; + + return r; +} + +/************************************************* +* Name: store64 +* +* Description: Store a 64-bit integer to array of 8 bytes in little-endian order +* +* Arguments: - uint8_t *x: pointer to the output byte array (allocated) +* - uint64_t u: input 64-bit unsigned integer +**************************************************/ +static void store64(uint8_t x[8], uint64_t u) { + unsigned int i; + + for(i=0;i<8;i++) + x[i] = u >> 8*i; +} + +/* Keccak round constants */ +static const uint64_t KeccakF_RoundConstants[NROUNDS] = { + (uint64_t)0x0000000000000001ULL, + (uint64_t)0x0000000000008082ULL, + (uint64_t)0x800000000000808aULL, + (uint64_t)0x8000000080008000ULL, + (uint64_t)0x000000000000808bULL, + (uint64_t)0x0000000080000001ULL, + (uint64_t)0x8000000080008081ULL, + (uint64_t)0x8000000000008009ULL, + (uint64_t)0x000000000000008aULL, + (uint64_t)0x0000000000000088ULL, + (uint64_t)0x0000000080008009ULL, + (uint64_t)0x000000008000000aULL, + (uint64_t)0x000000008000808bULL, + (uint64_t)0x800000000000008bULL, + (uint64_t)0x8000000000008089ULL, + (uint64_t)0x8000000000008003ULL, + (uint64_t)0x8000000000008002ULL, + (uint64_t)0x8000000000000080ULL, + (uint64_t)0x000000000000800aULL, + (uint64_t)0x800000008000000aULL, + (uint64_t)0x8000000080008081ULL, + (uint64_t)0x8000000000008080ULL, + (uint64_t)0x0000000080000001ULL, + (uint64_t)0x8000000080008008ULL +}; + +/************************************************* +* Name: KeccakF1600_StatePermute +* +* Description: The Keccak F1600 Permutation +* +* Arguments: - uint64_t *state: pointer to input/output Keccak state +**************************************************/ +static void KeccakF1600_StatePermute(uint64_t state[25]) +{ + int round; + + uint64_t Aba, Abe, Abi, Abo, Abu; + uint64_t Aga, Age, Agi, Ago, Agu; + uint64_t Aka, Ake, Aki, Ako, Aku; + uint64_t Ama, Ame, Ami, Amo, Amu; + uint64_t Asa, Ase, Asi, Aso, Asu; + uint64_t BCa, BCe, BCi, BCo, BCu; + uint64_t Da, De, Di, Do, Du; + uint64_t Eba, Ebe, Ebi, Ebo, Ebu; + uint64_t Ega, Ege, Egi, Ego, Egu; + uint64_t Eka, Eke, Eki, Eko, Eku; + uint64_t Ema, Eme, Emi, Emo, Emu; + uint64_t Esa, Ese, Esi, Eso, Esu; + + //copyFromState(A, state) + Aba = state[ 0]; + Abe = state[ 1]; + Abi = state[ 2]; + Abo = state[ 3]; + Abu = state[ 4]; + Aga = state[ 5]; + Age = state[ 6]; + Agi = state[ 7]; + Ago = state[ 8]; + Agu = state[ 9]; + Aka = state[10]; + Ake = state[11]; + Aki = state[12]; + Ako = state[13]; + Aku = state[14]; + Ama = state[15]; + Ame = state[16]; + Ami = state[17]; + Amo = state[18]; + Amu = state[19]; + Asa = state[20]; + Ase = state[21]; + Asi = state[22]; + Aso = state[23]; + Asu = state[24]; + + for(round = 0; round < NROUNDS; round += 2) { + // prepareTheta + BCa = Aba^Aga^Aka^Ama^Asa; + BCe = Abe^Age^Ake^Ame^Ase; + BCi = Abi^Agi^Aki^Ami^Asi; + BCo = Abo^Ago^Ako^Amo^Aso; + BCu = Abu^Agu^Aku^Amu^Asu; + + //thetaRhoPiChiIotaPrepareTheta(round, A, E) + Da = BCu^ROL(BCe, 1); + De = BCa^ROL(BCi, 1); + Di = BCe^ROL(BCo, 1); + Do = BCi^ROL(BCu, 1); + Du = BCo^ROL(BCa, 1); + + Aba ^= Da; + BCa = Aba; + Age ^= De; + BCe = ROL(Age, 44); + Aki ^= Di; + BCi = ROL(Aki, 43); + Amo ^= Do; + BCo = ROL(Amo, 21); + Asu ^= Du; + BCu = ROL(Asu, 14); + Eba = BCa ^((~BCe)& BCi ); + Eba ^= (uint64_t)KeccakF_RoundConstants[round]; + Ebe = BCe ^((~BCi)& BCo ); + Ebi = BCi ^((~BCo)& BCu ); + Ebo = BCo ^((~BCu)& BCa ); + Ebu = BCu ^((~BCa)& BCe ); + + Abo ^= Do; + BCa = ROL(Abo, 28); + Agu ^= Du; + BCe = ROL(Agu, 20); + Aka ^= Da; + BCi = ROL(Aka, 3); + Ame ^= De; + BCo = ROL(Ame, 45); + Asi ^= Di; + BCu = ROL(Asi, 61); + Ega = BCa ^((~BCe)& BCi ); + Ege = BCe ^((~BCi)& BCo ); + Egi = BCi ^((~BCo)& BCu ); + Ego = BCo ^((~BCu)& BCa ); + Egu = BCu ^((~BCa)& BCe ); + + Abe ^= De; + BCa = ROL(Abe, 1); + Agi ^= Di; + BCe = ROL(Agi, 6); + Ako ^= Do; + BCi = ROL(Ako, 25); + Amu ^= Du; + BCo = ROL(Amu, 8); + Asa ^= Da; + BCu = ROL(Asa, 18); + Eka = BCa ^((~BCe)& BCi ); + Eke = BCe ^((~BCi)& BCo ); + Eki = BCi ^((~BCo)& BCu ); + Eko = BCo ^((~BCu)& BCa ); + Eku = BCu ^((~BCa)& BCe ); + + Abu ^= Du; + BCa = ROL(Abu, 27); + Aga ^= Da; + BCe = ROL(Aga, 36); + Ake ^= De; + BCi = ROL(Ake, 10); + Ami ^= Di; + BCo = ROL(Ami, 15); + Aso ^= Do; + BCu = ROL(Aso, 56); + Ema = BCa ^((~BCe)& BCi ); + Eme = BCe ^((~BCi)& BCo ); + Emi = BCi ^((~BCo)& BCu ); + Emo = BCo ^((~BCu)& BCa ); + Emu = BCu ^((~BCa)& BCe ); + + Abi ^= Di; + BCa = ROL(Abi, 62); + Ago ^= Do; + BCe = ROL(Ago, 55); + Aku ^= Du; + BCi = ROL(Aku, 39); + Ama ^= Da; + BCo = ROL(Ama, 41); + Ase ^= De; + BCu = ROL(Ase, 2); + Esa = BCa ^((~BCe)& BCi ); + Ese = BCe ^((~BCi)& BCo ); + Esi = BCi ^((~BCo)& BCu ); + Eso = BCo ^((~BCu)& BCa ); + Esu = BCu ^((~BCa)& BCe ); + + // prepareTheta + BCa = Eba^Ega^Eka^Ema^Esa; + BCe = Ebe^Ege^Eke^Eme^Ese; + BCi = Ebi^Egi^Eki^Emi^Esi; + BCo = Ebo^Ego^Eko^Emo^Eso; + BCu = Ebu^Egu^Eku^Emu^Esu; + + //thetaRhoPiChiIotaPrepareTheta(round+1, E, A) + Da = BCu^ROL(BCe, 1); + De = BCa^ROL(BCi, 1); + Di = BCe^ROL(BCo, 1); + Do = BCi^ROL(BCu, 1); + Du = BCo^ROL(BCa, 1); + + Eba ^= Da; + BCa = Eba; + Ege ^= De; + BCe = ROL(Ege, 44); + Eki ^= Di; + BCi = ROL(Eki, 43); + Emo ^= Do; + BCo = ROL(Emo, 21); + Esu ^= Du; + BCu = ROL(Esu, 14); + Aba = BCa ^((~BCe)& BCi ); + Aba ^= (uint64_t)KeccakF_RoundConstants[round+1]; + Abe = BCe ^((~BCi)& BCo ); + Abi = BCi ^((~BCo)& BCu ); + Abo = BCo ^((~BCu)& BCa ); + Abu = BCu ^((~BCa)& BCe ); + + Ebo ^= Do; + BCa = ROL(Ebo, 28); + Egu ^= Du; + BCe = ROL(Egu, 20); + Eka ^= Da; + BCi = ROL(Eka, 3); + Eme ^= De; + BCo = ROL(Eme, 45); + Esi ^= Di; + BCu = ROL(Esi, 61); + Aga = BCa ^((~BCe)& BCi ); + Age = BCe ^((~BCi)& BCo ); + Agi = BCi ^((~BCo)& BCu ); + Ago = BCo ^((~BCu)& BCa ); + Agu = BCu ^((~BCa)& BCe ); + + Ebe ^= De; + BCa = ROL(Ebe, 1); + Egi ^= Di; + BCe = ROL(Egi, 6); + Eko ^= Do; + BCi = ROL(Eko, 25); + Emu ^= Du; + BCo = ROL(Emu, 8); + Esa ^= Da; + BCu = ROL(Esa, 18); + Aka = BCa ^((~BCe)& BCi ); + Ake = BCe ^((~BCi)& BCo ); + Aki = BCi ^((~BCo)& BCu ); + Ako = BCo ^((~BCu)& BCa ); + Aku = BCu ^((~BCa)& BCe ); + + Ebu ^= Du; + BCa = ROL(Ebu, 27); + Ega ^= Da; + BCe = ROL(Ega, 36); + Eke ^= De; + BCi = ROL(Eke, 10); + Emi ^= Di; + BCo = ROL(Emi, 15); + Eso ^= Do; + BCu = ROL(Eso, 56); + Ama = BCa ^((~BCe)& BCi ); + Ame = BCe ^((~BCi)& BCo ); + Ami = BCi ^((~BCo)& BCu ); + Amo = BCo ^((~BCu)& BCa ); + Amu = BCu ^((~BCa)& BCe ); + + Ebi ^= Di; + BCa = ROL(Ebi, 62); + Ego ^= Do; + BCe = ROL(Ego, 55); + Eku ^= Du; + BCi = ROL(Eku, 39); + Ema ^= Da; + BCo = ROL(Ema, 41); + Ese ^= De; + BCu = ROL(Ese, 2); + Asa = BCa ^((~BCe)& BCi ); + Ase = BCe ^((~BCi)& BCo ); + Asi = BCi ^((~BCo)& BCu ); + Aso = BCo ^((~BCu)& BCa ); + Asu = BCu ^((~BCa)& BCe ); + } + + //copyToState(state, A) + state[ 0] = Aba; + state[ 1] = Abe; + state[ 2] = Abi; + state[ 3] = Abo; + state[ 4] = Abu; + state[ 5] = Aga; + state[ 6] = Age; + state[ 7] = Agi; + state[ 8] = Ago; + state[ 9] = Agu; + state[10] = Aka; + state[11] = Ake; + state[12] = Aki; + state[13] = Ako; + state[14] = Aku; + state[15] = Ama; + state[16] = Ame; + state[17] = Ami; + state[18] = Amo; + state[19] = Amu; + state[20] = Asa; + state[21] = Ase; + state[22] = Asi; + state[23] = Aso; + state[24] = Asu; +} + + +/************************************************* +* Name: keccak_squeeze +* +* Description: Squeeze step of Keccak. Squeezes arbitratrily many bytes. +* Modifies the state. Can be called multiple times to keep +* squeezing, i.e., is incremental. +* +* Arguments: - uint8_t *out: pointer to output +* - size_t outlen: number of bytes to be squeezed (written to out) +* - uint64_t *s: pointer to input/output Keccak state +* - unsigned int pos: number of bytes in current block already squeezed +* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) +* +* Returns new position pos in current block +**************************************************/ +static unsigned int keccak_squeeze(uint8_t *out, + size_t outlen, + uint64_t s[25], + unsigned int pos, + unsigned int r) +{ + unsigned int i; + + while(outlen) { + if(pos == r) { + KeccakF1600_StatePermute(s); + pos = 0; + } + for(i=pos;i < r && i < pos+outlen; i++) + *out++ = s[i/8] >> 8*(i%8); + outlen -= i-pos; + pos = i; + } + + return pos; +} + + +/************************************************* +* Name: keccak_absorb_once +* +* Description: Absorb step of Keccak; +* non-incremental, starts by zeroeing the state. +* +* Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state +* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) +* - const uint8_t *in: pointer to input to be absorbed into s +* - size_t inlen: length of input in bytes +* - uint8_t p: domain-separation byte for different Keccak-derived functions +**************************************************/ +static void keccak_absorb_once(uint64_t s[25], + unsigned int r, + const uint8_t *in, + size_t inlen, + uint8_t p) +{ + unsigned int i; + + for(i=0;i<25;i++) + s[i] = 0; + + while(inlen >= r) { + for(i=0;is, SHAKE128_RATE, in, inlen, 0x1F); + state->pos = SHAKE128_RATE; +} + +/************************************************* +* Name: shake128_squeezeblocks +* +* Description: Squeeze step of SHAKE128 XOF. Squeezes full blocks of +* SHAKE128_RATE bytes each. Can be called multiple times +* to keep squeezing. Assumes new block has not yet been +* started (state->pos = SHAKE128_RATE). +* +* Arguments: - uint8_t *out: pointer to output blocks +* - size_t nblocks: number of blocks to be squeezed (written to output) +* - keccak_state *s: pointer to input/output Keccak state +**************************************************/ +static void shake128_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) +{ + keccak_squeezeblocks(out, nblocks, state->s, SHAKE128_RATE); +} + +/************************************************* +* Name: shake256_squeeze +* +* Description: Squeeze step of SHAKE256 XOF. Squeezes arbitraily many +* bytes. Can be called multiple times to keep squeezing. +* +* Arguments: - uint8_t *out: pointer to output blocks +* - size_t outlen : number of bytes to be squeezed (written to output) +* - keccak_state *s: pointer to input/output Keccak state +**************************************************/ +static void shake256_squeeze(uint8_t *out, size_t outlen, keccak_state *state) +{ + state->pos = keccak_squeeze(out, outlen, state->s, state->pos, SHAKE256_RATE); +} + +/************************************************* +* Name: shake256_absorb_once +* +* Description: Initialize, absorb into and finalize SHAKE256 XOF; non-incremental. +* +* Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak state +* - const uint8_t *in: pointer to input to be absorbed into s +* - size_t inlen: length of input in bytes +**************************************************/ +static void shake256_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen) +{ + keccak_absorb_once(state->s, SHAKE256_RATE, in, inlen, 0x1F); + state->pos = SHAKE256_RATE; +} + +/************************************************* +* Name: shake256_squeezeblocks +* +* Description: Squeeze step of SHAKE256 XOF. Squeezes full blocks of +* SHAKE256_RATE bytes each. Can be called multiple times +* to keep squeezing. Assumes next block has not yet been +* started (state->pos = SHAKE256_RATE). +* +* Arguments: - uint8_t *out: pointer to output blocks +* - size_t nblocks: number of blocks to be squeezed (written to output) +* - keccak_state *s: pointer to input/output Keccak state +**************************************************/ +static void shake256_squeezeblocks(uint8_t *out, size_t nblocks, keccak_state *state) +{ + keccak_squeezeblocks(out, nblocks, state->s, SHAKE256_RATE); +} + +/************************************************* +* Name: shake256 +* +* Description: SHAKE256 XOF with non-incremental API +* +* Arguments: - uint8_t *out: pointer to output +* - size_t outlen: requested output length in bytes +* - const uint8_t *in: pointer to input +* - size_t inlen: length of input in bytes +**************************************************/ +static void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) +{ + size_t nblocks; + keccak_state state; + + shake256_absorb_once(&state, in, inlen); + nblocks = outlen/SHAKE256_RATE; + shake256_squeezeblocks(out, nblocks, &state); + outlen -= nblocks*SHAKE256_RATE; + out += nblocks*SHAKE256_RATE; + shake256_squeeze(out, outlen, &state); +} + +/************************************************* +* Name: sha3_256 +* +* Description: SHA3-256 with non-incremental API +* +* Arguments: - uint8_t *h: pointer to output (32 bytes) +* - const uint8_t *in: pointer to input +* - size_t inlen: length of input in bytes +**************************************************/ +static void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen) +{ + unsigned int i; + uint64_t s[25]; + + keccak_absorb_once(s, SHA3_256_RATE, in, inlen, 0x06); + KeccakF1600_StatePermute(s); + for(i=0;i<4;i++) + store64(h+8*i,s[i]); +} + +/************************************************* +* Name: sha3_512 +* +* Description: SHA3-512 with non-incremental API +* +* Arguments: - uint8_t *h: pointer to output (64 bytes) +* - const uint8_t *in: pointer to input +* - size_t inlen: length of input in bytes +**************************************************/ +static void sha3_512(uint8_t h[64], const uint8_t *in, size_t inlen) +{ + unsigned int i; + uint64_t s[25]; + + keccak_absorb_once(s, SHA3_512_RATE, in, inlen, 0x06); + KeccakF1600_StatePermute(s); + for(i=0;i<8;i++) + store64(h+8*i,s[i]); +} + +// +// symmetric-shake.c +// + +/************************************************* +* Name: kyber_shake128_absorb +* +* Description: Absorb step of the SHAKE128 specialized for the Kyber context. +* +* Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak state +* - const uint8_t *seed: pointer to KYBER_SYMBYTES input to be absorbed into state +* - uint8_t i: additional byte of input +* - uint8_t j: additional byte of input +**************************************************/ +static void kyber_shake128_absorb(keccak_state *state, + const uint8_t seed[KYBER_SYMBYTES], + uint8_t x, + uint8_t y) +{ + uint8_t extseed[KYBER_SYMBYTES+2]; + + memcpy(extseed, seed, KYBER_SYMBYTES); + extseed[KYBER_SYMBYTES+0] = x; + extseed[KYBER_SYMBYTES+1] = y; + + shake128_absorb_once(state, extseed, sizeof(extseed)); +} + +/************************************************* +* Name: kyber_shake256_prf +* +* Description: Usage of SHAKE256 as a PRF, concatenates secret and public input +* and then generates outlen bytes of SHAKE256 output +* +* Arguments: - uint8_t *out: pointer to output +* - size_t outlen: number of requested output bytes +* - const uint8_t *key: pointer to the key (of length KYBER_SYMBYTES) +* - uint8_t nonce: single-byte nonce (public PRF input) +**************************************************/ +static void kyber_shake256_prf(uint8_t *out, size_t outlen, const uint8_t key[KYBER_SYMBYTES], uint8_t nonce) +{ + uint8_t extkey[KYBER_SYMBYTES+1]; + + memcpy(extkey, key, KYBER_SYMBYTES); + extkey[KYBER_SYMBYTES] = nonce; + + shake256(out, outlen, extkey, sizeof(extkey)); +} + +// +// kem.c +// + +// Modified crypto_kem_keypair to BoringSSL style API +void generate_key(struct public_key *out_pub, struct private_key *out_priv, + const uint8_t seed[KYBER_GENERATE_KEY_BYTES]) +{ + size_t i; + uint8_t* pk = &out_pub->opaque[0]; + uint8_t* sk = &out_priv->opaque[0]; + + indcpa_keypair(pk, sk, seed); + for(i=0;iopaque[0]; + uint8_t *ct = out_ciphertext; + + uint8_t buf[2*KYBER_SYMBYTES]; + /* Will contain key, coins */ + uint8_t kr[2*KYBER_SYMBYTES]; + + memcpy(buf, seed, KYBER_SYMBYTES); + /* Don't release system RNG output */ + hash_h(buf, buf, KYBER_SYMBYTES); + + /* Multitarget countermeasure for coins + contributory KEM */ + hash_h(buf+KYBER_SYMBYTES, pk, KYBER_PUBLICKEYBYTES); + hash_g(kr, buf, 2*KYBER_SYMBYTES); + + /* coins are in kr+KYBER_SYMBYTES */ + indcpa_enc(ct, buf, pk, kr+KYBER_SYMBYTES); + + /* overwrite coins in kr with H(c) */ + hash_h(kr+KYBER_SYMBYTES, ct, KYBER_CIPHERTEXTBYTES); + /* hash concatenation of pre-k and H(c) to k */ + kdf(ss, kr, 2*KYBER_SYMBYTES); +} + +// Modified crypto_kem_decap to BoringSSL style API +void decap(uint8_t out_shared_key[KYBER_SSBYTES], + const struct private_key *in_priv, + const uint8_t *ct, size_t ciphertext_len) +{ + uint8_t *ss = out_shared_key; + const uint8_t *sk = &in_priv->opaque[0]; + + size_t i; + int fail = 1; + uint8_t buf[2*KYBER_SYMBYTES]; + /* Will contain key, coins */ + uint8_t kr[2*KYBER_SYMBYTES]; + uint8_t cmp[KYBER_CIPHERTEXTBYTES]; + const uint8_t *pk = sk+KYBER_INDCPA_SECRETKEYBYTES; + + if (ciphertext_len == KYBER_CIPHERTEXTBYTES) { + indcpa_dec(buf, ct, sk); + + /* Multitarget countermeasure for coins + contributory KEM */ + for(i=0;iopaque, KYBER_PUBLICKEYBYTES); +} + +void parse_public_key(struct public_key *out, + const uint8_t in[KYBER_PUBLICKEYBYTES]) { + memcpy(&out->opaque, in, KYBER_PUBLICKEYBYTES); +} diff --git a/crypto/kyber/kyber512.c b/crypto/kyber/kyber512.c new file mode 100644 index 000000000..21eed11a2 --- /dev/null +++ b/crypto/kyber/kyber512.c @@ -0,0 +1,5 @@ +#define KYBER_K 2 + +#include "kyber.c" + + diff --git a/crypto/kyber/kyber768.c b/crypto/kyber/kyber768.c new file mode 100644 index 000000000..3e572b72e --- /dev/null +++ b/crypto/kyber/kyber768.c @@ -0,0 +1,4 @@ +#define KYBER_K 3 + +#include "kyber.c" + diff --git a/crypto/obj/obj_dat.h b/crypto/obj/obj_dat.h index cc185f1bf..2c1d64800 100644 --- a/crypto/obj/obj_dat.h +++ b/crypto/obj/obj_dat.h @@ -57,7 +57,7 @@ /* This file is generated by crypto/obj/objects.go. */ -#define NUM_NID 964 +#define NUM_NID 966 static const uint8_t kObjectData[] = { /* NID_rsadsi */ @@ -8782,6 +8782,10 @@ static const ASN1_OBJECT kObjects[NUM_NID] = { {"X448", "X448", NID_X448, 3, &kObjectData[6184], 0}, {"SHA512-256", "sha512-256", NID_sha512_256, 9, &kObjectData[6187], 0}, {"HKDF", "hkdf", NID_hkdf, 0, NULL, 0}, + {"X25519Kyber512Draft00", "X25519Kyber512Draft00", + NID_X25519Kyber512Draft00, 0, NULL, 0}, + {"X25519Kyber768Draft00", "X25519Kyber768Draft00", + NID_X25519Kyber768Draft00, 0, NULL, 0}, }; static const uint16_t kNIDsInShortNameOrder[] = { @@ -8981,6 +8985,8 @@ static const uint16_t kNIDsInShortNameOrder[] = { 458 /* UID */, 0 /* UNDEF */, 948 /* X25519 */, + 964 /* X25519Kyber512Draft00 */, + 965 /* X25519Kyber768Draft00 */, 961 /* X448 */, 11 /* X500 */, 378 /* X500algorithms */, @@ -9852,6 +9858,8 @@ static const uint16_t kNIDsInLongNameOrder[] = { 133 /* Time Stamping */, 375 /* Trust Root */, 948 /* X25519 */, + 964 /* X25519Kyber512Draft00 */, + 965 /* X25519Kyber768Draft00 */, 961 /* X448 */, 12 /* X509 */, 402 /* X509v3 AC Targeting */, diff --git a/crypto/obj/obj_mac.num b/crypto/obj/obj_mac.num index 6367cc364..db64f60a6 100644 --- a/crypto/obj/obj_mac.num +++ b/crypto/obj/obj_mac.num @@ -952,3 +952,5 @@ ED448 960 X448 961 sha512_256 962 hkdf 963 +X25519Kyber512Draft00 964 +X25519Kyber768Draft00 965 diff --git a/crypto/obj/objects.txt b/crypto/obj/objects.txt index 67b76c773..b5b7b5786 100644 --- a/crypto/obj/objects.txt +++ b/crypto/obj/objects.txt @@ -1335,6 +1335,10 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme # NID for CECPQ2 (no corresponding OID). : CECPQ2 +# NID for Kyber hybrids (no corresponding OID). + : X25519Kyber512Draft00 + : X25519Kyber768Draft00 + # See RFC 8410. 1 3 101 110 : X25519 1 3 101 111 : X448 diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc index 8343fee39..58fdd264c 100644 --- a/crypto/x509/x509_test.cc +++ b/crypto/x509/x509_test.cc @@ -1470,6 +1470,23 @@ TEST(X509Test, TestCRL) { Verify(leaf.get(), {root.get()}, {root.get()}, {algorithm_mismatch_crl2.get()}, X509_V_FLAG_CRL_CHECK)); + // The CRL is valid for a month. + EXPECT_EQ(X509_V_ERR_CRL_HAS_EXPIRED, + Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()}, + X509_V_FLAG_CRL_CHECK, [](X509_VERIFY_PARAM *param) { + X509_VERIFY_PARAM_set_time( + param, kReferenceTime + 2 * 30 * 24 * 3600); + })); + + // X509_V_FLAG_NO_CHECK_TIME suppresses the validity check. + EXPECT_EQ(X509_V_OK, + Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()}, + X509_V_FLAG_CRL_CHECK | X509_V_FLAG_NO_CHECK_TIME, + [](X509_VERIFY_PARAM *param) { + X509_VERIFY_PARAM_set_time( + param, kReferenceTime + 2 * 30 * 24 * 3600); + })); + // Parsing kBadExtensionCRL should fail. EXPECT_FALSE(CRLFromPEM(kBadExtensionCRL)); } @@ -3566,6 +3583,95 @@ TEST(X509Test, TrustedFirst) { })); } +// Test that notBefore and notAfter checks work correctly. +TEST(X509Test, Expiry) { + bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); + ASSERT_TRUE(key); + + // The following are measured in seconds relative to kReferenceTime. The + // validity periods are staggered so we can independently test both leaf and + // root time checks. + const time_t kSecondsInDay = 24 * 3600; + const time_t kRootStart = -30 * kSecondsInDay; + const time_t kIntermediateStart = -20 * kSecondsInDay; + const time_t kLeafStart = -10 * kSecondsInDay; + const time_t kIntermediateEnd = 10 * kSecondsInDay; + const time_t kLeafEnd = 20 * kSecondsInDay; + const time_t kRootEnd = 30 * kSecondsInDay; + + bssl::UniquePtr root = + MakeTestCert("Root", "Root", key.get(), /*is_ca=*/true); + ASSERT_TRUE(root); + ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notBefore(root.get()), kReferenceTime, + /*offset_day=*/0, + /*offset_sec=*/kRootStart)); + ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notAfter(root.get()), kReferenceTime, + /*offset_day=*/0, + /*offset_sec=*/kRootEnd)); + ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256())); + + bssl::UniquePtr intermediate = + MakeTestCert("Root", "Intermediate", key.get(), /*is_ca=*/true); + ASSERT_TRUE(intermediate); + ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notBefore(intermediate.get()), + kReferenceTime, + /*offset_day=*/0, + /*offset_sec=*/kIntermediateStart)); + ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notAfter(intermediate.get()), + kReferenceTime, + /*offset_day=*/0, + /*offset_sec=*/kIntermediateEnd)); + ASSERT_TRUE(X509_sign(intermediate.get(), key.get(), EVP_sha256())); + + bssl::UniquePtr leaf = + MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); + ASSERT_TRUE(leaf); + ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notBefore(leaf.get()), kReferenceTime, + /*offset_day=*/0, + /*offset_sec=*/kLeafStart)); + ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notAfter(leaf.get()), kReferenceTime, + /*offset_day=*/0, + /*offset_sec=*/kLeafEnd)); + ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); + + struct VerifyAt { + time_t time; + void operator()(X509_VERIFY_PARAM *param) const { + X509_VERIFY_PARAM_set_time(param, time); + } + }; + + for (bool check_time : {true, false}) { + SCOPED_TRACE(check_time); + unsigned long flags = check_time ? 0 : X509_V_FLAG_NO_CHECK_TIME; + int not_yet_valid = check_time ? X509_V_ERR_CERT_NOT_YET_VALID : X509_V_OK; + int has_expired = check_time ? X509_V_ERR_CERT_HAS_EXPIRED : X509_V_OK; + + EXPECT_EQ(not_yet_valid, + Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, flags, + VerifyAt{kReferenceTime + kRootStart - 1})); + EXPECT_EQ(not_yet_valid, + Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, flags, + VerifyAt{kReferenceTime + kIntermediateStart - 1})); + EXPECT_EQ(not_yet_valid, + Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, flags, + VerifyAt{kReferenceTime + kLeafStart - 1})); + + EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, + {}, flags, VerifyAt{kReferenceTime})); + + EXPECT_EQ(has_expired, + Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, flags, + VerifyAt{kReferenceTime + kRootEnd + 1})); + EXPECT_EQ(has_expired, + Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, flags, + VerifyAt{kReferenceTime + kIntermediateEnd + 1})); + EXPECT_EQ(has_expired, + Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, flags, + VerifyAt{kReferenceTime + kLeafEnd + 1})); + } +} + // kConstructedBitString is an X.509 certificate where the signature is encoded // as a BER constructed BIT STRING. Note that, while OpenSSL's parser accepts // this input, it interprets the value incorrectly. diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index f6089cd85..eca2d1eb4 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -971,18 +971,21 @@ static int check_cert(X509_STORE_CTX *ctx) { // Check CRL times against values in X509_STORE_CTX static int check_crl_time(X509_STORE_CTX *ctx, X509_CRL *crl, int notify) { - time_t *ptime; - int i; + if (ctx->param->flags & X509_V_FLAG_NO_CHECK_TIME) { + return 1; + } + if (notify) { ctx->current_crl = crl; } + time_t *ptime; if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) { ptime = &ctx->param->check_time; } else { ptime = NULL; } - i = X509_cmp_time(X509_CRL_get0_lastUpdate(crl), ptime); + int i = X509_cmp_time(X509_CRL_get0_lastUpdate(crl), ptime); if (i == 0) { if (!notify) { return 0; @@ -1739,16 +1742,18 @@ static int check_policy(X509_STORE_CTX *ctx) { } static int check_cert_time(X509_STORE_CTX *ctx, X509 *x) { - time_t *ptime; - int i; + if (ctx->param->flags & X509_V_FLAG_NO_CHECK_TIME) { + return 1; + } + time_t *ptime; if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) { ptime = &ctx->param->check_time; } else { ptime = NULL; } - i = X509_cmp_time(X509_get_notBefore(x), ptime); + int i = X509_cmp_time(X509_get_notBefore(x), ptime); if (i == 0) { ctx->error = X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD; ctx->current_cert = x; diff --git a/go.mod b/go.mod index 25a9d6639..5e5fd6f29 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,6 @@ module boringssl.googlesource.com/boringssl go 1.13 require ( - golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a - golang.org/x/net v0.0.0-20210614182718-04defd469f4e + golang.org/x/crypto v0.1.0 + golang.org/x/net v0.1.0 ) diff --git a/go.sum b/go.sum index 87e3c894c..dd47a1724 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,32 @@ -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/include/openssl/kyber.h b/include/openssl/kyber.h new file mode 100644 index 000000000..074ac5906 --- /dev/null +++ b/include/openssl/kyber.h @@ -0,0 +1,109 @@ +#ifndef OPENSSL_HEADER_KYBER_H +#define OPENSSL_HEADER_KYBER_H + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#define KYBER512_PUBLIC_KEY_BYTES 800 +#define KYBER512_CIPHERTEXT_BYTES 768 +#define KYBER512_PRIVATE_KEY_BYTES 1632 +#define KYBER768_PUBLIC_KEY_BYTES 1184 +#define KYBER768_CIPHERTEXT_BYTES 1088 +#define KYBER768_PRIVATE_KEY_BYTES 2400 + +struct KYBER512_private_key { + uint8_t opaque[KYBER512_PRIVATE_KEY_BYTES]; +}; +struct KYBER768_private_key { + uint8_t opaque[KYBER768_PRIVATE_KEY_BYTES]; +}; +struct KYBER512_public_key { + uint8_t opaque[KYBER512_PUBLIC_KEY_BYTES]; +}; +struct KYBER768_public_key { + uint8_t opaque[KYBER768_PUBLIC_KEY_BYTES]; +}; + +// KYBER_GENERATE_KEY_BYTES is the number of bytes of entropy needed to +// generate a keypair. +#define KYBER_GENERATE_KEY_BYTES 64 + +// KYBER_ENCAP_BYTES is the number of bytes of entropy needed to encapsulate a +// session key. +#define KYBER_ENCAP_BYTES 32 + +// KYBER_KEY_BYTES is the number of bytes in a shared key. +#define KYBER_KEY_BYTES 32 + +// KYBER512_generate_key is a deterministic function that outputs a public and +// private key based on the given entropy. +OPENSSL_EXPORT void KYBER512_generate_key( + struct KYBER512_public_key *out_pub, struct KYBER512_private_key *out_priv, + const uint8_t input[KYBER_GENERATE_KEY_BYTES]); + +// KYBER768_generate_key is a deterministic function that outputs a public and +// private key based on the given entropy. +OPENSSL_EXPORT void KYBER768_generate_key( + struct KYBER768_public_key *out_pub, struct KYBER768_private_key *out_priv, + const uint8_t input[KYBER_GENERATE_KEY_BYTES]); + +// KYBER512_encap is a deterministic function the generates and encrypts a random +// session key from the given entropy, writing those values to |out_shared_key| +// and |out_ciphertext|, respectively. +OPENSSL_EXPORT void KYBER512_encap(uint8_t out_ciphertext[KYBER512_CIPHERTEXT_BYTES], + uint8_t out_shared_key[KYBER_KEY_BYTES], + const struct KYBER512_public_key *in_pub, + const uint8_t in[KYBER_ENCAP_BYTES]); + +// KYBER768_encap is a deterministic function the generates and encrypts a random +// session key from the given entropy, writing those values to |out_shared_key| +// and |out_ciphertext|, respectively. +OPENSSL_EXPORT void KYBER768_encap(uint8_t out_ciphertext[KYBER768_CIPHERTEXT_BYTES], + uint8_t out_shared_key[KYBER_KEY_BYTES], + const struct KYBER768_public_key *in_pub, + const uint8_t in[KYBER_ENCAP_BYTES]); + +// KYBER_decap decrypts a session key from |ciphertext_len| bytes of +// |ciphertext|. If the ciphertext is valid, the decrypted key is written to +// |out_shared_key|. Otherwise a key dervied from |ciphertext| and a secret key (kept +// in |in_priv|) is written. If the ciphertext is the wrong length then it will +// leak which was done via side-channels. Otherwise it should perform either +// action in constant-time. +OPENSSL_EXPORT void KYBER512_decap(uint8_t out_shared_key[KYBER_KEY_BYTES], + const struct KYBER512_private_key *in_priv, + const uint8_t *ciphertext, size_t ciphertext_len); + +// KYBER_decap decrypts a session key from |ciphertext_len| bytes of +// |ciphertext|. If the ciphertext is valid, the decrypted key is written to +// |out_shared_key|. Otherwise a key dervied from |ciphertext| and a secret key (kept +// in |in_priv|) is written. If the ciphertext is the wrong length then it will +// leak which was done via side-channels. Otherwise it should perform either +// action in constant-time. +OPENSSL_EXPORT void KYBER768_decap(uint8_t out_shared_key[KYBER_KEY_BYTES], + const struct KYBER768_private_key *in_priv, + const uint8_t *ciphertext, size_t ciphertext_len); + +// KYBER512_marshal_public_key serialises |in_pub| to |out|. +OPENSSL_EXPORT void KYBER512_marshal_public_key( + uint8_t out[KYBER512_PUBLIC_KEY_BYTES], const struct KYBER512_public_key *in_pub); + +// KYBER768_marshal_public_key serialises |in_pub| to |out|. +OPENSSL_EXPORT void KYBER768_marshal_public_key( + uint8_t out[KYBER768_PUBLIC_KEY_BYTES], const struct KYBER768_public_key *in_pub); + +// KYBER512_parse_public_key sets |*out| to the public-key encoded in |in|. +OPENSSL_EXPORT void KYBER512_parse_public_key( + struct KYBER512_public_key *out, const uint8_t in[KYBER512_PUBLIC_KEY_BYTES]); + +// KYBER768_parse_public_key sets |*out| to the public-key encoded in |in|. +OPENSSL_EXPORT void KYBER768_parse_public_key( + struct KYBER768_public_key *out, const uint8_t in[KYBER768_PUBLIC_KEY_BYTES]); + +#if defined(__cplusplus) +} // extern C +#endif + +#endif // OPENSSL_HEADER_KYBER_H diff --git a/include/openssl/nid.h b/include/openssl/nid.h index 54ecc5e26..583a06409 100644 --- a/include/openssl/nid.h +++ b/include/openssl/nid.h @@ -4255,6 +4255,12 @@ extern "C" { #define LN_hkdf "hkdf" #define NID_hkdf 963 +#define SN_X25519Kyber512Draft00 "X25519Kyber512Draft00" +#define NID_X25519Kyber512Draft00 964 + +#define SN_X25519Kyber768Draft00 "X25519Kyber768Draft00" +#define NID_X25519Kyber768Draft00 965 + #if defined(__cplusplus) } /* extern C */ diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 633a15a8d..815dcd117 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -2340,6 +2340,8 @@ OPENSSL_EXPORT int SSL_set1_curves_list(SSL *ssl, const char *curves); #define SSL_CURVE_SECP521R1 25 #define SSL_CURVE_X25519 29 #define SSL_CURVE_CECPQ2 16696 +#define SSL_CURVE_X25519KYBER512DRAFT00 65072 +#define SSL_CURVE_X25519KYBER768DRAFT00 65073 // SSL_get_curve_id returns the ID of the curve used by |ssl|'s most recently // completed handshake or 0 if not applicable. diff --git a/include/openssl/x509.h b/include/openssl/x509.h index 96b749587..57eb002d6 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -2582,6 +2582,10 @@ OPENSSL_EXPORT void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth); // will force the behaviour to match that of previous versions. #define X509_V_FLAG_NO_ALT_CHAINS 0x100000 +// X509_V_FLAG_NO_CHECK_TIME disables all time checks in certificate +// verification. +#define X509_V_FLAG_NO_CHECK_TIME 0x200000 + #define X509_VP_FLAG_DEFAULT 0x1 #define X509_VP_FLAG_OVERWRITE 0x2 #define X509_VP_FLAG_RESET_FLAGS 0x4 diff --git a/ssl/extensions.cc b/ssl/extensions.cc index 157d19f35..b5685236c 100644 --- a/ssl/extensions.cc +++ b/ssl/extensions.cc @@ -205,7 +205,8 @@ static bool tls1_check_duplicate_extensions(const CBS *cbs) { } static bool is_post_quantum_group(uint16_t id) { - return id == SSL_CURVE_CECPQ2; + return id == SSL_CURVE_CECPQ2 || id == SSL_CURVE_X25519KYBER512DRAFT00 || + id == SSL_CURVE_X25519KYBER768DRAFT00; } bool ssl_client_hello_init(const SSL *ssl, SSL_CLIENT_HELLO *out, @@ -341,8 +342,8 @@ bool tls1_get_shared_group(SSL_HANDSHAKE *hs, uint16_t *out_group_id) { for (uint16_t pref_group : pref) { for (uint16_t supp_group : supp) { if (pref_group == supp_group && - // CECPQ2(b) doesn't fit in the u8-length-prefixed ECPoint field in - // TLS 1.2 and below. + // CECPQ2(b) and Kyber don't fit in the u8-length-prefixed ECPoint + // field in TLS 1.2 and below. (ssl_protocol_version(ssl) >= TLS1_3_VERSION || !is_post_quantum_group(pref_group))) { *out_group_id = pref_group; @@ -408,7 +409,7 @@ bool tls1_set_curves_list(Array *out_group_ids, const char *curves) { bool tls1_check_group_id(const SSL_HANDSHAKE *hs, uint16_t group_id) { if (is_post_quantum_group(group_id) && ssl_protocol_version(hs->ssl) < TLS1_3_VERSION) { - // CECPQ2(b) requires TLS 1.3. + // CECPQ2(b) and Kyber requires TLS 1.3. return false; } diff --git a/ssl/internal.h b/ssl/internal.h index 153c9ca32..b01dbbb72 100644 --- a/ssl/internal.h +++ b/ssl/internal.h @@ -1115,7 +1115,7 @@ class SSLKeyShare { struct NamedGroup { int nid; uint16_t group_id; - const char name[8], alias[11]; + const char name[23], alias[23]; }; // NamedGroups returns all supported groups. diff --git a/ssl/ssl_key_share.cc b/ssl/ssl_key_share.cc index 920f25eed..2972d83a6 100644 --- a/ssl/ssl_key_share.cc +++ b/ssl/ssl_key_share.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -193,6 +194,200 @@ class X25519KeyShare : public SSLKeyShare { uint8_t private_key_[32]; }; +class X25519Kyber768Draft00KeyShare : public SSLKeyShare { + public: + X25519Kyber768Draft00KeyShare() {} + + uint16_t GroupID() const override { return SSL_CURVE_X25519KYBER768DRAFT00; } + + bool Offer(CBB *out) override { + uint8_t x25519_public_key[32]; + X25519_keypair(x25519_public_key, x25519_private_key_); + + uint8_t kyber_entropy[KYBER_GENERATE_KEY_BYTES]; + KYBER768_public_key kyber_public_key; + RAND_bytes(kyber_entropy, sizeof(kyber_entropy)); + KYBER768_generate_key(&kyber_public_key, &kyber_private_key_, kyber_entropy); + + uint8_t kyber_public_key_bytes[KYBER768_PUBLIC_KEY_BYTES]; + KYBER768_marshal_public_key(kyber_public_key_bytes, &kyber_public_key); + + if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) || + !CBB_add_bytes(out, kyber_public_key_bytes, + sizeof(kyber_public_key_bytes))) { + return false; + } + + return true; + } + + bool Accept(CBB *out_public_key, Array *out_secret, + uint8_t *out_alert, Span peer_key) override { + Array secret; + if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return false; + } + + uint8_t x25519_public_key[32]; + X25519_keypair(x25519_public_key, x25519_private_key_); + + KYBER768_public_key peer_public_key; + if (peer_key.size() != 32 + KYBER768_PUBLIC_KEY_BYTES) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + + KYBER768_parse_public_key(&peer_public_key, peer_key.data() + 32); + + if (!X25519(secret.data(), x25519_private_key_, peer_key.data())) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + + uint8_t ciphertext[KYBER768_CIPHERTEXT_BYTES]; + uint8_t entropy[KYBER_ENCAP_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); + + KYBER768_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy); + if(!CBB_add_bytes(out_public_key, x25519_public_key, + sizeof(x25519_public_key)) || + !CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { + return false; + } + + *out_secret = std::move(secret); + return true; + } + + bool Finish(Array *out_secret, uint8_t *out_alert, + Span peer_key) override { + *out_alert = SSL_AD_INTERNAL_ERROR; + + Array secret; + if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return false; + } + + if (peer_key.size() != 32 + KYBER768_CIPHERTEXT_BYTES || + !X25519(secret.data(), x25519_private_key_, peer_key.data())) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + + KYBER768_decap(secret.data() + 32, &kyber_private_key_, + peer_key.data() + 32, peer_key.size() - 32); + + *out_secret = std::move(secret); + return true; + } + + private: + uint8_t x25519_private_key_[32]; + KYBER768_private_key kyber_private_key_; +}; + +class X25519Kyber512Draft00KeyShare : public SSLKeyShare { + public: + X25519Kyber512Draft00KeyShare() {} + + uint16_t GroupID() const override { return SSL_CURVE_X25519KYBER512DRAFT00; } + + bool Offer(CBB *out) override { + uint8_t x25519_public_key[32]; + X25519_keypair(x25519_public_key, x25519_private_key_); + + uint8_t kyber_entropy[KYBER_GENERATE_KEY_BYTES]; + KYBER512_public_key kyber_public_key; + RAND_bytes(kyber_entropy, sizeof(kyber_entropy)); + KYBER512_generate_key(&kyber_public_key, &kyber_private_key_, kyber_entropy); + + uint8_t kyber_public_key_bytes[KYBER512_PUBLIC_KEY_BYTES]; + KYBER512_marshal_public_key(kyber_public_key_bytes, &kyber_public_key); + + if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) || + !CBB_add_bytes(out, kyber_public_key_bytes, + sizeof(kyber_public_key_bytes))) { + return false; + } + + return true; + } + + bool Accept(CBB *out_public_key, Array *out_secret, + uint8_t *out_alert, Span peer_key) override { + Array secret; + if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return false; + } + + uint8_t x25519_public_key[32]; + X25519_keypair(x25519_public_key, x25519_private_key_); + + KYBER512_public_key peer_public_key; + if (peer_key.size() != 32 + KYBER512_PUBLIC_KEY_BYTES) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + + KYBER512_parse_public_key(&peer_public_key, peer_key.data() + 32); + + if (!X25519(secret.data(), x25519_private_key_, peer_key.data())) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + + uint8_t ciphertext[KYBER512_CIPHERTEXT_BYTES]; + uint8_t entropy[KYBER_ENCAP_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); + + KYBER512_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy); + if(!CBB_add_bytes(out_public_key, x25519_public_key, + sizeof(x25519_public_key)) || + !CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { + return false; + } + + *out_secret = std::move(secret); + return true; + } + + bool Finish(Array *out_secret, uint8_t *out_alert, + Span peer_key) override { + *out_alert = SSL_AD_INTERNAL_ERROR; + + Array secret; + if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return false; + } + + if (peer_key.size() != 32 + KYBER512_CIPHERTEXT_BYTES || + !X25519(secret.data(), x25519_private_key_, peer_key.data())) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + + KYBER512_decap(secret.data() + 32, &kyber_private_key_, + peer_key.data() + 32, peer_key.size() - 32); + + *out_secret = std::move(secret); + return true; + } + + private: + uint8_t x25519_private_key_[32]; + KYBER512_private_key kyber_private_key_; +}; + class CECPQ2KeyShare : public SSLKeyShare { public: CECPQ2KeyShare() {} @@ -297,6 +492,10 @@ constexpr NamedGroup kNamedGroups[] = { {NID_secp521r1, SSL_CURVE_SECP521R1, "P-521", "secp521r1"}, {NID_X25519, SSL_CURVE_X25519, "X25519", "x25519"}, {NID_CECPQ2, SSL_CURVE_CECPQ2, "CECPQ2", "CECPQ2"}, + {NID_X25519Kyber512Draft00, SSL_CURVE_X25519KYBER512DRAFT00, + "X25519Kyber512Draft00", "Xyber512D00"}, + {NID_X25519Kyber768Draft00, SSL_CURVE_X25519KYBER768DRAFT00, + "X25519Kyber768Draft00", "Xyber768D00"} }; } // namespace @@ -323,6 +522,10 @@ UniquePtr SSLKeyShare::Create(uint16_t group_id) { return UniquePtr(New()); case SSL_CURVE_CECPQ2: return UniquePtr(New()); + case SSL_CURVE_X25519KYBER512DRAFT00: + return UniquePtr(New()); + case SSL_CURVE_X25519KYBER768DRAFT00: + return UniquePtr(New()); default: return nullptr; } diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index 295b6ef7c..67a01e0db 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc @@ -403,7 +403,18 @@ static const CurveTest kCurveTests[] = { "P-256:CECPQ2", { SSL_CURVE_SECP256R1, SSL_CURVE_CECPQ2 }, }, - + { + "Xyber512D00", + { SSL_CURVE_X25519KYBER512DRAFT00 }, + }, + { + "Xyber768D00", + { SSL_CURVE_X25519KYBER768DRAFT00 }, + }, + { + "P-256:Xyber512D00", + { SSL_CURVE_SECP256R1, SSL_CURVE_X25519KYBER512DRAFT00 }, + }, { "P-256:P-384:P-521:X25519", { @@ -8134,6 +8145,8 @@ RVHWbCvFvNZAoWiIJ2z34RLGInyZvCZ8xLAvsuaWULDDaoeDl1M0t4Hm SSL_CTX_set_verify(client_ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); + X509_VERIFY_PARAM_set_flags(SSL_CTX_get0_param(client_ctx.get()), + X509_V_FLAG_NO_CHECK_TIME); struct TestCase { X509 *cert; diff --git a/tool/speed.cc b/tool/speed.cc index c12ebb255..abd9d2df1 100644 --- a/tool/speed.cc +++ b/tool/speed.cc @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -900,6 +901,116 @@ static bool SpeedScrypt(const std::string &selected) { return true; } +static bool SpeedKyber768(const std::string &selected) { + if (!selected.empty() && selected != "Kyber768") { + return true; + } + + TimeResults results; + + if (!TimeFunction(&results, []() -> bool { + struct KYBER768_public_key pub; + struct KYBER768_private_key priv; + uint8_t entropy[KYBER_GENERATE_KEY_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); + KYBER768_generate_key(&pub, &priv, entropy); + return true; + })) { + fprintf(stderr, "Failed to time KYBER768_generate_key.\n"); + return false; + } + + results.Print("Kyber768 generate"); + + struct KYBER768_public_key pub; + struct KYBER768_private_key priv; + uint8_t key_entropy[KYBER_GENERATE_KEY_BYTES]; + RAND_bytes(key_entropy, sizeof(key_entropy)); + KYBER768_generate_key(&pub, &priv, key_entropy); + + uint8_t ciphertext[KYBER768_CIPHERTEXT_BYTES]; + if (!TimeFunction(&results, [&pub, &ciphertext]() -> bool { + uint8_t entropy[KYBER_ENCAP_BYTES]; + uint8_t shared_key[KYBER_KEY_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); + KYBER768_encap(ciphertext, shared_key, &pub, entropy); + return true; + })) { + fprintf(stderr, "Failed to time KYBER768_encap.\n"); + return false; + } + + results.Print("Kyber768 encap"); + + if (!TimeFunction(&results, [&priv, &ciphertext]() -> bool { + uint8_t shared_key[KYBER_KEY_BYTES]; + KYBER768_decap(shared_key, &priv, ciphertext, sizeof(ciphertext)); + return true; + })) { + fprintf(stderr, "Failed to time KYBER768_decap.\n"); + return false; + } + + results.Print("Kyber768 decap"); + + return true; +} + +static bool SpeedKyber512(const std::string &selected) { + if (!selected.empty() && selected != "Kyber512") { + return true; + } + + TimeResults results; + + if (!TimeFunction(&results, []() -> bool { + struct KYBER512_public_key pub; + struct KYBER512_private_key priv; + uint8_t entropy[KYBER_GENERATE_KEY_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); + KYBER512_generate_key(&pub, &priv, entropy); + return true; + })) { + fprintf(stderr, "Failed to time KYBER512_generate_key.\n"); + return false; + } + + results.Print("Kyber512 generate"); + + struct KYBER512_public_key pub; + struct KYBER512_private_key priv; + uint8_t key_entropy[KYBER_GENERATE_KEY_BYTES]; + RAND_bytes(key_entropy, sizeof(key_entropy)); + KYBER512_generate_key(&pub, &priv, key_entropy); + + uint8_t ciphertext[KYBER512_CIPHERTEXT_BYTES]; + if (!TimeFunction(&results, [&pub, &ciphertext]() -> bool { + uint8_t entropy[KYBER_ENCAP_BYTES]; + uint8_t shared_key[KYBER_KEY_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); + KYBER512_encap(ciphertext, shared_key, &pub, entropy); + return true; + })) { + fprintf(stderr, "Failed to time KYBER512_encap.\n"); + return false; + } + + results.Print("Kyber512 encap"); + + if (!TimeFunction(&results, [&priv, &ciphertext]() -> bool { + uint8_t shared_key[KYBER_KEY_BYTES]; + KYBER512_decap(shared_key, &priv, ciphertext, sizeof(ciphertext)); + return true; + })) { + fprintf(stderr, "Failed to time KYBER512_decap.\n"); + return false; + } + + results.Print("Kyber512 decap"); + + return true; +} + static bool SpeedHRSS(const std::string &selected) { if (!selected.empty() && selected != "HRSS") { return true; @@ -1445,6 +1556,8 @@ bool Speed(const std::vector &args) { !SpeedScrypt(selected) || !SpeedRSAKeyGen(selected) || !SpeedHRSS(selected) || + !SpeedKyber512(selected) || + !SpeedKyber768(selected) || !SpeedHashToCurve(selected) || !SpeedTrustToken("TrustToken-Exp1-Batch1", TRUST_TOKEN_experiment_v1(), 1, selected) ||