boring2/boring-sys/patches/boring-pq.patch

3304 lines
108 KiB
Diff

From d3f8e78ff611976f81cca75a2cfced25534960be Mon Sep 17 00:00:00 2001
From: Bas Westerbaan <bas@cloudflare.com>
Date: Fri, 22 Jul 2022 16:43:48 +0200
Subject: [PATCH] Add X25519Kyber{512,768}Draft00 key exchange
Also adds temporary P256Kyber768Draft00 key exchange.
Cf RTG-2076 RTG-2051 RTG-2508
---
BUILD.generated.bzl | 3 +
CMakeLists.txt | 2 +
sources.json | 5 +-
src/crypto/CMakeLists.txt | 2 +
src/crypto/kyber/kyber.c | 2252 +++++++++++++++++++++++++++++++++++
src/crypto/kyber/kyber512.c | 5 +
src/crypto/kyber/kyber768.c | 4 +
src/crypto/obj/obj_dat.h | 18 +-
src/crypto/obj/obj_mac.num | 3 +
src/crypto/obj/objects.txt | 5 +
src/include/openssl/kyber.h | 109 ++
src/include/openssl/nid.h | 9 +
src/include/openssl/ssl.h | 3 +
src/ssl/internal.h | 2 +-
src/ssl/ssl_key_share.cc | 423 +++++++
src/ssl/ssl_test.cc | 21 +-
src/ssl/t1_lib.cc | 10 +-
src/tool/speed.cc | 113 ++
18 files changed, 2981 insertions(+), 8 deletions(-)
create mode 100644 src/crypto/kyber/kyber.c
create mode 100644 src/crypto/kyber/kyber512.c
create mode 100644 src/crypto/kyber/kyber768.c
create mode 100644 src/include/openssl/kyber.h
diff --git a/BUILD.generated.bzl b/BUILD.generated.bzl
index 6aba1a22a..0c99721df 100644
--- a/BUILD.generated.bzl
+++ b/BUILD.generated.bzl
@@ -168,6 +168,7 @@ crypto_headers = [
"src/include/openssl/hmac.h",
"src/include/openssl/hrss.h",
"src/include/openssl/is_boringssl.h",
+ "src/include/openssl/kyber.h",
"src/include/openssl/lhash.h",
"src/include/openssl/md4.h",
"src/include/openssl/md5.h",
@@ -370,6 +371,8 @@ crypto_sources = [
"src/crypto/hkdf/hkdf.c",
"src/crypto/hpke/hpke.c",
"src/crypto/hrss/hrss.c",
+ "src/crypto/kyber/kyber512.c",
+ "src/crypto/kyber/kyber768.c",
"src/crypto/lhash/lhash.c",
"src/crypto/mem.c",
"src/crypto/obj/obj.c",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1645a264a..f174f9f56 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -471,6 +471,8 @@ add_library(
src/crypto/hkdf/hkdf.c
src/crypto/hpke/hpke.c
src/crypto/hrss/hrss.c
+ src/crypto/kyber/kyber512.c
+ src/crypto/kyber/kyber768.c
src/crypto/lhash/lhash.c
src/crypto/mem.c
src/crypto/obj/obj.c
diff --git a/sources.json b/sources.json
index 584a816d8..b06ae8363 100644
--- a/sources.json
+++ b/sources.json
@@ -114,6 +114,8 @@
"src/crypto/hkdf/hkdf.c",
"src/crypto/hpke/hpke.c",
"src/crypto/hrss/hrss.c",
+ "src/crypto/kyber/kyber512.c",
+ "src/crypto/kyber/kyber768.c",
"src/crypto/lhash/lhash.c",
"src/crypto/mem.c",
"src/crypto/obj/obj.c",
@@ -276,6 +278,7 @@
"src/include/openssl/hkdf.h",
"src/include/openssl/hmac.h",
"src/include/openssl/hrss.h",
+ "src/include/openssl/kyber.h",
"src/include/openssl/is_boringssl.h",
"src/include/openssl/lhash.h",
"src/include/openssl/md4.h",
@@ -971,4 +974,4 @@
"urandom_test": [
"src/crypto/fipsmodule/rand/urandom_test.cc"
]
-}
\ No newline at end of file
+}
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index cde92b591..294ae42e7 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -307,6 +307,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/src/crypto/kyber/kyber.c b/src/crypto/kyber/kyber.c
new file mode 100644
index 000000000..346d4daec
--- /dev/null
+++ b/src/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 <openssl/kyber.h>
+#include <openssl/base.h>
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#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<len;i++)
+ r |= a[i] ^ b[i];
+
+ return (-(uint64_t)r) >> 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<len;i++)
+ r[i] ^= b & (r[i] ^ x[i]);
+}
+
+//
+// reduce.c
+//
+
+/*************************************************
+* Name: montgomery_reduce
+*
+* Description: Montgomery reduction; given a 32-bit integer a, computes
+* 16-bit integer congruent to a * R^-1 mod q, where R=2^16
+*
+* Arguments: - int32_t a: input integer to be reduced;
+* has to be in {-q2^15,...,q2^15-1}
+*
+* Returns: integer in {-q+1,...,q-1} congruent to a * R^-1 modulo q.
+**************************************************/
+static int16_t montgomery_reduce(int32_t a)
+{
+ int16_t t;
+
+ t = (int16_t)a*QINV;
+ t = (a - (int32_t)t*KYBER_Q) >> 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<KYBER_N/8;i++) {
+ t = load32_littleendian(buf+4*i);
+ d = t & 0x55555555;
+ d += (t>>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<KYBER_N/4;i++) {
+ t = load24_littleendian(buf+3*i);
+ d = t & 0x00249249;
+ d += (t>>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;i<KYBER_N/8;i++) {
+ for(j=0;j<8;j++) {
+ // map to positive standard representatives
+ u = a->coeffs[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;i<KYBER_N/8;i++) {
+ for(j=0;j<8;j++) {
+ // map to positive standard representatives
+ u = a->coeffs[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;i<KYBER_N/2;i++) {
+ r->coeffs[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<KYBER_N/8;i++) {
+ t[0] = (a[0] >> 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;i<KYBER_N/2;i++) {
+ // map to positive standard representatives
+ t0 = a->coeffs[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;i<KYBER_N/2;i++) {
+ r->coeffs[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<KYBER_N/8;i++) {
+ for(j=0;j<8;j++) {
+ mask = -(int16_t)((msg[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;i<KYBER_N/8;i++) {
+ msg[i] = 0;
+ for(j=0;j<8;j++) {
+ t = a->coeffs[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;i<KYBER_N/4;i++) {
+ basemul(&r->coeffs[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;i<KYBER_N;i++)
+ r->coeffs[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;i<KYBER_N;i++)
+ r->coeffs[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;i<KYBER_N;i++)
+ r->coeffs[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;i<KYBER_N;i++)
+ r->coeffs[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;i<KYBER_K;i++) {
+ for(j=0;j<KYBER_N/8;j++) {
+ for(k=0;k<8;k++) {
+ t[k] = a->vec[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;i<KYBER_K;i++) {
+ for(j=0;j<KYBER_N/4;j++) {
+ for(k=0;k<4;k++) {
+ t[k] = a->vec[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<KYBER_K;i++) {
+ for(j=0;j<KYBER_N/8;j++) {
+ t[0] = (a[0] >> 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<KYBER_K;i++) {
+ for(j=0;j<KYBER_N/4;j++) {
+ t[0] = (a[0] >> 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;i<KYBER_K;i++)
+ poly_tobytes(r+i*KYBER_POLYBYTES, &a->vec[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;i<KYBER_K;i++)
+ poly_frombytes(&r->vec[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;i<KYBER_K;i++)
+ poly_ntt(&r->vec[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;i<KYBER_K;i++)
+ poly_invntt_tomont(&r->vec[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;i<KYBER_K;i++) {
+ poly_basemul_montgomery(&t, &a->vec[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;i<KYBER_K;i++)
+ poly_reduce(&r->vec[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;i<KYBER_K;i++)
+ poly_add(&r->vec[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<KYBER_SYMBYTES;i++)
+ r[i+KYBER_POLYVECBYTES] = seed[i];
+}
+
+/*************************************************
+* Name: unpack_pk
+*
+* Description: De-serialize public key from a byte array;
+* approximate inverse of pack_pk
+*
+* Arguments: - polyvec *pk: pointer to output public-key polynomial vector
+* - uint8_t *seed: pointer to output seed to generate matrix A
+* - const uint8_t *packedpk: pointer to input serialized public key
+**************************************************/
+static void unpack_pk(polyvec *pk,
+ uint8_t seed[KYBER_SYMBYTES],
+ const uint8_t packedpk[KYBER_INDCPA_PUBLICKEYBYTES])
+{
+ size_t i;
+ polyvec_frombytes(pk, packedpk);
+ for(i=0;i<KYBER_SYMBYTES;i++)
+ seed[i] = packedpk[i+KYBER_POLYVECBYTES];
+}
+
+/*************************************************
+* Name: pack_sk
+*
+* Description: Serialize the secret key
+*
+* Arguments: - uint8_t *r: pointer to output serialized secret key
+* - polyvec *sk: pointer to input vector of polynomials (secret key)
+**************************************************/
+static void pack_sk(uint8_t r[KYBER_INDCPA_SECRETKEYBYTES], polyvec *sk)
+{
+ polyvec_tobytes(r, sk);
+}
+
+/*************************************************
+* Name: unpack_sk
+*
+* Description: De-serialize the secret key; inverse of pack_sk
+*
+* Arguments: - polyvec *sk: pointer to output vector of polynomials (secret key)
+* - const uint8_t *packedsk: pointer to input serialized secret key
+**************************************************/
+static void unpack_sk(polyvec *sk, const uint8_t packedsk[KYBER_INDCPA_SECRETKEYBYTES])
+{
+ polyvec_frombytes(sk, packedsk);
+}
+
+/*************************************************
+* Name: pack_ciphertext
+*
+* Description: Serialize the ciphertext as concatenation of the
+* compressed and serialized vector of polynomials b
+* and the compressed and serialized polynomial v
+*
+* Arguments: uint8_t *r: pointer to the output serialized ciphertext
+* poly *pk: pointer to the input vector of polynomials b
+* poly *v: pointer to the input polynomial v
+**************************************************/
+static void pack_ciphertext(uint8_t r[KYBER_INDCPA_BYTES], polyvec *b, poly *v)
+{
+ polyvec_compress(r, b);
+ poly_compress(r+KYBER_POLYVECCOMPRESSEDBYTES, v);
+}
+
+/*************************************************
+* Name: unpack_ciphertext
+*
+* Description: De-serialize and decompress ciphertext from a byte array;
+* approximate inverse of pack_ciphertext
+*
+* Arguments: - polyvec *b: pointer to the output vector of polynomials b
+* - poly *v: pointer to the output polynomial v
+* - const uint8_t *c: pointer to the input serialized ciphertext
+**************************************************/
+static void unpack_ciphertext(polyvec *b, poly *v, const uint8_t c[KYBER_INDCPA_BYTES])
+{
+ polyvec_decompress(b, c);
+ poly_decompress(v, c+KYBER_POLYVECCOMPRESSEDBYTES);
+}
+
+/*************************************************
+* Name: rej_uniform
+*
+* Description: Run rejection sampling on uniform random bytes to generate
+* uniform random integers mod q
+*
+* Arguments: - int16_t *r: pointer to output buffer
+* - unsigned int len: requested number of 16-bit integers (uniform mod q)
+* - const uint8_t *buf: pointer to input buffer (assumed to be uniformly random bytes)
+* - unsigned int buflen: length of input buffer in bytes
+*
+* Returns number of sampled 16-bit integers (at most len)
+**************************************************/
+static unsigned int rej_uniform(int16_t *r,
+ unsigned int len,
+ const uint8_t *buf,
+ unsigned int buflen)
+{
+ unsigned int ctr, pos;
+ uint16_t val0, val1;
+
+ ctr = pos = 0;
+ while(ctr < len && pos + 3 <= buflen) {
+ val0 = ((buf[pos+0] >> 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<KYBER_K;i++) {
+ for(j=0;j<KYBER_K;j++) {
+ if(transposed)
+ xof_absorb(&state, seed, i, j);
+ else
+ xof_absorb(&state, seed, j, i);
+
+ xof_squeezeblocks(buf, GEN_MATRIX_NBLOCKS, &state);
+ buflen = GEN_MATRIX_NBLOCKS*XOF_BLOCKBYTES;
+ ctr = rej_uniform(a[i].vec[j].coeffs, KYBER_N, buf, buflen);
+
+ while(ctr < KYBER_N) {
+ off = buflen % 3;
+ for(k = 0; k < off; k++)
+ buf[k] = buf[buflen - off + k];
+ xof_squeezeblocks(buf + off, 1, &state);
+ buflen = off + XOF_BLOCKBYTES;
+ ctr += rej_uniform(a[i].vec[j].coeffs + ctr, KYBER_N - ctr, buf, buflen);
+ }
+ }
+ }
+}
+
+/*************************************************
+* Name: indcpa_keypair
+*
+* Description: Generates public and private key for the CPA-secure
+* public-key encryption scheme underlying Kyber
+*
+* Arguments: - uint8_t *pk: pointer to output public key
+* (of length KYBER_INDCPA_PUBLICKEYBYTES bytes)
+* - uint8_t *sk: pointer to output private key
+ (of length KYBER_INDCPA_SECRETKEYBYTES bytes)
+**************************************************/
+static void indcpa_keypair(uint8_t pk[KYBER_INDCPA_PUBLICKEYBYTES],
+ uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES],
+ const uint8_t seed[KYBER_SYMBYTES])
+{
+ unsigned int i;
+ uint8_t buf[2*KYBER_SYMBYTES];
+ const uint8_t *publicseed = buf;
+ const uint8_t *noiseseed = buf+KYBER_SYMBYTES;
+ uint8_t nonce = 0;
+ polyvec a[KYBER_K], e, pkpv, skpv;
+
+ memcpy(buf, seed, KYBER_SYMBYTES);
+ hash_g(buf, buf, KYBER_SYMBYTES);
+
+ gen_a(a, publicseed);
+
+ for(i=0;i<KYBER_K;i++)
+ poly_getnoise_eta1(&skpv.vec[i], noiseseed, nonce++);
+ for(i=0;i<KYBER_K;i++)
+ poly_getnoise_eta1(&e.vec[i], noiseseed, nonce++);
+
+ polyvec_ntt(&skpv);
+ polyvec_ntt(&e);
+
+ // matrix-vector multiplication
+ for(i=0;i<KYBER_K;i++) {
+ polyvec_basemul_acc_montgomery(&pkpv.vec[i], &a[i], &skpv);
+ poly_tomont(&pkpv.vec[i]);
+ }
+
+ polyvec_add(&pkpv, &pkpv, &e);
+ polyvec_reduce(&pkpv);
+
+ pack_sk(sk, &skpv);
+ pack_pk(pk, &pkpv, publicseed);
+}
+
+/*************************************************
+* Name: indcpa_enc
+*
+* Description: Encryption function of the CPA-secure
+* public-key encryption scheme underlying Kyber.
+*
+* Arguments: - uint8_t *c: pointer to output ciphertext
+* (of length KYBER_INDCPA_BYTES bytes)
+* - const uint8_t *m: pointer to input message
+* (of length KYBER_INDCPA_MSGBYTES bytes)
+* - const uint8_t *pk: pointer to input public key
+* (of length KYBER_INDCPA_PUBLICKEYBYTES)
+* - const uint8_t *coins: pointer to input random coins used as seed
+* (of length KYBER_SYMBYTES) to deterministically
+* generate all randomness
+**************************************************/
+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])
+{
+ unsigned int i;
+ uint8_t seed[KYBER_SYMBYTES];
+ uint8_t nonce = 0;
+ polyvec sp, pkpv, ep, at[KYBER_K], b;
+ poly v, k, epp;
+
+ unpack_pk(&pkpv, seed, pk);
+ poly_frommsg(&k, m);
+ gen_at(at, seed);
+
+ for(i=0;i<KYBER_K;i++)
+ poly_getnoise_eta1(sp.vec+i, coins, nonce++);
+ for(i=0;i<KYBER_K;i++)
+ poly_getnoise_eta2(ep.vec+i, coins, nonce++);
+ poly_getnoise_eta2(&epp, coins, nonce++);
+
+ polyvec_ntt(&sp);
+
+ // matrix-vector multiplication
+ for(i=0;i<KYBER_K;i++)
+ polyvec_basemul_acc_montgomery(&b.vec[i], &at[i], &sp);
+
+ polyvec_basemul_acc_montgomery(&v, &pkpv, &sp);
+
+ polyvec_invntt_tomont(&b);
+ poly_invntt_tomont(&v);
+
+ polyvec_add(&b, &b, &ep);
+ poly_add(&v, &v, &epp);
+ poly_add(&v, &v, &k);
+ polyvec_reduce(&b);
+ poly_reduce(&v);
+
+ pack_ciphertext(c, &b, &v);
+}
+
+/*************************************************
+* Name: indcpa_dec
+*
+* Description: Decryption function of the CPA-secure
+* public-key encryption scheme underlying Kyber.
+*
+* Arguments: - uint8_t *m: pointer to output decrypted message
+* (of length KYBER_INDCPA_MSGBYTES)
+* - const uint8_t *c: pointer to input ciphertext
+* (of length KYBER_INDCPA_BYTES)
+* - const uint8_t *sk: pointer to input secret key
+* (of length KYBER_INDCPA_SECRETKEYBYTES)
+**************************************************/
+static void indcpa_dec(uint8_t m[KYBER_INDCPA_MSGBYTES],
+ const uint8_t c[KYBER_INDCPA_BYTES],
+ const uint8_t sk[KYBER_INDCPA_SECRETKEYBYTES])
+{
+ polyvec b, skpv;
+ poly v, mp;
+
+ unpack_ciphertext(&b, &v, c);
+ unpack_sk(&skpv, sk);
+
+ polyvec_ntt(&b);
+ polyvec_basemul_acc_montgomery(&mp, &skpv, &b);
+ poly_invntt_tomont(&mp);
+
+ poly_sub(&mp, &v, &mp);
+ poly_reduce(&mp);
+
+ poly_tomsg(m, &mp);
+}
+
+//
+// fips202.c
+//
+
+/* Based on the public domain implementation in crypto_hash/keccakc512/simple/ from
+ * http://bench.cr.yp.to/supercop.html by Ronny Van Keer and the public domain "TweetFips202"
+ * implementation from https://twitter.com/tweetfips202 by Gilles Van Assche, Daniel J. Bernstein,
+ * and Peter Schwabe */
+
+#define NROUNDS 24
+#define ROL(a, offset) ((a << offset) ^ (a >> (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;i<r/8;i++)
+ s[i] ^= load64(in+8*i);
+ in += r;
+ inlen -= r;
+ KeccakF1600_StatePermute(s);
+ }
+
+ for(i=0;i<inlen;i++)
+ s[i/8] ^= (uint64_t)in[i] << 8*(i%8);
+
+ s[i/8] ^= (uint64_t)p << 8*(i%8);
+ s[(r-1)/8] ^= 1ULL << 63;
+}
+
+/*************************************************
+* Name: keccak_squeezeblocks
+*
+* Description: Squeeze step of Keccak. Squeezes full blocks of r bytes each.
+* Modifies the state. Can be called multiple times to keep
+* squeezing, i.e., is incremental. Assumes zero bytes of current
+* block have already been squeezed.
+*
+* Arguments: - uint8_t *out: pointer to output blocks
+* - size_t nblocks: number of blocks to be squeezed (written to out)
+* - uint64_t *s: pointer to input/output Keccak state
+* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128)
+**************************************************/
+static void keccak_squeezeblocks(uint8_t *out,
+ size_t nblocks,
+ uint64_t s[25],
+ unsigned int r)
+{
+ unsigned int i;
+
+ while(nblocks) {
+ KeccakF1600_StatePermute(s);
+ for(i=0;i<r/8;i++)
+ store64(out+8*i, s[i]);
+ out += r;
+ nblocks -= 1;
+ }
+}
+
+
+/*************************************************
+* Name: shake128_absorb_once
+*
+* Description: Initialize, absorb into and finalize SHAKE128 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 shake128_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen)
+{
+ keccak_absorb_once(state->s, 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;i<KYBER_INDCPA_PUBLICKEYBYTES;i++)
+ sk[i+KYBER_INDCPA_SECRETKEYBYTES] = pk[i];
+ hash_h(sk+KYBER_SECRETKEYBYTES-2*KYBER_SYMBYTES, pk, KYBER_PUBLICKEYBYTES);
+ /* Value z for pseudo-random output on reject */
+ memcpy(sk+KYBER_SECRETKEYBYTES-KYBER_SYMBYTES, seed+KYBER_SYMBYTES, KYBER_SYMBYTES);
+}
+
+// Modified crypto_kem_enc to BoringSSL style API
+void encap(uint8_t out_ciphertext[KYBER_CIPHERTEXTBYTES],
+ uint8_t ss[KYBER_KEY_BYTES],
+ const struct public_key *in_pub,
+ const uint8_t seed[KYBER_ENCAP_BYTES])
+{
+ const uint8_t *pk = &in_pub->opaque[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;i<KYBER_SYMBYTES;i++)
+ buf[KYBER_SYMBYTES+i] = sk[KYBER_SECRETKEYBYTES-2*KYBER_SYMBYTES+i];
+ hash_g(kr, buf, 2*KYBER_SYMBYTES);
+
+ /* coins are in kr+KYBER_SYMBYTES */
+ indcpa_enc(cmp, buf, pk, kr+KYBER_SYMBYTES);
+
+ fail = verify(ct, cmp, KYBER_CIPHERTEXTBYTES);
+ }
+
+ /* overwrite coins in kr with H(c) */
+ hash_h(kr+KYBER_SYMBYTES, ct, ciphertext_len);
+
+ /* Overwrite pre-k with z on re-encryption failure */
+ cmov(kr, sk+KYBER_SECRETKEYBYTES-KYBER_SYMBYTES, KYBER_SYMBYTES, fail);
+
+ /* hash concatenation of pre-k and H(c) to k */
+ kdf(ss, kr, 2*KYBER_SYMBYTES);
+}
+
+void marshal_public_key(uint8_t out[KYBER_PUBLICKEYBYTES],
+ const struct public_key *in_pub) {
+ memcpy(out, &in_pub->opaque, 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/src/crypto/kyber/kyber512.c b/src/crypto/kyber/kyber512.c
new file mode 100644
index 000000000..21eed11a2
--- /dev/null
+++ b/src/crypto/kyber/kyber512.c
@@ -0,0 +1,5 @@
+#define KYBER_K 2
+
+#include "kyber.c"
+
+
diff --git a/src/crypto/kyber/kyber768.c b/src/crypto/kyber/kyber768.c
new file mode 100644
index 000000000..3e572b72e
--- /dev/null
+++ b/src/crypto/kyber/kyber768.c
@@ -0,0 +1,4 @@
+#define KYBER_K 3
+
+#include "kyber.c"
+
diff --git a/src/crypto/obj/obj_dat.h b/src/crypto/obj/obj_dat.h
index 778d8e3cf..8956aa6a9 100644
--- a/src/crypto/obj/obj_dat.h
+++ b/src/crypto/obj/obj_dat.h
@@ -57,7 +57,7 @@
/* This file is generated by crypto/obj/objects.go. */
-#define NUM_NID 963
+#define NUM_NID 970
static const uint8_t kObjectData[] = {
/* NID_rsadsi */
@@ -8781,6 +8781,16 @@ static const ASN1_OBJECT kObjects[NUM_NID] = {
{"ED448", "ED448", NID_ED448, 3, &kObjectData[6181], 0},
{"X448", "X448", NID_X448, 3, &kObjectData[6184], 0},
{"SHA512-256", "sha512-256", NID_sha512_256, 9, &kObjectData[6187], 0},
+ {NULL, NULL, NID_undef, 0, NULL, 0},
+ {"X25519Kyber512Draft00", "X25519Kyber512Draft00",
+ NID_X25519Kyber512Draft00, 0, NULL, 0},
+ {"X25519Kyber768Draft00", "X25519Kyber768Draft00",
+ NID_X25519Kyber768Draft00, 0, NULL, 0},
+ {NULL, NULL, NID_undef, 0, NULL, 0},
+ {NULL, NULL, NID_undef, 0, NULL, 0},
+ {NULL, NULL, NID_undef, 0, NULL, 0},
+ {"P256Kyber768Draft00", "P256Kyber768Draft00", NID_P256Kyber768Draft00, 0,
+ NULL, 0},
};
static const uint16_t kNIDsInShortNameOrder[] = {
@@ -8913,6 +8923,7 @@ static const uint16_t kNIDsInShortNameOrder[] = {
18 /* OU */,
749 /* Oakley-EC2N-3 */,
750 /* Oakley-EC2N-4 */,
+ 969 /* P256Kyber768Draft00 */,
9 /* PBE-MD2-DES */,
168 /* PBE-MD2-RC2-64 */,
10 /* PBE-MD5-DES */,
@@ -8979,6 +8990,8 @@ static const uint16_t kNIDsInShortNameOrder[] = {
458 /* UID */,
0 /* UNDEF */,
948 /* X25519 */,
+ 964 /* X25519Kyber512Draft00 */,
+ 965 /* X25519Kyber768Draft00 */,
961 /* X448 */,
11 /* X500 */,
378 /* X500algorithms */,
@@ -9826,6 +9839,7 @@ static const uint16_t kNIDsInLongNameOrder[] = {
366 /* OCSP Nonce */,
371 /* OCSP Service Locator */,
180 /* OCSP Signing */,
+ 969 /* P256Kyber768Draft00 */,
161 /* PBES2 */,
69 /* PBKDF2 */,
162 /* PBMAC1 */,
@@ -9850,6 +9864,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/src/crypto/obj/obj_mac.num b/src/crypto/obj/obj_mac.num
index f110ee929..802692f36 100644
--- a/src/crypto/obj/obj_mac.num
+++ b/src/crypto/obj/obj_mac.num
@@ -951,3 +951,6 @@ CECPQ2 959
ED448 960
X448 961
sha512_256 962
+X25519Kyber512Draft00 964
+X25519Kyber768Draft00 965
+P256Kyber768Draft00 969
diff --git a/src/crypto/obj/objects.txt b/src/crypto/obj/objects.txt
index b88342dd8..458218c59 100644
--- a/src/crypto/obj/objects.txt
+++ b/src/crypto/obj/objects.txt
@@ -1335,6 +1335,11 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme
# NID for CECPQ2 (no corresponding OID).
: CECPQ2
+# NID for Kyber hybrids (no corresponding OID).
+ : X25519Kyber512Draft00
+ : X25519Kyber768Draft00
+ : P256Kyber768Draft00
+
# See RFC 8410.
1 3 101 110 : X25519
1 3 101 111 : X448
diff --git a/src/include/openssl/kyber.h b/src/include/openssl/kyber.h
new file mode 100644
index 000000000..074ac5906
--- /dev/null
+++ b/src/include/openssl/kyber.h
@@ -0,0 +1,109 @@
+#ifndef OPENSSL_HEADER_KYBER_H
+#define OPENSSL_HEADER_KYBER_H
+
+#include <openssl/base.h>
+
+#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/src/include/openssl/nid.h b/src/include/openssl/nid.h
index bf7f3da5f..3c146db3c 100644
--- a/src/include/openssl/nid.h
+++ b/src/include/openssl/nid.h
@@ -4251,6 +4251,15 @@ extern "C" {
#define NID_sha512_256 962
#define OBJ_sha512_256 2L, 16L, 840L, 1L, 101L, 3L, 4L, 2L, 6L
+#define SN_X25519Kyber512Draft00 "X25519Kyber512Draft00"
+#define NID_X25519Kyber512Draft00 964
+
+#define SN_X25519Kyber768Draft00 "X25519Kyber768Draft00"
+#define NID_X25519Kyber768Draft00 965
+
+#define SN_P256Kyber768Draft00 "P256Kyber768Draft00"
+#define NID_P256Kyber768Draft00 969
+
#if defined(__cplusplus)
} /* extern C */
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 7ff7e72c8..6833ce6de 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -2271,6 +2271,9 @@ 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
+#define SSL_CURVE_P256KYBER768DRAFT00 65074
// 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/src/ssl/internal.h b/src/ssl/internal.h
index b3b754053..7d3880f79 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -1102,7 +1102,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/src/ssl/ssl_key_share.cc b/src/ssl/ssl_key_share.cc
index 6cac3cf22..4bcdb2014 100644
--- a/src/ssl/ssl_key_share.cc
+++ b/src/ssl/ssl_key_share.cc
@@ -25,6 +25,7 @@
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/hrss.h>
+#include <openssl/kyber.h>
#include <openssl/mem.h>
#include <openssl/nid.h>
#include <openssl/rand.h>
@@ -208,6 +209,416 @@ class X25519KeyShare : public SSLKeyShare {
uint8_t private_key_[32];
};
+class P256Kyber768Draft00KeyShare : public SSLKeyShare {
+ public:
+ P256Kyber768Draft00KeyShare() {}
+
+ uint16_t GroupID() const override { return SSL_CURVE_P256KYBER768DRAFT00; }
+
+ bool Offer(CBB *out) override {
+ assert(!p256_private_key_);
+
+ // Set up a shared |BN_CTX| for P-256 operations.
+ UniquePtr<BN_CTX> bn_ctx(BN_CTX_new());
+ if (!bn_ctx) {
+ return false;
+ }
+
+ BN_CTXScope scope(bn_ctx.get());
+
+ // Generate a P-256 private key.
+ UniquePtr<EC_GROUP> group;
+ group.reset(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ p256_private_key_.reset(BN_new());
+ if (!group || !p256_private_key_ ||
+ !BN_rand_range_ex(p256_private_key_.get(), 1,
+ EC_GROUP_get0_order(group.get()))) {
+ return false;
+ }
+
+ // Compute the corresponding P-256 public key and serialize it.
+ UniquePtr<EC_POINT> p256_public_key(EC_POINT_new(group.get()));
+ if (!p256_public_key ||
+ !EC_POINT_mul(group.get(), p256_public_key.get(), p256_private_key_.get(),
+ NULL, NULL, bn_ctx.get()) ||
+ !EC_POINT_point2cbb(out, group.get(), p256_public_key.get(),
+ POINT_CONVERSION_UNCOMPRESSED, bn_ctx.get())) {
+ return false;
+ }
+
+
+ // Kyber
+ 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, kyber_public_key_bytes,
+ sizeof(kyber_public_key_bytes))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool Accept(CBB *out_public_key, Array<uint8_t> *out_secret,
+ uint8_t *out_alert, Span<const uint8_t> peer_key) override {
+ assert(!p256_private_key_);
+
+ if (peer_key.size() != 65 + KYBER768_PUBLIC_KEY_BYTES) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+ return false;
+ }
+
+ // Set up a shared |BN_CTX| for P-256 operations.
+ UniquePtr<BN_CTX> bn_ctx(BN_CTX_new());
+ if (!bn_ctx) {
+ return false;
+ }
+
+ BN_CTXScope scope(bn_ctx.get());
+
+ UniquePtr<EC_GROUP> group;
+ group.reset(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ if (!group) {
+ return false;
+ }
+
+ // Parse peer point
+ UniquePtr<EC_POINT> peer_point(EC_POINT_new(group.get()));
+ UniquePtr<EC_POINT> result(EC_POINT_new(group.get()));
+ BIGNUM *x = BN_CTX_get(bn_ctx.get());
+ if (!peer_point || !result || !x) {
+ return false;
+ }
+
+ if (peer_key.empty() || peer_key[0] != POINT_CONVERSION_UNCOMPRESSED ||
+ !EC_POINT_oct2point(group.get(), peer_point.get(), peer_key.data(),
+ 65, bn_ctx.get())) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return false;
+ }
+
+ p256_private_key_.reset(BN_new());
+ if (!p256_private_key_ || !BN_rand_range_ex(p256_private_key_.get(), 1,
+ EC_GROUP_get0_order(group.get()))) {
+ return false;
+ }
+
+ // Compute the corresponding P-256 public key and serialize it.
+ UniquePtr<EC_POINT> p256_public_key(EC_POINT_new(group.get()));
+ if (!p256_public_key ||
+ !EC_POINT_mul(group.get(), p256_public_key.get(), p256_private_key_.get(),
+ NULL, NULL, bn_ctx.get()) ||
+ !EC_POINT_point2cbb(out_public_key, group.get(), p256_public_key.get(),
+ POINT_CONVERSION_UNCOMPRESSED, bn_ctx.get())) {
+ return false;
+ }
+
+ // Compute the x-coordinate of |peer_key| * |p256_private_key_|.
+ if (!EC_POINT_mul(group.get(), result.get(), NULL, peer_point.get(),
+ p256_private_key_.get(), bn_ctx.get()) ||
+ !EC_POINT_get_affine_coordinates_GFp(group.get(), result.get(), x, NULL,
+ bn_ctx.get())) {
+ return false;
+ }
+
+ // Encode the x-coordinate left-padded with zeros.
+ Array<uint8_t> secret;
+ if (!secret.Init(32 + KYBER_KEY_BYTES) ||
+ !BN_bn2bin_padded(secret.data(), 32, x)) {
+ return false;
+ }
+
+
+ KYBER768_public_key peer_public_key;
+ KYBER768_parse_public_key(&peer_public_key, peer_key.data() + 65);
+
+ 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, ciphertext, sizeof(ciphertext))) {
+ return false;
+ }
+
+ *out_secret = std::move(secret);
+ return true;
+ }
+
+ bool Finish(Array<uint8_t> *out_secret, uint8_t *out_alert,
+ Span<const uint8_t> peer_key) override {
+ assert(p256_private_key_);
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+
+ Array<uint8_t> secret;
+ if (!secret.Init(32 + KYBER_KEY_BYTES)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return false;
+ }
+
+ if (peer_key.size() != 65 + KYBER768_CIPHERTEXT_BYTES) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+ return false;
+ }
+
+ // Set up a shared |BN_CTX| for P-256 operations.
+ UniquePtr<BN_CTX> bn_ctx(BN_CTX_new());
+ if (!bn_ctx) {
+ return false;
+ }
+
+ BN_CTXScope scope(bn_ctx.get());
+
+ UniquePtr<EC_GROUP> group;
+ group.reset(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ if (!group) {
+ return false;
+ }
+
+ // Parse peer point
+ UniquePtr<EC_POINT> peer_point(EC_POINT_new(group.get()));
+ UniquePtr<EC_POINT> result(EC_POINT_new(group.get()));
+ BIGNUM *x = BN_CTX_get(bn_ctx.get());
+ if (!peer_point || !result || !x) {
+ return false;
+ }
+
+ if (peer_key.empty() || peer_key[0] != POINT_CONVERSION_UNCOMPRESSED ||
+ !EC_POINT_oct2point(group.get(), peer_point.get(), peer_key.data(),
+ 65, bn_ctx.get())) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return false;
+ }
+
+ // Compute the x-coordinate of |peer_key| * |p256_private_key_|.
+ if (!EC_POINT_mul(group.get(), result.get(), NULL, peer_point.get(),
+ p256_private_key_.get(), bn_ctx.get()) ||
+ !EC_POINT_get_affine_coordinates_GFp(group.get(), result.get(), x, NULL,
+ bn_ctx.get())) {
+ return false;
+ }
+
+ // Encode the x-coordinate left-padded with zeros.
+ if (!secret.Init(32 + KYBER_KEY_BYTES) ||
+ !BN_bn2bin_padded(secret.data(), 32, x)) {
+ return false;
+ }
+
+ KYBER768_decap(secret.data() + 32, &kyber_private_key_,
+ peer_key.data() + 65, peer_key.size() - 65);
+
+ *out_secret = std::move(secret);
+ return true;
+ }
+
+ private:
+ UniquePtr<BIGNUM> p256_private_key_;
+ KYBER768_private_key kyber_private_key_;
+};
+
+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<uint8_t> *out_secret,
+ uint8_t *out_alert, Span<const uint8_t> peer_key) override {
+ Array<uint8_t> 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<uint8_t> *out_secret, uint8_t *out_alert,
+ Span<const uint8_t> peer_key) override {
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+
+ Array<uint8_t> 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<uint8_t> *out_secret,
+ uint8_t *out_alert, Span<const uint8_t> peer_key) override {
+ Array<uint8_t> 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<uint8_t> *out_secret, uint8_t *out_alert,
+ Span<const uint8_t> peer_key) override {
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+
+ Array<uint8_t> 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() {}
@@ -306,6 +717,12 @@ CONSTEXPR_ARRAY 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"},
+ {NID_P256Kyber768Draft00, SSL_CURVE_P256KYBER768DRAFT00,
+ "P256Kyber768Draft00", "P256Kyber768D00"}
};
} // namespace
@@ -332,6 +749,12 @@ UniquePtr<SSLKeyShare> SSLKeyShare::Create(uint16_t group_id) {
return UniquePtr<SSLKeyShare>(New<X25519KeyShare>());
case SSL_CURVE_CECPQ2:
return UniquePtr<SSLKeyShare>(New<CECPQ2KeyShare>());
+ case SSL_CURVE_X25519KYBER512DRAFT00:
+ return UniquePtr<SSLKeyShare>(New<X25519Kyber512Draft00KeyShare>());
+ case SSL_CURVE_X25519KYBER768DRAFT00:
+ return UniquePtr<SSLKeyShare>(New<X25519Kyber768Draft00KeyShare>());
+ case SSL_CURVE_P256KYBER768DRAFT00:
+ return UniquePtr<SSLKeyShare>(New<P256Kyber768Draft00KeyShare>());
default:
return nullptr;
}
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 637f4d5c6..a0d6d9922 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -399,7 +399,26 @@ 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 },
+ },
+ {
+ "P256Kyber768D00",
+ { SSL_CURVE_P256KYBER768DRAFT00 },
+ },
+ {
+ "P-256:P256Kyber768D00",
+ { SSL_CURVE_SECP256R1, SSL_CURVE_P256KYBER768DRAFT00 },
+ },
{
"P-256:P-384:P-521:X25519",
{
diff --git a/src/ssl/t1_lib.cc b/src/ssl/t1_lib.cc
index 342c17021..4d368d89d 100644
--- a/src/ssl/t1_lib.cc
+++ b/src/ssl/t1_lib.cc
@@ -205,7 +205,9 @@ 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 ||
+ id == SSL_CURVE_P256KYBER768DRAFT00;
}
bool ssl_client_hello_init(const SSL *ssl, SSL_CLIENT_HELLO *out,
@@ -334,8 +336,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;
@@ -401,7 +403,7 @@ bool tls1_set_curves_list(Array<uint16_t> *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/src/tool/speed.cc b/src/tool/speed.cc
index 1b89b42f5..9f02a6783 100644
--- a/src/tool/speed.cc
+++ b/src/tool/speed.cc
@@ -36,6 +36,7 @@
#include <openssl/ec_key.h>
#include <openssl/evp.h>
#include <openssl/hrss.h>
+#include <openssl/kyber.h>
#include <openssl/mem.h>
#include <openssl/nid.h>
#include <openssl/rand.h>
@@ -890,6 +891,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;
@@ -1371,6 +1482,8 @@ bool Speed(const std::vector<std::string> &args) {
!SpeedScrypt(selected) ||
!SpeedRSAKeyGen(selected) ||
!SpeedHRSS(selected) ||
+ !SpeedKyber512(selected) ||
+ !SpeedKyber768(selected) ||
!SpeedHashToCurve(selected) ||
!SpeedTrustToken("TrustToken-Exp1-Batch1", TRUST_TOKEN_experiment_v1(), 1,
selected) ||
--
2.40.0