3304 lines
108 KiB
Diff
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
|
|
|