diff --git a/BUILD.generated.bzl b/BUILD.generated.bzl index 738e1055f..9466757a2 100644 --- a/BUILD.generated.bzl +++ b/BUILD.generated.bzl @@ -253,7 +253,6 @@ crypto_internal_headers = [ "src/crypto/fipsmodule/tls/internal.h", "src/crypto/hrss/internal.h", "src/crypto/internal.h", - "src/crypto/kyber/internal.h", "src/crypto/lhash/internal.h", "src/crypto/obj/obj_dat.h", "src/crypto/pkcs7/internal.h", @@ -382,8 +381,8 @@ crypto_sources = [ "src/crypto/fipsmodule/fips_shared_support.c", "src/crypto/hpke/hpke.c", "src/crypto/hrss/hrss.c", - "src/crypto/kyber/keccak.c", - "src/crypto/kyber/kyber.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/BUILD.generated_tests.bzl b/BUILD.generated_tests.bzl index 92dec1e01..8f70dedc0 100644 --- a/BUILD.generated_tests.bzl +++ b/BUILD.generated_tests.bzl @@ -40,7 +40,6 @@ test_support_sources = [ "src/crypto/fipsmodule/tls/internal.h", "src/crypto/hrss/internal.h", "src/crypto/internal.h", - "src/crypto/kyber/internal.h", "src/crypto/lhash/internal.h", "src/crypto/obj/obj_dat.h", "src/crypto/pkcs7/internal.h", @@ -124,7 +123,6 @@ crypto_test_sources = [ "src/crypto/hpke/hpke_test.cc", "src/crypto/hrss/hrss_test.cc", "src/crypto/impl_dispatch_test.cc", - "src/crypto/kyber/kyber_test.cc", "src/crypto/lhash/lhash_test.cc", "src/crypto/obj/obj_test.cc", "src/crypto/pem/pem_test.cc", @@ -218,8 +216,6 @@ crypto_test_data = [ "src/crypto/fipsmodule/rand/ctrdrbg_vectors.txt", "src/crypto/hmac_extra/hmac_tests.txt", "src/crypto/hpke/hpke_test_vectors.txt", - "src/crypto/kyber/keccak_tests.txt", - "src/crypto/kyber/kyber_tests.txt", "src/crypto/pkcs8/test/empty_password.p12", "src/crypto/pkcs8/test/no_encryption.p12", "src/crypto/pkcs8/test/nss.p12", diff --git a/CMakeLists.txt b/CMakeLists.txt index faed2befa..931c0e3a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -375,8 +375,8 @@ add_library( src/crypto/fipsmodule/fips_shared_support.c src/crypto/hpke/hpke.c src/crypto/hrss/hrss.c - src/crypto/kyber/keccak.c - src/crypto/kyber/kyber.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 4c0048e1d..f6ea5c40f 100644 --- a/sources.json +++ b/sources.json @@ -111,8 +111,8 @@ "src/crypto/fipsmodule/fips_shared_support.c", "src/crypto/hpke/hpke.c", "src/crypto/hrss/hrss.c", - "src/crypto/kyber/keccak.c", - "src/crypto/kyber/kyber.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", @@ -549,7 +549,6 @@ "src/crypto/hpke/hpke_test.cc", "src/crypto/hrss/hrss_test.cc", "src/crypto/impl_dispatch_test.cc", - "src/crypto/kyber/kyber_test.cc", "src/crypto/lhash/lhash_test.cc", "src/crypto/obj/obj_test.cc", "src/crypto/pem/pem_test.cc", @@ -634,8 +633,6 @@ "src/crypto/fipsmodule/rand/ctrdrbg_vectors.txt", "src/crypto/hmac_extra/hmac_tests.txt", "src/crypto/hpke/hpke_test_vectors.txt", - "src/crypto/kyber/keccak_tests.txt", - "src/crypto/kyber/kyber_tests.txt", "src/crypto/pkcs8/test/empty_password.p12", "src/crypto/pkcs8/test/no_encryption.p12", "src/crypto/pkcs8/test/nss.p12", @@ -1060,4 +1057,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 cdb5ddca1..2052fa791 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -170,8 +170,8 @@ add_library( ex_data.c hpke/hpke.c hrss/hrss.c - kyber/keccak.c - kyber/kyber.c + kyber/kyber512.c + kyber/kyber768.c lhash/lhash.c mem.c obj/obj.c @@ -400,7 +400,6 @@ add_executable( hmac_extra/hmac_test.cc hrss/hrss_test.cc impl_dispatch_test.cc - kyber/kyber_test.cc lhash/lhash_test.cc obj/obj_test.cc pem/pem_test.cc diff --git a/src/crypto/kyber/internal.h b/src/crypto/kyber/internal.h deleted file mode 100644 index b3bfa86b8..000000000 --- a/src/crypto/kyber/internal.h +++ /dev/null @@ -1,91 +0,0 @@ -/* Copyright (c) 2023, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - -#ifndef OPENSSL_HEADER_CRYPTO_KYBER_INTERNAL_H -#define OPENSSL_HEADER_CRYPTO_KYBER_INTERNAL_H - -#include -#include - -#if defined(__cplusplus) -extern "C" { -#endif - - -// KYBER_ENCAP_ENTROPY is the number of bytes of uniformly random entropy -// necessary to encapsulate a secret. The entropy will be leaked to the -// decapsulating party. -#define KYBER_ENCAP_ENTROPY 32 - -// KYBER_GENERATE_KEY_ENTROPY is the number of bytes of uniformly random entropy -// necessary to generate a key. -#define KYBER_GENERATE_KEY_ENTROPY 64 - -struct BORINGSSL_keccak_st { - uint64_t state[25]; - size_t rate_bytes; - size_t offset; -}; - -enum boringssl_keccak_config_t { - boringssl_sha3_256, - boringssl_sha3_512, - boringssl_shake128, - boringssl_shake256, -}; - -// BORINGSSL_keccak hashes |in_len| bytes from |in| and writes |out_len| bytes -// of output to |out|. If the |config| specifies a fixed-output function, like -// SHA3-256, then |out_len| must be the correct length for that function. -OPENSSL_EXPORT void BORINGSSL_keccak(uint8_t *out, size_t out_len, - const uint8_t *in, size_t in_len, - enum boringssl_keccak_config_t config); - -// BORINGSSL_keccak_init absorbs |in_len| bytes from |in| and sets up |ctx| for -// squeezing. The |config| must specify a SHAKE variant, otherwise callers -// should use |BORINGSSL_keccak|. -OPENSSL_EXPORT void BORINGSSL_keccak_init( - struct BORINGSSL_keccak_st *ctx, const uint8_t *in, size_t in_len, - enum boringssl_keccak_config_t config); - -// BORINGSSL_keccak_squeeze writes |out_len| bytes to |out| from |ctx|. -OPENSSL_EXPORT void BORINGSSL_keccak_squeeze(struct BORINGSSL_keccak_st *ctx, - uint8_t *out, size_t out_len); - -// KYBER_generate_key_external_entropy is a deterministic function to create a -// pair of Kyber768 keys, using the supplied entropy. The entropy needs to be -// uniformly random generated. This function is should only be used for tests, -// regular callers should use the non-deterministic |KYBER_generate_key| -// directly. -OPENSSL_EXPORT void KYBER_generate_key_external_entropy( - uint8_t out_encoded_public_key[KYBER_PUBLIC_KEY_BYTES], - struct KYBER_private_key *out_private_key, - const uint8_t entropy[KYBER_GENERATE_KEY_ENTROPY]); - -// KYBER_encap_external_entropy is a deterministic function to encapsulate -// |out_shared_secret_len| bytes of |out_shared_secret| to |ciphertext|, using -// |KYBER_ENCAP_ENTROPY| bytes of |entropy| for randomization. The -// decapsulating side will be able to recover |entropy| in full. This -// function is should only be used for tests, regular callers should use the -// non-deterministic |KYBER_encap| directly. -OPENSSL_EXPORT void KYBER_encap_external_entropy( - uint8_t out_ciphertext[KYBER_CIPHERTEXT_BYTES], uint8_t *out_shared_secret, - size_t out_shared_secret_len, const struct KYBER_public_key *public_key, - const uint8_t entropy[KYBER_ENCAP_ENTROPY]); - -#if defined(__cplusplus) -} -#endif - -#endif // OPENSSL_HEADER_CRYPTO_KYBER_INTERNAL_H diff --git a/src/crypto/kyber/keccak.c b/src/crypto/kyber/keccak.c deleted file mode 100644 index f1c012d11..000000000 --- a/src/crypto/kyber/keccak.c +++ /dev/null @@ -1,204 +0,0 @@ -/* Copyright (c) 2023, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - -#include - -#include -#include - -#include "../internal.h" -#include "./internal.h" - - -// keccak_f implements the Keccak-1600 permutation as described at -// https://keccak.team/keccak_specs_summary.html. Each lane is represented as a -// 64-bit value and the 5×5 lanes are stored as an array in row-major order. -static void keccak_f(uint64_t state[25]) { - static const int kNumRounds = 24; - for (int round = 0; round < kNumRounds; round++) { - // θ step - uint64_t c[5]; - for (int x = 0; x < 5; x++) { - c[x] = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ - state[x + 20]; - } - - for (int x = 0; x < 5; x++) { - const uint64_t d = c[(x + 4) % 5] ^ CRYPTO_rotl_u64(c[(x + 1) % 5], 1); - for (int y = 0; y < 5; y++) { - state[y * 5 + x] ^= d; - } - } - - // ρ and π steps. - // - // These steps involve a mapping of the state matrix. Each input point, - // (x,y), is rotated and written to the point (y, 2x + 3y). In the Keccak - // pseudo-code a separate array is used because an in-place operation would - // overwrite some values that are subsequently needed. However, the mapping - // forms a trail through 24 of the 25 values so we can do it in place with - // only a single temporary variable. - // - // Start with (1, 0). The value here will be mapped and end up at (0, 2). - // That value will end up at (2, 1), then (1, 2), and so on. After 24 - // steps, 24 of the 25 values have been hit (as this mapping is injective) - // and the sequence will repeat. All that remains is to handle the element - // at (0, 0), but the rotation for that element is zero, and it goes to (0, - // 0), so we can ignore it. - static const uint8_t kIndexes[24] = {10, 7, 11, 17, 18, 3, 5, 16, - 8, 21, 24, 4, 15, 23, 19, 13, - 12, 2, 20, 14, 22, 9, 6, 1}; - static const uint8_t kRotations[24] = {1, 3, 6, 10, 15, 21, 28, 36, - 45, 55, 2, 14, 27, 41, 56, 8, - 25, 43, 62, 18, 39, 61, 20, 44}; - uint64_t prev_value = state[1]; - for (int i = 0; i < 24; i++) { - const uint64_t value = CRYPTO_rotl_u64(prev_value, kRotations[i]); - const size_t index = kIndexes[i]; - prev_value = state[index]; - state[index] = value; - } - - // χ step - for (int y = 0; y < 5; y++) { - const int row_index = 5 * y; - const uint64_t orig_x0 = state[row_index]; - const uint64_t orig_x1 = state[row_index + 1]; - state[row_index] ^= ~orig_x1 & state[row_index + 2]; - state[row_index + 1] ^= ~state[row_index + 2] & state[row_index + 3]; - state[row_index + 2] ^= ~state[row_index + 3] & state[row_index + 4]; - state[row_index + 3] ^= ~state[row_index + 4] & orig_x0; - state[row_index + 4] ^= ~orig_x0 & orig_x1; - } - - // ι step - // - // From https://keccak.team/files/Keccak-reference-3.0.pdf, section - // 1.2, the round constants are based on the output of a LFSR. Thus, as - // suggested in the appendix of of - // https://keccak.team/keccak_specs_summary.html, the values are - // simply encoded here. - static const uint64_t kRoundConstants[24] = { - 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, - 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, - 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, - 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, - 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, - 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, - 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, - 0x8000000000008080, 0x0000000080000001, 0x8000000080008008, - }; - - state[0] ^= kRoundConstants[round]; - } -} - -static void keccak_init(struct BORINGSSL_keccak_st *ctx, - size_t *out_required_out_len, const uint8_t *in, - size_t in_len, enum boringssl_keccak_config_t config) { - size_t capacity_bytes; - uint8_t terminator; - switch (config) { - case boringssl_sha3_256: - capacity_bytes = 512 / 8; - *out_required_out_len = 32; - terminator = 0x06; - break; - case boringssl_sha3_512: - capacity_bytes = 1024 / 8; - *out_required_out_len = 64; - terminator = 0x06; - break; - case boringssl_shake128: - capacity_bytes = 256 / 8; - *out_required_out_len = 0; - terminator = 0x1f; - break; - case boringssl_shake256: - capacity_bytes = 512 / 8; - *out_required_out_len = 0; - terminator = 0x1f; - break; - default: - abort(); - } - - OPENSSL_memset(ctx, 0, sizeof(*ctx)); - ctx->rate_bytes = 200 - capacity_bytes; - assert(ctx->rate_bytes % 8 == 0); - const size_t rate_words = ctx->rate_bytes / 8; - - while (in_len >= ctx->rate_bytes) { - for (size_t i = 0; i < rate_words; i++) { - ctx->state[i] ^= CRYPTO_load_u64_le(in + 8 * i); - } - keccak_f(ctx->state); - in += ctx->rate_bytes; - in_len -= ctx->rate_bytes; - } - - // XOR the final block. Accessing |ctx->state| as a |uint8_t*| is allowed by - // strict aliasing because we require |uint8_t| to be a character type. - uint8_t *state_bytes = (uint8_t *)ctx->state; - assert(in_len < ctx->rate_bytes); - for (size_t i = 0; i < in_len; i++) { - state_bytes[i] ^= in[i]; - } - state_bytes[in_len] ^= terminator; - state_bytes[ctx->rate_bytes - 1] ^= 0x80; - keccak_f(ctx->state); -} - -void BORINGSSL_keccak(uint8_t *out, size_t out_len, const uint8_t *in, - size_t in_len, enum boringssl_keccak_config_t config) { - struct BORINGSSL_keccak_st ctx; - size_t required_out_len; - keccak_init(&ctx, &required_out_len, in, in_len, config); - if (required_out_len != 0 && out_len != required_out_len) { - abort(); - } - BORINGSSL_keccak_squeeze(&ctx, out, out_len); -} - -void BORINGSSL_keccak_init(struct BORINGSSL_keccak_st *ctx, const uint8_t *in, - size_t in_len, - enum boringssl_keccak_config_t config) { - size_t required_out_len; - keccak_init(ctx, &required_out_len, in, in_len, config); - if (required_out_len != 0) { - abort(); - } -} - -void BORINGSSL_keccak_squeeze(struct BORINGSSL_keccak_st *ctx, uint8_t *out, - size_t out_len) { - // Accessing |ctx->state| as a |uint8_t*| is allowed by strict aliasing - // because we require |uint8_t| to be a character type. - const uint8_t *state_bytes = (const uint8_t *)ctx->state; - while (out_len) { - size_t remaining = ctx->rate_bytes - ctx->offset; - size_t todo = out_len; - if (todo > remaining) { - todo = remaining; - } - OPENSSL_memcpy(out, &state_bytes[ctx->offset], todo); - out += todo; - out_len -= todo; - ctx->offset += todo; - if (ctx->offset == ctx->rate_bytes) { - keccak_f(ctx->state); - ctx->offset = 0; - } - } -} diff --git a/src/crypto/kyber/kyber.c b/src/crypto/kyber/kyber.c index 776c085f9..ccb5b3d9b 100644 --- a/src/crypto/kyber/kyber.c +++ b/src/crypto/kyber/kyber.c @@ -1,833 +1,2426 @@ -/* Copyright (c) 2023, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +// 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 +// - Mitigated timing sidechannels (Kyberslash 1 and 2). +// (Note that these do not affect ephemeral usage as in TLS.) +// +// 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. -#include +#ifndef KYBER_K +#error "Don't compile this file direcly" +#endif -#include -#include +#include +#include -#include -#include +#include +#include +#include #include "../internal.h" -#include "./internal.h" - - -// See -// https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf - -#define DEGREE 256 -#define RANK 3 - -static const size_t kBarrettMultiplier = 5039; -static const unsigned kBarrettShift = 24; -static const uint16_t kPrime = 3329; -static const int kLog2Prime = 12; -static const uint16_t kHalfPrime = (/*kPrime=*/3329 - 1) / 2; -static const int kDU = 10; -static const int kDV = 4; -// kInverseDegree is 128^-1 mod 3329; 128 because kPrime does not have a 512th -// root of unity. -static const uint16_t kInverseDegree = 3303; -static const size_t kEncodedVectorSize = - (/*kLog2Prime=*/12 * DEGREE / 8) * RANK; -static const size_t kCompressedVectorSize = /*kDU=*/10 * RANK * DEGREE / 8; - -typedef struct scalar { - // On every function entry and exit, 0 <= c < kPrime. - uint16_t c[DEGREE]; -} scalar; - -typedef struct vector { - scalar v[RANK]; -} vector; - -typedef struct matrix { - scalar v[RANK][RANK]; -} matrix; - -// This bit of Python will be referenced in some of the following comments: -// -// p = 3329 -// -// def bitreverse(i): -// ret = 0 -// for n in range(7): -// bit = i & 1 -// ret <<= 1 -// ret |= bit -// i >>= 1 -// return ret - -// kNTTRoots = [pow(17, bitreverse(i), p) for i in range(128)] -static const uint16_t kNTTRoots[128] = { - 1, 1729, 2580, 3289, 2642, 630, 1897, 848, 1062, 1919, 193, 797, - 2786, 3260, 569, 1746, 296, 2447, 1339, 1476, 3046, 56, 2240, 1333, - 1426, 2094, 535, 2882, 2393, 2879, 1974, 821, 289, 331, 3253, 1756, - 1197, 2304, 2277, 2055, 650, 1977, 2513, 632, 2865, 33, 1320, 1915, - 2319, 1435, 807, 452, 1438, 2868, 1534, 2402, 2647, 2617, 1481, 648, - 2474, 3110, 1227, 910, 17, 2761, 583, 2649, 1637, 723, 2288, 1100, - 1409, 2662, 3281, 233, 756, 2156, 3015, 3050, 1703, 1651, 2789, 1789, - 1847, 952, 1461, 2687, 939, 2308, 2437, 2388, 733, 2337, 268, 641, - 1584, 2298, 2037, 3220, 375, 2549, 2090, 1645, 1063, 319, 2773, 757, - 2099, 561, 2466, 2594, 2804, 1092, 403, 1026, 1143, 2150, 2775, 886, - 1722, 1212, 1874, 1029, 2110, 2935, 885, 2154, -}; -// kInverseNTTRoots = [pow(17, -bitreverse(i), p) for i in range(128)] -static const uint16_t kInverseNTTRoots[128] = { - 1, 1600, 40, 749, 2481, 1432, 2699, 687, 1583, 2760, 69, 543, - 2532, 3136, 1410, 2267, 2508, 1355, 450, 936, 447, 2794, 1235, 1903, - 1996, 1089, 3273, 283, 1853, 1990, 882, 3033, 2419, 2102, 219, 855, - 2681, 1848, 712, 682, 927, 1795, 461, 1891, 2877, 2522, 1894, 1010, - 1414, 2009, 3296, 464, 2697, 816, 1352, 2679, 1274, 1052, 1025, 2132, - 1573, 76, 2998, 3040, 1175, 2444, 394, 1219, 2300, 1455, 2117, 1607, - 2443, 554, 1179, 2186, 2303, 2926, 2237, 525, 735, 863, 2768, 1230, - 2572, 556, 3010, 2266, 1684, 1239, 780, 2954, 109, 1292, 1031, 1745, - 2688, 3061, 992, 2596, 941, 892, 1021, 2390, 642, 1868, 2377, 1482, - 1540, 540, 1678, 1626, 279, 314, 1173, 2573, 3096, 48, 667, 1920, - 2229, 1041, 2606, 1692, 680, 2746, 568, 3312, -}; +#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 -// kModRoots = [pow(17, 2*bitreverse(i) + 1, p) for i in range(128)] -static const uint16_t kModRoots[128] = { - 17, 3312, 2761, 568, 583, 2746, 2649, 680, 1637, 1692, 723, 2606, - 2288, 1041, 1100, 2229, 1409, 1920, 2662, 667, 3281, 48, 233, 3096, - 756, 2573, 2156, 1173, 3015, 314, 3050, 279, 1703, 1626, 1651, 1678, - 2789, 540, 1789, 1540, 1847, 1482, 952, 2377, 1461, 1868, 2687, 642, - 939, 2390, 2308, 1021, 2437, 892, 2388, 941, 733, 2596, 2337, 992, - 268, 3061, 641, 2688, 1584, 1745, 2298, 1031, 2037, 1292, 3220, 109, - 375, 2954, 2549, 780, 2090, 1239, 1645, 1684, 1063, 2266, 319, 3010, - 2773, 556, 757, 2572, 2099, 1230, 561, 2768, 2466, 863, 2594, 735, - 2804, 525, 1092, 2237, 403, 2926, 1026, 2303, 1143, 2186, 2150, 1179, - 2775, 554, 886, 2443, 1722, 1607, 1212, 2117, 1874, 1455, 1029, 2300, - 2110, 1219, 2935, 394, 885, 2444, 2154, 1175, -}; +#define public_key KYBER_NAMESPACE(public_key) +#define private_key KYBER_NAMESPACE(private_key) -// reduce_once reduces 0 <= x < 2*kPrime, mod kPrime. -static uint16_t reduce_once(uint16_t x) { - assert(x < 2 * kPrime); - const uint16_t subtracted = x - kPrime; - uint16_t mask = 0u - (subtracted >> 15); - // On Aarch64, omitting a |value_barrier_u16| results in a 2x speedup of Kyber - // overall and Clang still produces constant-time code using `csel`. On other - // platforms & compilers on godbolt that we care about, this code also - // produces constant-time output. - return (mask & x) | (~mask & subtracted); -} - -// constant time reduce x mod kPrime using Barrett reduction. x must be less -// than kPrime + 2×kPrime². -static uint16_t reduce(uint32_t x) { - assert(x < kPrime + 2u * kPrime * kPrime); - uint64_t product = (uint64_t)x * kBarrettMultiplier; - uint32_t quotient = product >> kBarrettShift; - uint32_t remainder = x - quotient * kPrime; - return reduce_once(remainder); -} - -static void scalar_zero(scalar *out) { OPENSSL_memset(out, 0, sizeof(*out)); } - -static void vector_zero(vector *out) { OPENSSL_memset(out, 0, sizeof(*out)); } - -// In place number theoretic transform of a given scalar. -// Note that Kyber's kPrime 3329 does not have a 512th root of unity, so this -// transform leaves off the last iteration of the usual FFT code, with the 128 -// relevant roots of unity being stored in |kNTTRoots|. This means the output -// should be seen as 128 elements in GF(3329^2), with the coefficients of the -// elements being consecutive entries in |s->c|. -static void scalar_ntt(scalar *s) { - int offset = DEGREE; - // `int` is used here because using `size_t` throughout caused a ~5% slowdown - // with Clang 14 on Aarch64. - for (int step = 1; step < DEGREE / 2; step <<= 1) { - offset >>= 1; - int k = 0; - for (int i = 0; i < step; i++) { - const uint32_t step_root = kNTTRoots[i + step]; - for (int j = k; j < k + offset; j++) { - uint16_t odd = reduce(step_root * s->c[j + offset]); - uint16_t even = s->c[j]; - s->c[j] = reduce_once(odd + even); - s->c[j + offset] = reduce_once(even - odd + kPrime); - } - k += 2 * offset; +#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 int 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_absorb(keccak_state *state, const uint8_t *in, size_t inlen); +static void shake256_finalize(keccak_state *state); +static void shake256_init(keccak_state *state); + +static void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen); +static void sha3_256(uint8_t h[32], const uint8_t *in, size_t inlen); +static void sha3_512(uint8_t h[64], const uint8_t *in, size_t inlen); + +// +// symmetric.h +// + +typedef keccak_state xof_state; + +static void kyber_shake128_absorb(keccak_state *s, + const uint8_t seed[KYBER_SYMBYTES], + uint8_t x, + uint8_t y); + +static void kyber_shake256_prf(uint8_t *out, size_t outlen, const uint8_t key[KYBER_SYMBYTES], uint8_t nonce); + +#define XOF_BLOCKBYTES SHAKE128_RATE + +#define hash_h(OUT, IN, INBYTES) sha3_256(OUT, IN, INBYTES) +#define hash_g(OUT, IN, INBYTES) sha3_512(OUT, IN, INBYTES) +#define xof_absorb(STATE, SEED, X, Y) kyber_shake128_absorb(STATE, SEED, X, Y) +#define xof_squeezeblocks(OUT, OUTBLOCKS, STATE) shake128_squeezeblocks(OUT, OUTBLOCKS, STATE) +#define prf(OUT, OUTBYTES, KEY, NONCE) kyber_shake256_prf(OUT, OUTBYTES, KEY, NONCE) +#define kdf(OUT, IN, INBYTES) shake256(OUT, KYBER_SSBYTES, IN, INBYTES) + + +// +// verify.c +// + +/************************************************* +* Name: verify +* +* Description: Compare two arrays for equality in constant time. +* +* Arguments: const uint8_t *a: pointer to first byte array +* const uint8_t *b: pointer to second byte array +* size_t len: length of the byte arrays +* +* Returns 0 if the byte arrays are equal, 1 otherwise +**************************************************/ +static int verify(const uint8_t *a, const uint8_t *b, size_t len) +{ + size_t i; + uint8_t r = 0; + + for(i=0;i> 63; +} + +/************************************************* +* Name: cmov +* +* Description: Copy len bytes from x to r if b is 1; +* don't modify x if b is 0. Requires b to be in {0,1}; +* assumes two's complement representation of negative integers. +* Runs in constant time. +* +* Arguments: uint8_t *r: pointer to output byte array +* const uint8_t *x: pointer to input byte array +* size_t len: Amount of bytes to be copied +* uint8_t b: Condition bit; has to be in {0,1} +**************************************************/ +static void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b) +{ + size_t i; + + b = -b; + for(i=0;i> 16; + return t; +} + +/************************************************* +* Name: barrett_reduce +* +* Description: Barrett reduction; given a 16-bit integer a, computes +* centered representative congruent to a mod q in {-(q-1)/2,...,(q-1)/2} +* +* Arguments: - int16_t a: input integer to be reduced +* +* Returns: integer in {-(q-1)/2,...,(q-1)/2} congruent to a modulo q. +**************************************************/ +static int16_t barrett_reduce(int16_t a) { + int16_t t; + const int16_t v = ((1<<26) + KYBER_Q/2)/KYBER_Q; + + t = ((int32_t)v*a + (1<<25)) >> 26; + t *= KYBER_Q; + return a - t; +} + +// +// cbd.c +// + +/************************************************* +* Name: load32_littleendian +* +* Description: load 4 bytes into a 32-bit integer +* in little-endian order +* +* Arguments: - const uint8_t *x: pointer to input byte array +* +* Returns 32-bit unsigned integer loaded from x +**************************************************/ +static uint32_t load32_littleendian(const uint8_t x[4]) +{ + uint32_t r; + r = (uint32_t)x[0]; + r |= (uint32_t)x[1] << 8; + r |= (uint32_t)x[2] << 16; + r |= (uint32_t)x[3] << 24; + return r; +} + +/************************************************* +* Name: load24_littleendian +* +* Description: load 3 bytes into a 32-bit integer +* in little-endian order. +* This function is only needed for Kyber-512 +* +* Arguments: - const uint8_t *x: pointer to input byte array +* +* Returns 32-bit unsigned integer loaded from x (most significant byte is zero) +**************************************************/ +#if KYBER_ETA1 == 3 +static uint32_t load24_littleendian(const uint8_t x[3]) +{ + uint32_t r; + r = (uint32_t)x[0]; + r |= (uint32_t)x[1] << 8; + r |= (uint32_t)x[2] << 16; + return r; +} +#endif + + +/************************************************* +* Name: cbd2 +* +* Description: Given an array of uniformly random bytes, compute +* polynomial with coefficients distributed according to +* a centered binomial distribution with parameter eta=2 +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *buf: pointer to input byte array +**************************************************/ +static void cbd2(poly *r, const uint8_t buf[2*KYBER_N/4]) +{ + unsigned int i,j; + uint32_t t,d; + int16_t a,b; + + for(i=0;i>1) & 0x55555555; + + for(j=0;j<8;j++) { + a = (d >> (4*j+0)) & 0x3; + b = (d >> (4*j+2)) & 0x3; + r->coeffs[8*i+j] = a - b; + } + } +} + +/************************************************* +* Name: cbd3 +* +* Description: Given an array of uniformly random bytes, compute +* polynomial with coefficients distributed according to +* a centered binomial distribution with parameter eta=3. +* This function is only needed for Kyber-512 +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *buf: pointer to input byte array +**************************************************/ +#if KYBER_ETA1 == 3 +static void cbd3(poly *r, const uint8_t buf[3*KYBER_N/4]) +{ + unsigned int i,j; + uint32_t t,d; + int16_t a,b; + + for(i=0;i>1) & 0x00249249; + d += (t>>2) & 0x00249249; + + for(j=0;j<4;j++) { + a = (d >> (6*j+0)) & 0x7; + b = (d >> (6*j+3)) & 0x7; + r->coeffs[4*i+j] = a - b; } } } +#endif + +static void poly_cbd_eta1(poly *r, const uint8_t buf[KYBER_ETA1*KYBER_N/4]) +{ +#if KYBER_ETA1 == 2 + cbd2(r, buf); +#elif KYBER_ETA1 == 3 + cbd3(r, buf); +#else +#error "This implementation requires eta1 in {2,3}" +#endif +} -static void vector_ntt(vector *a) { - for (int i = 0; i < RANK; i++) { - scalar_ntt(&a->v[i]); +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); +} -// In place inverse number theoretic transform of a given scalar, with pairs of -// entries of s->v being interpreted as elements of GF(3329^2). Just as with the -// number theoretic transform, this leaves off the first step of the normal iFFT -// to account for the fact that 3329 does not have a 512th root of unity, using -// the precomputed 128 roots of unity stored in |kInverseNTTRoots|. -static void scalar_inverse_ntt(scalar *s) { - int step = DEGREE / 2; - // `int` is used here because using `size_t` throughout caused a ~5% slowdown - // with Clang 14 on Aarch64. - for (int offset = 2; offset < DEGREE; offset <<= 1) { - step >>= 1; - int k = 0; - for (int i = 0; i < step; i++) { - uint32_t step_root = kInverseNTTRoots[i + step]; - for (int j = k; j < k + offset; j++) { - uint16_t odd = s->c[j + offset]; - uint16_t even = s->c[j]; - s->c[j] = reduce_once(odd + even); - s->c[j + offset] = reduce(step_root * (even - odd + kPrime)); +/************************************************* +* 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; } - k += 2 * offset; } } - for (int i = 0; i < DEGREE; i++) { - s->c[i] = reduce(s->c[i] * kInverseDegree); - } } -static void vector_inverse_ntt(vector *a) { - for (int i = 0; i < RANK; i++) { - scalar_inverse_ntt(&a->v[i]); +/************************************************* +* 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]); } -static void scalar_add(scalar *lhs, const scalar *rhs) { - for (int i = 0; i < DEGREE; i++) { - lhs->c[i] = reduce_once(lhs->c[i] + rhs->c[i]); +// +// 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; + uint32_t d0; + uint8_t t[8]; + +#if (KYBER_POLYCOMPRESSEDBYTES == 128) + for(i=0;icoeffs[8*i+j]; + u += (u >> 15) & KYBER_Q; + d0 = u << 4; + d0 += 1665; + d0 *= 80635; + d0 >>= 28; + t[j] = d0 & 0xf; + } + + r[0] = t[0] | (t[1] << 4); + r[1] = t[2] | (t[3] << 4); + r[2] = t[4] | (t[5] << 4); + r[3] = t[6] | (t[7] << 4); + r += 4; } +#elif (KYBER_POLYCOMPRESSEDBYTES == 160) + for(i=0;icoeffs[8*i+j]; + u += (u >> 15) & KYBER_Q; + d0 = u << 5; + d0 += 1664; + d0 *= 40318; + d0 >>= 27; + t[j] = d0 & 0x1f; + } + + 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 } -static void scalar_sub(scalar *lhs, const scalar *rhs) { - for (int i = 0; i < DEGREE; i++) { - lhs->c[i] = reduce_once(lhs->c[i] - rhs->c[i] + kPrime); +/************************************************* +* Name: poly_decompress +* +* Description: De-serialization and subsequent decompression of a polynomial; +* approximate inverse of poly_compress +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *a: pointer to input byte array +* (of length KYBER_POLYCOMPRESSEDBYTES bytes) +**************************************************/ +static void poly_decompress(poly *r, const uint8_t a[KYBER_POLYCOMPRESSEDBYTES]) +{ + unsigned int i; + +#if (KYBER_POLYCOMPRESSEDBYTES == 128) + for(i=0;icoeffs[2*i+0] = (((uint16_t)(a[0] & 15)*KYBER_Q) + 8) >> 4; + r->coeffs[2*i+1] = (((uint16_t)(a[0] >> 4)*KYBER_Q) + 8) >> 4; + a += 1; + } +#elif (KYBER_POLYCOMPRESSEDBYTES == 160) + unsigned int j; + uint8_t t[8]; + for(i=0;i> 0); + t[1] = (a[0] >> 5) | (a[1] << 3); + t[2] = (a[1] >> 2); + t[3] = (a[1] >> 7) | (a[2] << 1); + t[4] = (a[2] >> 4) | (a[3] << 4); + t[5] = (a[3] >> 1); + t[6] = (a[3] >> 6) | (a[4] << 2); + t[7] = (a[4] >> 3); + a += 5; + + for(j=0;j<8;j++) + r->coeffs[8*i+j] = ((uint32_t)(t[j] & 31)*KYBER_Q + 16) >> 5; } +#else +#error "KYBER_POLYCOMPRESSEDBYTES needs to be in {128, 160}" +#endif } -// Multiplying two scalars in the number theoretically transformed state. Since -// 3329 does not have a 512th root of unity, this means we have to interpret -// the 2*ith and (2*i+1)th entries of the scalar as elements of GF(3329)[X]/(X^2 -// - 17^(2*bitreverse(i)+1)) The value of 17^(2*bitreverse(i)+1) mod 3329 is -// stored in the precomputed |kModRoots| table. Note that our Barrett transform -// only allows us to multipy two reduced numbers together, so we need some -// intermediate reduction steps, even if an uint64_t could hold 3 multiplied -// numbers. -static void scalar_mult(scalar *out, const scalar *lhs, const scalar *rhs) { - for (int i = 0; i < DEGREE / 2; i++) { - uint32_t real_real = (uint32_t)lhs->c[2 * i] * rhs->c[2 * i]; - uint32_t img_img = (uint32_t)lhs->c[2 * i + 1] * rhs->c[2 * i + 1]; - uint32_t real_img = (uint32_t)lhs->c[2 * i] * rhs->c[2 * i + 1]; - uint32_t img_real = (uint32_t)lhs->c[2 * i + 1] * rhs->c[2 * i]; - out->c[2 * i] = - reduce(real_real + (uint32_t)reduce(img_img) * kModRoots[i]); - out->c[2 * i + 1] = reduce(img_real + real_img); +/************************************************* +* Name: poly_tobytes +* +* Description: Serialization of a polynomial +* +* Arguments: - uint8_t *r: pointer to output byte array +* (needs space for KYBER_POLYBYTES bytes) +* - const poly *a: pointer to input polynomial +**************************************************/ +static void poly_tobytes(uint8_t r[KYBER_POLYBYTES], const poly *a) +{ + unsigned int i; + uint16_t t0, t1; + + for(i=0;icoeffs[2*i]; + t0 += ((int16_t)t0 >> 15) & KYBER_Q; + t1 = a->coeffs[2*i+1]; + t1 += ((int16_t)t1 >> 15) & KYBER_Q; + r[3*i+0] = (t0 >> 0); + r[3*i+1] = (t0 >> 8) | (t1 << 4); + r[3*i+2] = (t1 >> 4); } } -static void vector_add(vector *lhs, const vector *rhs) { - for (int i = 0; i < RANK; i++) { - scalar_add(&lhs->v[i], &rhs->v[i]); +/************************************************* +* Name: poly_frombytes +* +* Description: De-serialization of a polynomial; +* inverse of poly_tobytes +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *a: pointer to input byte array +* (of KYBER_POLYBYTES bytes) +**************************************************/ +static void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES]) +{ + unsigned int i; + for(i=0;icoeffs[2*i] = ((a[3*i+0] >> 0) | ((uint16_t)a[3*i+1] << 8)) & 0xFFF; + r->coeffs[2*i+1] = ((a[3*i+1] >> 4) | ((uint16_t)a[3*i+2] << 4)) & 0xFFF; } } -static void matrix_mult(vector *out, const matrix *m, const vector *a) { - vector_zero(out); - for (int i = 0; i < RANK; i++) { - for (int j = 0; j < RANK; j++) { - scalar product; - scalar_mult(&product, &m->v[i][j], &a->v[j]); - scalar_add(&out->v[i], &product); +/************************************************* +* Name: poly_frommsg +* +* Description: Convert 32-byte message to polynomial +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *msg: pointer to input message +**************************************************/ +static void poly_frommsg(poly *r, const uint8_t msg[KYBER_INDCPA_MSGBYTES]) +{ + unsigned int i,j; + int16_t mask; + +#if (KYBER_INDCPA_MSGBYTES != KYBER_N/8) +#error "KYBER_INDCPA_MSGBYTES must be equal to KYBER_N/8 bytes!" +#endif + + for(i=0;i> j)&1); + r->coeffs[8*i+j] = mask & ((KYBER_Q+1)/2); } } } -static void matrix_mult_transpose(vector *out, const matrix *m, - const vector *a) { - vector_zero(out); - for (int i = 0; i < RANK; i++) { - for (int j = 0; j < RANK; j++) { - scalar product; - scalar_mult(&product, &m->v[j][i], &a->v[j]); - scalar_add(&out->v[i], &product); +/************************************************* +* 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; + uint32_t t; + + for(i=0;icoeffs[8*i+j]; + t <<= 1; + t += 1665; + t *= 80635; + t >>= 28; + t &= 1; + msg[i] |= t << j; } } } -static void scalar_inner_product(scalar *out, const vector *lhs, - const vector *rhs) { - scalar_zero(out); - for (int i = 0; i < RANK; i++) { - scalar product; - scalar_mult(&product, &lhs->v[i], &rhs->v[i]); - scalar_add(out, &product); - } +/************************************************* +* 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); } -// Algorithm 1 of the Kyber spec. Rejection samples a Keccak stream to get -// uniformly distributed elements. This is used for matrix expansion and only -// operates on public inputs. -static void scalar_from_keccak_vartime(scalar *out, - struct BORINGSSL_keccak_st *keccak_ctx) { - assert(keccak_ctx->offset == 0); - assert(keccak_ctx->rate_bytes == 168); - static_assert(168 % 3 == 0, "block and coefficient boundaries do not align"); - - int done = 0; - while (done < DEGREE) { - uint8_t block[168]; - BORINGSSL_keccak_squeeze(keccak_ctx, block, sizeof(block)); - for (size_t i = 0; i < sizeof(block) && done < DEGREE; i += 3) { - uint16_t d1 = block[i] + 256 * (block[i + 1] % 16); - uint16_t d2 = block[i + 1] / 16 + 16 * block[i + 2]; - if (d1 < kPrime) { - out->c[done++] = d1; - } - if (d2 < kPrime && done < DEGREE) { - out->c[done++] = d2; - } - } - } +/************************************************* +* 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); } -// Algorithm 2 of the Kyber spec, with eta fixed to two and the PRF call -// included. Creates binominally distributed elements by sampling 2*|eta| bits, -// and setting the coefficient to the count of the first bits minus the count of -// the second bits, resulting in a centered binomial distribution. Since eta is -// two this gives -2/2 with a probability of 1/16, -1/1 with probability 1/4, -// and 0 with probability 3/8. -static void scalar_centered_binomial_distribution_eta_2_with_prf( - scalar *out, const uint8_t input[33]) { - uint8_t entropy[128]; - static_assert(sizeof(entropy) == 2 * /*kEta=*/2 * DEGREE / 8, ""); - BORINGSSL_keccak(entropy, sizeof(entropy), input, 33, boringssl_shake256); - - for (int i = 0; i < DEGREE; i += 2) { - uint8_t byte = entropy[i / 2]; - - uint16_t value = kPrime; - value += (byte & 1) + ((byte >> 1) & 1); - value -= ((byte >> 2) & 1) + ((byte >> 3) & 1); - out->c[i] = reduce_once(value); - - byte >>= 4; - value = kPrime; - value += (byte & 1) + ((byte >> 1) & 1); - value -= ((byte >> 2) & 1) + ((byte >> 3) & 1); - out->c[i + 1] = reduce_once(value); - } + +/************************************************* +* 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); } -// Generates a secret vector by using -// |scalar_centered_binomial_distribution_eta_2_with_prf|, using the given seed -// appending and incrementing |counter| for entry of the vector. -static void vector_generate_secret_eta_2(vector *out, uint8_t *counter, - const uint8_t seed[32]) { - uint8_t input[33]; - OPENSSL_memcpy(input, seed, 32); - for (int i = 0; i < RANK; i++) { - input[32] = (*counter)++; - scalar_centered_binomial_distribution_eta_2_with_prf(&out->v[i], input); - } +/************************************************* +* 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); } -// Expands the matrix of a seed for key generation and for encaps-CPA. -static void matrix_expand(matrix *out, const uint8_t rho[32]) { - uint8_t input[34]; - OPENSSL_memcpy(input, rho, 32); - for (int i = 0; i < RANK; i++) { - for (int j = 0; j < RANK; j++) { - input[32] = i; - input[33] = j; - struct BORINGSSL_keccak_st keccak_ctx; - BORINGSSL_keccak_init(&keccak_ctx, input, sizeof(input), - boringssl_shake128); - scalar_from_keccak_vartime(&out->v[i][j], &keccak_ctx); - } +/************************************************* +* Name: poly_basemul_montgomery +* +* Description: Multiplication of two polynomials in NTT domain +* +* Arguments: - poly *r: pointer to output polynomial +* - const poly *a: pointer to first input polynomial +* - const poly *b: pointer to second input polynomial +**************************************************/ +static void poly_basemul_montgomery(poly *r, const poly *a, const poly *b) +{ + unsigned int i; + for(i=0;icoeffs[4*i], &a->coeffs[4*i], &b->coeffs[4*i], zetas[64+i]); + basemul(&r->coeffs[4*i+2], &a->coeffs[4*i+2], &b->coeffs[4*i+2], -zetas[64+i]); } } -static const uint8_t kMasks[8] = {0x01, 0x03, 0x07, 0x0f, - 0x1f, 0x3f, 0x7f, 0xff}; - -static void scalar_encode(uint8_t *out, const scalar *s, int bits) { - assert(bits <= (int)sizeof(*s->c) * 8 && bits != 1); - - uint8_t out_byte = 0; - int out_byte_bits = 0; - - for (int i = 0; i < DEGREE; i++) { - uint16_t element = s->c[i]; - int element_bits_done = 0; - - while (element_bits_done < bits) { - int chunk_bits = bits - element_bits_done; - int out_bits_remaining = 8 - out_byte_bits; - if (chunk_bits >= out_bits_remaining) { - chunk_bits = out_bits_remaining; - out_byte |= (element & kMasks[chunk_bits - 1]) << out_byte_bits; - *out = out_byte; - out++; - out_byte_bits = 0; - out_byte = 0; - } else { - out_byte |= (element & kMasks[chunk_bits - 1]) << out_byte_bits; - out_byte_bits += chunk_bits; +/************************************************* +* Name: poly_tomont +* +* Description: Inplace conversion of all coefficients of a polynomial +* from normal domain to Montgomery domain +* +* Arguments: - poly *r: pointer to input/output polynomial +**************************************************/ +static void poly_tomont(poly *r) +{ + unsigned int i; + const int16_t f = (1ULL << 32) % KYBER_Q; + for(i=0;icoeffs[i] = montgomery_reduce((int32_t)r->coeffs[i]*f); +} + +/************************************************* +* Name: poly_reduce +* +* Description: Applies Barrett reduction to all coefficients of a polynomial +* for details of the Barrett reduction see comments in reduce.c +* +* Arguments: - poly *r: pointer to input/output polynomial +**************************************************/ +static void poly_reduce(poly *r) +{ + unsigned int i; + for(i=0;icoeffs[i] = barrett_reduce(r->coeffs[i]); +} + +/************************************************* +* Name: poly_add +* +* Description: Add two polynomials; no modular reduction is performed +* +* Arguments: - poly *r: pointer to output polynomial +* - const poly *a: pointer to first input polynomial +* - const poly *b: pointer to second input polynomial +**************************************************/ +static void poly_add(poly *r, const poly *a, const poly *b) +{ + unsigned int i; + for(i=0;icoeffs[i] = a->coeffs[i] + b->coeffs[i]; +} + +/************************************************* +* Name: poly_sub +* +* Description: Subtract two polynomials; no modular reduction is performed +* +* Arguments: - poly *r: pointer to output polynomial +* - const poly *a: pointer to first input polynomial +* - const poly *b: pointer to second input polynomial +**************************************************/ +static void poly_sub(poly *r, const poly *a, const poly *b) +{ + unsigned int i; + for(i=0;icoeffs[i] = a->coeffs[i] - b->coeffs[i]; +} + +// +// polyvec.c +// + +/************************************************* +* Name: polyvec_compress +* +* Description: Compress and serialize vector of polynomials +* +* Arguments: - uint8_t *r: pointer to output byte array +* (needs space for KYBER_POLYVECCOMPRESSEDBYTES) +* - const polyvec *a: pointer to input vector of polynomials +**************************************************/ +static void polyvec_compress(uint8_t r[KYBER_POLYVECCOMPRESSEDBYTES], const polyvec *a) +{ + unsigned int i,j,k; + uint64_t d0; + +#if (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 352)) + uint16_t t[8]; + for(i=0;ivec[i].coeffs[8*j+k]; + t[k] += ((int16_t)t[k] >> 15) & KYBER_Q; + d0 = t[k]; + d0 <<= 11; + d0 += 1664; + d0 *= 645084; + d0 >>= 31; + t[k] = d0 & 0x7ff; } - element_bits_done += chunk_bits; - element >>= chunk_bits; + r[ 0] = (t[0] >> 0); + r[ 1] = (t[0] >> 8) | (t[1] << 3); + r[ 2] = (t[1] >> 5) | (t[2] << 6); + r[ 3] = (t[2] >> 2); + r[ 4] = (t[2] >> 10) | (t[3] << 1); + r[ 5] = (t[3] >> 7) | (t[4] << 4); + r[ 6] = (t[4] >> 4) | (t[5] << 7); + r[ 7] = (t[5] >> 1); + r[ 8] = (t[5] >> 9) | (t[6] << 2); + r[ 9] = (t[6] >> 6) | (t[7] << 5); + r[10] = (t[7] >> 3); + r += 11; } } +#elif (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 320)) + uint16_t t[4]; + for(i=0;ivec[i].coeffs[4*j+k]; + t[k] += ((int16_t)t[k] >> 15) & KYBER_Q; + d0 = t[k]; + d0 <<= 10; + d0 += 1665; + d0 *= 1290167; + d0 >>= 32; + t[k] = d0 & 0x3ff; + } - if (out_byte_bits > 0) { - *out = out_byte; + 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 } -// scalar_encode_1 is |scalar_encode| specialised for |bits| == 1. -static void scalar_encode_1(uint8_t out[32], const scalar *s) { - for (int i = 0; i < DEGREE; i += 8) { - uint8_t out_byte = 0; - for (int j = 0; j < 8; j++) { - out_byte |= (s->c[i + j] & 1) << j; +/************************************************* +* Name: polyvec_decompress +* +* Description: De-serialize and decompress vector of polynomials; +* approximate inverse of polyvec_compress +* +* Arguments: - polyvec *r: pointer to output vector of polynomials +* - const uint8_t *a: pointer to input byte array +* (of length KYBER_POLYVECCOMPRESSEDBYTES) +**************************************************/ +static void polyvec_decompress(polyvec *r, const uint8_t a[KYBER_POLYVECCOMPRESSEDBYTES]) +{ + unsigned int i,j,k; + +#if (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 352)) + uint16_t t[8]; + for(i=0;i> 0) | ((uint16_t)a[ 1] << 8); + t[1] = (a[1] >> 3) | ((uint16_t)a[ 2] << 5); + t[2] = (a[2] >> 6) | ((uint16_t)a[ 3] << 2) | ((uint16_t)a[4] << 10); + t[3] = (a[4] >> 1) | ((uint16_t)a[ 5] << 7); + t[4] = (a[5] >> 4) | ((uint16_t)a[ 6] << 4); + t[5] = (a[6] >> 7) | ((uint16_t)a[ 7] << 1) | ((uint16_t)a[8] << 9); + t[6] = (a[8] >> 2) | ((uint16_t)a[ 9] << 6); + t[7] = (a[9] >> 5) | ((uint16_t)a[10] << 3); + a += 11; + + for(k=0;k<8;k++) + r->vec[i].coeffs[8*j+k] = ((uint32_t)(t[k] & 0x7FF)*KYBER_Q + 1024) >> 11; + } + } +#elif (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 320)) + uint16_t t[4]; + for(i=0;i> 0) | ((uint16_t)a[1] << 8); + t[1] = (a[1] >> 2) | ((uint16_t)a[2] << 6); + t[2] = (a[2] >> 4) | ((uint16_t)a[3] << 4); + t[3] = (a[3] >> 6) | ((uint16_t)a[4] << 2); + a += 5; + + for(k=0;k<4;k++) + r->vec[i].coeffs[4*j+k] = ((uint32_t)(t[k] & 0x3FF)*KYBER_Q + 512) >> 10; } - *out = out_byte; - out++; } +#else +#error "KYBER_POLYVECCOMPRESSEDBYTES needs to be in {320*KYBER_K, 352*KYBER_K}" +#endif } -// Encodes an entire vector into 32*|RANK|*|bits| bytes. Note that since 256 -// (DEGREE) is divisible by 8, the individual vector entries will always fill a -// whole number of bytes, so we do not need to worry about bit packing here. -static void vector_encode(uint8_t *out, const vector *a, int bits) { - for (int i = 0; i < RANK; i++) { - scalar_encode(out + i * bits * DEGREE / 8, &a->v[i], bits); - } +/************************************************* +* Name: polyvec_tobytes +* +* Description: Serialize vector of polynomials +* +* Arguments: - uint8_t *r: pointer to output byte array +* (needs space for KYBER_POLYVECBYTES) +* - const polyvec *a: pointer to input vector of polynomials +**************************************************/ +static void polyvec_tobytes(uint8_t r[KYBER_POLYVECBYTES], const polyvec *a) +{ + unsigned int i; + for(i=0;ivec[i]); } -// scalar_decode parses |DEGREE * bits| bits from |in| into |DEGREE| values in -// |out|. It returns one on success and zero if any parsed value is >= -// |kPrime|. -static int scalar_decode(scalar *out, const uint8_t *in, int bits) { - assert(bits <= (int)sizeof(*out->c) * 8 && bits != 1); +/************************************************* +* Name: polyvec_frombytes +* +* Description: De-serialize vector of polynomials; +* inverse of polyvec_tobytes +* +* Arguments: - uint8_t *r: pointer to output byte array +* - const polyvec *a: pointer to input vector of polynomials +* (of length KYBER_POLYVECBYTES) +**************************************************/ +static void polyvec_frombytes(polyvec *r, const uint8_t a[KYBER_POLYVECBYTES]) +{ + unsigned int i; + for(i=0;ivec[i], a+i*KYBER_POLYBYTES); +} - uint8_t in_byte = 0; - int in_byte_bits_left = 0; +/************************************************* +* Name: polyvec_ntt +* +* Description: Apply forward NTT to all elements of a vector of polynomials +* +* Arguments: - polyvec *r: pointer to in/output vector of polynomials +**************************************************/ +static void polyvec_ntt(polyvec *r) +{ + unsigned int i; + for(i=0;ivec[i]); +} - for (int i = 0; i < DEGREE; i++) { - uint16_t element = 0; - int element_bits_done = 0; +/************************************************* +* Name: polyvec_invntt_tomont +* +* Description: Apply inverse NTT to all elements of a vector of polynomials +* and multiply by Montgomery factor 2^16 +* +* Arguments: - polyvec *r: pointer to in/output vector of polynomials +**************************************************/ +static void polyvec_invntt_tomont(polyvec *r) +{ + unsigned int i; + for(i=0;ivec[i]); +} - while (element_bits_done < bits) { - if (in_byte_bits_left == 0) { - in_byte = *in; - in++; - in_byte_bits_left = 8; - } +/************************************************* +* Name: polyvec_basemul_acc_montgomery +* +* Description: Multiply elements of a and b in NTT domain, accumulate into r, +* and multiply by 2^-16. +* +* Arguments: - poly *r: pointer to output polynomial +* - const polyvec *a: pointer to first input vector of polynomials +* - const polyvec *b: pointer to second input vector of polynomials +**************************************************/ +static void polyvec_basemul_acc_montgomery(poly *r, const polyvec *a, const polyvec *b) +{ + unsigned int i; + poly t; + + poly_basemul_montgomery(r, &a->vec[0], &b->vec[0]); + for(i=1;ivec[i], &b->vec[i]); + poly_add(r, r, &t); + } - int chunk_bits = bits - element_bits_done; - if (chunk_bits > in_byte_bits_left) { - chunk_bits = in_byte_bits_left; - } + poly_reduce(r); +} - element |= (in_byte & kMasks[chunk_bits - 1]) << element_bits_done; - in_byte_bits_left -= chunk_bits; - in_byte >>= chunk_bits; +/************************************************* +* Name: polyvec_reduce +* +* Description: Applies Barrett reduction to each coefficient +* of each element of a vector of polynomials; +* for details of the Barrett reduction see comments in reduce.c +* +* Arguments: - polyvec *r: pointer to input/output polynomial +**************************************************/ +static void polyvec_reduce(polyvec *r) +{ + unsigned int i; + for(i=0;ivec[i]); +} - element_bits_done += chunk_bits; - } +/************************************************* +* Name: polyvec_add +* +* Description: Add vectors of polynomials +* +* Arguments: - polyvec *r: pointer to output vector of polynomials +* - const polyvec *a: pointer to first input vector of polynomials +* - const polyvec *b: pointer to second input vector of polynomials +**************************************************/ +static void polyvec_add(polyvec *r, const polyvec *a, const polyvec *b) +{ + unsigned int i; + for(i=0;ivec[i], &a->vec[i], &b->vec[i]); +} - if (element >= kPrime) { - return 0; - } - out->c[i] = element; - } +// +// 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;ic[i + j] = in_byte & 1; - in_byte >>= 1; - } +/************************************************* +* 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; } -// Decodes 32*|RANK|*|bits| bytes from |in| into |out|. It returns one on -// success or zero if any parsed value is >= |kPrime|. -static int vector_decode(vector *out, const uint8_t *in, int bits) { - for (int i = 0; i < RANK; i++) { - if (!scalar_decode(&out->v[i], in + i * bits * DEGREE / 8, bits)) { - return 0; +#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> kBarrettShift; - uint32_t remainder = product - quotient * kPrime; - - // Adjust the quotient to round correctly: - // 0 <= remainder <= kHalfPrime round to 0 - // kHalfPrime < remainder <= kPrime + kHalfPrime round to 1 - // kPrime + kHalfPrime < remainder < 2 * kPrime round to 2 - assert(remainder < 2u * kPrime); - quotient += 1 & constant_time_lt_w(kHalfPrime, remainder); - quotient += 1 & constant_time_lt_w(kPrime + kHalfPrime, remainder); - return quotient & ((1 << bits) - 1); -} - -// Decompresses |x| by using an equi-distant representative. The formula is -// round(kPrime/2^|bits|*x). Note that 2^|bits| being the divisor allows us to -// implement this logic using only bit operations. -static uint16_t decompress(uint16_t x, int bits) { - uint32_t product = (uint32_t)x * kPrime; - uint32_t power = 1 << bits; - // This is |product| % power, since |power| is a power of 2. - uint32_t remainder = product & (power - 1); - // This is |product| / power, since |power| is a power of 2. - uint32_t lower = product >> bits; - // The rounding logic works since the first half of numbers mod |power| have a - // 0 as first bit, and the second half has a 1 as first bit, since |power| is - // a power of 2. As a 12 bit number, |remainder| is always positive, so we - // will shift in 0s for a right shift. - return lower + (remainder >> (bits - 1)); -} - -static void scalar_compress(scalar *s, int bits) { - for (int i = 0; i < DEGREE; i++) { - s->c[i] = compress(s->c[i], bits); +/************************************************* +* 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;ic[i] = decompress(s->c[i], bits); - } +/************************************************* +* 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 int 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; + + if (!unpack_pk(&pkpv, seed, pk)) + return 0; + + poly_frommsg(&k, m); + gen_at(at, seed); + + for(i=0;iv[i], bits); - } +/************************************************* +* 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); } -static void vector_decompress(vector *a, int bits) { - for (int i = 0; i < RANK; i++) { - scalar_decompress(&a->v[i], bits); - } +// +// 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; } -struct public_key { - vector t; - uint8_t rho[32]; - uint8_t public_key_hash[32]; - matrix m; +/************************************************* +* 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 }; -static struct public_key *public_key_from_external( - const struct KYBER_public_key *external) { - static_assert(sizeof(struct KYBER_public_key) >= sizeof(struct public_key), - "Kyber public key is too small"); - static_assert(alignof(struct KYBER_public_key) >= alignof(struct public_key), - "Kyber public key align incorrect"); - return (struct public_key *)external; +/************************************************* +* 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; } -struct private_key { - struct public_key pub; - vector s; - uint8_t fo_failure_secret[32]; -}; -static struct private_key *private_key_from_external( - const struct KYBER_private_key *external) { - static_assert(sizeof(struct KYBER_private_key) >= sizeof(struct private_key), - "Kyber private key too small"); - static_assert( - alignof(struct KYBER_private_key) >= alignof(struct private_key), - "Kyber private key align incorrect"); - return (struct private_key *)external; -} - -// Calls |KYBER_generate_key_external_entropy| with random bytes from -// |RAND_bytes|. -void KYBER_generate_key(uint8_t out_encoded_public_key[KYBER_PUBLIC_KEY_BYTES], - struct KYBER_private_key *out_private_key) { - uint8_t entropy[KYBER_GENERATE_KEY_ENTROPY]; - RAND_bytes(entropy, sizeof(entropy)); - KYBER_generate_key_external_entropy(out_encoded_public_key, out_private_key, - entropy); -} - -static int kyber_marshal_public_key(CBB *out, const struct public_key *pub) { - uint8_t *vector_output; - if (!CBB_add_space(out, &vector_output, kEncodedVectorSize)) { - return 0; +/************************************************* +* 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; } - vector_encode(vector_output, &pub->t, kLog2Prime); - if (!CBB_add_bytes(out, pub->rho, sizeof(pub->rho))) { - return 0; + + return pos; +} + +/************************************************* +* Name: keccak_absorb +* +* Description: Absorb step of Keccak; incremental. +* +* Arguments: - uint64_t *s: pointer to Keccak state +* - unsigned int pos: position in current block to be absorbed +* - 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 +* +* Returns new position pos in current block +**************************************************/ +static unsigned int keccak_absorb(uint64_t s[25], + unsigned int pos, + unsigned int r, + const uint8_t *in, + size_t inlen) +{ + unsigned int i; + + while(pos+inlen >= r) { + for(i=pos;ipub.rho, hashed, sizeof(priv->pub.rho)); - matrix_expand(&priv->pub.m, rho); - uint8_t counter = 0; - vector_generate_secret_eta_2(&priv->s, &counter, sigma); - vector_ntt(&priv->s); - vector error; - vector_generate_secret_eta_2(&error, &counter, sigma); - vector_ntt(&error); - matrix_mult_transpose(&priv->pub.t, &priv->pub.m, &priv->s); - vector_add(&priv->pub.t, &error); - - CBB cbb; - CBB_init_fixed(&cbb, out_encoded_public_key, KYBER_PUBLIC_KEY_BYTES); - if (!kyber_marshal_public_key(&cbb, &priv->pub)) { - abort(); + +/************************************************* +* 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;ipub.public_key_hash, sizeof(priv->pub.public_key_hash), - out_encoded_public_key, KYBER_PUBLIC_KEY_BYTES, - boringssl_sha3_256); - OPENSSL_memcpy(priv->fo_failure_secret, entropy + 32, 32); -} - -void KYBER_public_from_private(struct KYBER_public_key *out_public_key, - const struct KYBER_private_key *private_key) { - struct public_key *const pub = public_key_from_external(out_public_key); - const struct private_key *const priv = private_key_from_external(private_key); - *pub = priv->pub; -} - -// Algorithm 5 of the Kyber spec. Encrypts a message with given randomness to -// the ciphertext in |out|. Without applying the Fujisaki-Okamoto transform this -// would not result in a CCA secure scheme, since lattice schemes are vulnerable -// to decryption failure oracles. -static void encrypt_cpa(uint8_t out[KYBER_CIPHERTEXT_BYTES], - const struct public_key *pub, const uint8_t message[32], - const uint8_t randomness[32]) { - uint8_t counter = 0; - vector secret; - vector_generate_secret_eta_2(&secret, &counter, randomness); - vector_ntt(&secret); - vector error; - vector_generate_secret_eta_2(&error, &counter, randomness); - uint8_t input[33]; - OPENSSL_memcpy(input, randomness, 32); - input[32] = counter; - scalar scalar_error; - scalar_centered_binomial_distribution_eta_2_with_prf(&scalar_error, input); - vector u; - matrix_mult(&u, &pub->m, &secret); - vector_inverse_ntt(&u); - vector_add(&u, &error); - scalar v; - scalar_inner_product(&v, &pub->t, &secret); - scalar_inverse_ntt(&v); - scalar_add(&v, &scalar_error); - scalar expanded_message; - scalar_decode_1(&expanded_message, message); - scalar_decompress(&expanded_message, 1); - scalar_add(&v, &expanded_message); - vector_compress(&u, kDU); - vector_encode(out, &u, kDU); - scalar_compress(&v, kDV); - scalar_encode(out + kCompressedVectorSize, &v, kDV); -} - -// Calls KYBER_encap_external_entropy| with random bytes from |RAND_bytes| -void KYBER_encap(uint8_t out_ciphertext[KYBER_CIPHERTEXT_BYTES], - uint8_t *out_shared_secret, size_t out_shared_secret_len, - const struct KYBER_public_key *public_key) { - uint8_t entropy[KYBER_ENCAP_ENTROPY]; - RAND_bytes(entropy, KYBER_ENCAP_ENTROPY); - KYBER_encap_external_entropy(out_ciphertext, out_shared_secret, - out_shared_secret_len, public_key, entropy); -} - -// Algorithm 8 of the Kyber spec, safe for line 2 of the spec. The spec there -// hashes the output of the system's random number generator, since the FO -// transform will reveal it to the decrypting party. There is no reason to do -// this when a secure random number generator is used. When an insecure random -// number generator is used, the caller should switch to a secure one before -// calling this method. -void KYBER_encap_external_entropy( - uint8_t out_ciphertext[KYBER_CIPHERTEXT_BYTES], uint8_t *out_shared_secret, - size_t out_shared_secret_len, const struct KYBER_public_key *public_key, - const uint8_t entropy[KYBER_ENCAP_ENTROPY]) { - const struct public_key *pub = public_key_from_external(public_key); - uint8_t input[64]; - OPENSSL_memcpy(input, entropy, KYBER_ENCAP_ENTROPY); - OPENSSL_memcpy(input + KYBER_ENCAP_ENTROPY, pub->public_key_hash, - sizeof(input) - KYBER_ENCAP_ENTROPY); - uint8_t prekey_and_randomness[64]; - BORINGSSL_keccak(prekey_and_randomness, sizeof(prekey_and_randomness), input, - sizeof(input), boringssl_sha3_512); - encrypt_cpa(out_ciphertext, pub, entropy, prekey_and_randomness + 32); - BORINGSSL_keccak(prekey_and_randomness + 32, 32, out_ciphertext, - KYBER_CIPHERTEXT_BYTES, boringssl_sha3_256); - BORINGSSL_keccak(out_shared_secret, out_shared_secret_len, - prekey_and_randomness, sizeof(prekey_and_randomness), - boringssl_shake256); -} - -// Algorithm 6 of the Kyber spec. -static void decrypt_cpa(uint8_t out[32], const struct private_key *priv, - const uint8_t ciphertext[KYBER_CIPHERTEXT_BYTES]) { - vector u; - vector_decode(&u, ciphertext, kDU); - vector_decompress(&u, kDU); - vector_ntt(&u); - scalar v; - scalar_decode(&v, ciphertext + kCompressedVectorSize, kDV); - scalar_decompress(&v, kDV); - scalar mask; - scalar_inner_product(&mask, &priv->s, &u); - scalar_inverse_ntt(&mask); - scalar_sub(&v, &mask); - scalar_compress(&v, 1); - scalar_encode_1(out, &v); -} - -// Algorithm 9 of the Kyber spec, performing the FO transform by running -// encrypt_cpa on the decrypted message. The spec does not allow the decryption -// failure to be passed on to the caller, and instead returns a result that is -// deterministic but unpredictable to anyone without knowledge of the private -// key. -void KYBER_decap(uint8_t *out_shared_secret, size_t out_shared_secret_len, - const uint8_t ciphertext[KYBER_CIPHERTEXT_BYTES], - const struct KYBER_private_key *private_key) { - const struct private_key *priv = private_key_from_external(private_key); - uint8_t decrypted[64]; - decrypt_cpa(decrypted, priv, ciphertext); - OPENSSL_memcpy(decrypted + 32, priv->pub.public_key_hash, - sizeof(decrypted) - 32); - uint8_t prekey_and_randomness[64]; - BORINGSSL_keccak(prekey_and_randomness, sizeof(prekey_and_randomness), - decrypted, sizeof(decrypted), boringssl_sha3_512); - uint8_t expected_ciphertext[KYBER_CIPHERTEXT_BYTES]; - encrypt_cpa(expected_ciphertext, &priv->pub, decrypted, - prekey_and_randomness + 32); - uint8_t mask = - constant_time_eq_int_8(CRYPTO_memcmp(ciphertext, expected_ciphertext, - sizeof(expected_ciphertext)), - 0); - uint8_t input[64]; - for (int i = 0; i < 32; i++) { - input[i] = constant_time_select_8(mask, prekey_and_randomness[i], - priv->fo_failure_secret[i]); + for(i=0;is, SHAKE128_RATE, in, inlen, 0x1F); + state->pos = SHAKE128_RATE; } -// kyber_parse_public_key_no_hash parses |in| into |pub| but doesn't calculate -// the value of |pub->public_key_hash|. -static int kyber_parse_public_key_no_hash(struct public_key *pub, CBS *in) { - CBS t_bytes; - if (!CBS_get_bytes(in, &t_bytes, kEncodedVectorSize) || - !vector_decode(&pub->t, CBS_data(&t_bytes), kLog2Prime) || - !CBS_copy_bytes(in, pub->rho, sizeof(pub->rho))) { - return 0; - } - matrix_expand(&pub->m, pub->rho); - return 1; +/************************************************* +* 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_absorb +* +* Description: Absorb step of the SHAKE256 XOF; incremental. +* +* Arguments: - keccak_state *state: pointer to (initialized) 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(keccak_state *state, const uint8_t *in, size_t inlen) +{ + state->pos = keccak_absorb(state->s, state->pos, SHAKE256_RATE, in, inlen); +} + +/************************************************* +* Name: shake256_finalize +* +* Description: Finalize absorb step of the SHAKE256 XOF. +* +* Arguments: - keccak_state *state: pointer to Keccak state +**************************************************/ +static void shake256_finalize(keccak_state *state) +{ + keccak_finalize(state->s, state->pos, SHAKE256_RATE, 0x1F); + state->pos = SHAKE256_RATE; +} + +/************************************************* +* Name: keccak_init +* +* Description: Initializes the Keccak state. +* +* Arguments: - uint64_t *s: pointer to Keccak state +**************************************************/ +static void keccak_init(uint64_t s[25]) +{ + unsigned int i; + for(i=0;i<25;i++) + s[i] = 0; +} + +/************************************************* +* Name: shake256_init +* +* Description: Initilizes Keccak state for use as SHAKE256 XOF +* +* Arguments: - keccak_state *state: pointer to (uninitialized) Keccak state +**************************************************/ +static void shake256_init(keccak_state *state) +{ + keccak_init(state->s); + state->pos = 0; +} + + +/************************************************* +* 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)); } -int KYBER_parse_public_key(struct KYBER_public_key *public_key, CBS *in) { - struct public_key *pub = public_key_from_external(public_key); - CBS orig_in = *in; - if (!kyber_parse_public_key_no_hash(pub, in) || // - CBS_len(in) != 0) { +/************************************************* +* Name: kyber_shake256_prf +* +* Description: Usage of SHAKE256 as a PRF, concatenates secret and public input +* and then generates outlen bytes of SHAKE256 output +* +* Arguments: - uint8_t *out: pointer to output +* - size_t outlen: number of requested output bytes +* - const uint8_t *key: pointer to the key (of length KYBER_SYMBYTES) +* - uint8_t nonce: single-byte nonce (public PRF input) +**************************************************/ +static void kyber_shake256_prf(uint8_t *out, size_t outlen, const uint8_t key[KYBER_SYMBYTES], uint8_t nonce) +{ + uint8_t extkey[KYBER_SYMBYTES+1]; + + memcpy(extkey, key, KYBER_SYMBYTES); + extkey[KYBER_SYMBYTES] = nonce; + + shake256(out, outlen, extkey, sizeof(extkey)); +} + +// +// kem.c +// + +// Modified crypto_kem_keypair to BoringSSL style API +void generate_key(struct public_key *out_pub, struct private_key *out_priv, + const uint8_t seed[KYBER_GENERATE_KEY_BYTES]) +{ + size_t i; + uint8_t* pk = &out_pub->opaque[0]; + uint8_t* sk = &out_priv->opaque[0]; + + indcpa_keypair(pk, sk, seed); + for(i=0;iopaque[0]; + uint8_t *ct = out_ciphertext; + + uint8_t buf[2*KYBER_SYMBYTES]; + /* Will contain key, coins */ + uint8_t kr[2*KYBER_SYMBYTES]; + + memcpy(buf, seed, KYBER_SYMBYTES); + + /* Don't release system RNG output */ + hash_h(buf, buf, KYBER_SYMBYTES); + + /* Multitarget countermeasure for coins + contributory KEM */ + hash_h(buf+KYBER_SYMBYTES, pk, KYBER_PUBLICKEYBYTES); + hash_g(kr, buf, 2*KYBER_SYMBYTES); + + /* coins are in kr+KYBER_SYMBYTES */ + if(!indcpa_enc(ct, buf, pk, kr+KYBER_SYMBYTES)) return 0; + + if (mlkem == 1) { + memcpy(ss, kr, KYBER_SYMBYTES); + } else { + /* 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); } - BORINGSSL_keccak(pub->public_key_hash, sizeof(pub->public_key_hash), - CBS_data(&orig_in), CBS_len(&orig_in), boringssl_sha3_256); return 1; } -int KYBER_marshal_private_key(CBB *out, - const struct KYBER_private_key *private_key) { - const struct private_key *const priv = private_key_from_external(private_key); - uint8_t *s_output; - if (!CBB_add_space(out, &s_output, kEncodedVectorSize)) { - return 0; +// 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, int mlkem) +{ + 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;is, kLog2Prime); - if (!kyber_marshal_public_key(out, &priv->pub) || - !CBB_add_bytes(out, priv->pub.public_key_hash, - sizeof(priv->pub.public_key_hash)) || - !CBB_add_bytes(out, priv->fo_failure_secret, - sizeof(priv->fo_failure_secret))) { - return 0; + + if (mlkem == 1) { + /* Compute shared secret in case of rejection: ss2 = PRF(z || c). */ + uint8_t ss2[KYBER_SYMBYTES]; + keccak_state ks; + shake256_init(&ks); + shake256_absorb( + &ks, + sk + KYBER_SECRETKEYBYTES - KYBER_SYMBYTES, + KYBER_SYMBYTES + ); + shake256_absorb(&ks, ct, ciphertext_len); + shake256_finalize(&ks); + shake256_squeeze(ss2, KYBER_SYMBYTES, &ks); + + /* Set ss2 to the real shared secret if c = c' */ + cmov(ss2, kr, KYBER_SYMBYTES, 1-fail); + memcpy(ss, ss2, KYBER_SYMBYTES); + } else { + /* 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); } - return 1; } -int KYBER_parse_private_key(struct KYBER_private_key *out_private_key, - CBS *in) { - struct private_key *const priv = private_key_from_external(out_private_key); +void marshal_public_key(uint8_t out[KYBER_PUBLICKEYBYTES], + const struct public_key *in_pub) { + memcpy(out, &in_pub->opaque, KYBER_PUBLICKEYBYTES); +} - CBS s_bytes; - if (!CBS_get_bytes(in, &s_bytes, kEncodedVectorSize) || - !vector_decode(&priv->s, CBS_data(&s_bytes), kLog2Prime) || - !kyber_parse_public_key_no_hash(&priv->pub, in) || - !CBS_copy_bytes(in, priv->pub.public_key_hash, - sizeof(priv->pub.public_key_hash)) || - !CBS_copy_bytes(in, priv->fo_failure_secret, - sizeof(priv->fo_failure_secret)) || - CBS_len(in) != 0) { - return 0; - } - return 1; +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/kyber/kyber_test.cc b/src/crypto/kyber/kyber_test.cc deleted file mode 100644 index eb76b5bd7..000000000 --- a/src/crypto/kyber/kyber_test.cc +++ /dev/null @@ -1,229 +0,0 @@ -/* Copyright (c) 2023, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - -#include - -#include - -#include - -#include -#include -#include - -#include "../test/file_test.h" -#include "../test/test_util.h" -#include "./internal.h" - - -static void KeccakFileTest(FileTest *t) { - std::vector input, sha3_256_expected, sha3_512_expected, - shake128_expected, shake256_expected; - ASSERT_TRUE(t->GetBytes(&input, "Input")); - ASSERT_TRUE(t->GetBytes(&sha3_256_expected, "SHA3-256")); - ASSERT_TRUE(t->GetBytes(&sha3_512_expected, "SHA3-512")); - ASSERT_TRUE(t->GetBytes(&shake128_expected, "SHAKE-128")); - ASSERT_TRUE(t->GetBytes(&shake256_expected, "SHAKE-256")); - - uint8_t sha3_256_digest[32]; - BORINGSSL_keccak(sha3_256_digest, sizeof(sha3_256_digest), input.data(), - input.size(), boringssl_sha3_256); - uint8_t sha3_512_digest[64]; - BORINGSSL_keccak(sha3_512_digest, sizeof(sha3_512_digest), input.data(), - input.size(), boringssl_sha3_512); - uint8_t shake128_output[512]; - BORINGSSL_keccak(shake128_output, sizeof(shake128_output), input.data(), - input.size(), boringssl_shake128); - uint8_t shake256_output[512]; - BORINGSSL_keccak(shake256_output, sizeof(shake256_output), input.data(), - input.size(), boringssl_shake256); - - EXPECT_EQ(Bytes(sha3_256_expected), Bytes(sha3_256_digest)); - EXPECT_EQ(Bytes(sha3_512_expected), Bytes(sha3_512_digest)); - EXPECT_EQ(Bytes(shake128_expected), Bytes(shake128_output)); - EXPECT_EQ(Bytes(shake256_expected), Bytes(shake256_output)); - - struct BORINGSSL_keccak_st ctx; - - BORINGSSL_keccak_init(&ctx, input.data(), input.size(), boringssl_shake128); - for (size_t i = 0; i < sizeof(shake128_output); i++) { - BORINGSSL_keccak_squeeze(&ctx, &shake128_output[i], 1); - } - EXPECT_EQ(Bytes(shake128_expected), Bytes(shake128_output)); - - BORINGSSL_keccak_init(&ctx, input.data(), input.size(), boringssl_shake256); - for (size_t i = 0; i < sizeof(shake256_output); i++) { - BORINGSSL_keccak_squeeze(&ctx, &shake256_output[i], 1); - } - EXPECT_EQ(Bytes(shake256_expected), Bytes(shake256_output)); -} - -TEST(KyberTest, KeccakTestVectors) { - FileTestGTest("crypto/kyber/keccak_tests.txt", KeccakFileTest); -} - -template -static std::vector Marshal(int (*marshal_func)(CBB *, const T *), - const T *t) { - bssl::ScopedCBB cbb; - uint8_t *encoded; - size_t encoded_len; - if (!CBB_init(cbb.get(), 1) || // - !marshal_func(cbb.get(), t) || // - !CBB_finish(cbb.get(), &encoded, &encoded_len)) { - abort(); - } - - std::vector ret(encoded, encoded + encoded_len); - OPENSSL_free(encoded); - return ret; -} - -TEST(KyberTest, Basic) { - uint8_t encoded_public_key[KYBER_PUBLIC_KEY_BYTES]; - KYBER_private_key priv; - KYBER_generate_key(encoded_public_key, &priv); - - uint8_t first_two_bytes[2]; - OPENSSL_memcpy(first_two_bytes, encoded_public_key, sizeof(first_two_bytes)); - OPENSSL_memset(encoded_public_key, 0xff, sizeof(first_two_bytes)); - CBS encoded_public_key_cbs; - CBS_init(&encoded_public_key_cbs, encoded_public_key, - sizeof(encoded_public_key)); - KYBER_public_key pub; - // Parsing should fail because the first coefficient is >= kPrime; - ASSERT_FALSE(KYBER_parse_public_key(&pub, &encoded_public_key_cbs)); - - OPENSSL_memcpy(encoded_public_key, first_two_bytes, sizeof(first_two_bytes)); - CBS_init(&encoded_public_key_cbs, encoded_public_key, - sizeof(encoded_public_key)); - ASSERT_TRUE(KYBER_parse_public_key(&pub, &encoded_public_key_cbs)); - EXPECT_EQ(CBS_len(&encoded_public_key_cbs), 0u); - - EXPECT_EQ(Bytes(encoded_public_key), - Bytes(Marshal(KYBER_marshal_public_key, &pub))); - - KYBER_public_key pub2; - KYBER_public_from_private(&pub2, &priv); - EXPECT_EQ(Bytes(encoded_public_key), - Bytes(Marshal(KYBER_marshal_public_key, &pub2))); - - std::vector encoded_private_key( - Marshal(KYBER_marshal_private_key, &priv)); - EXPECT_EQ(encoded_private_key.size(), size_t{KYBER_PRIVATE_KEY_BYTES}); - - OPENSSL_memcpy(first_two_bytes, encoded_private_key.data(), - sizeof(first_two_bytes)); - OPENSSL_memset(encoded_private_key.data(), 0xff, sizeof(first_two_bytes)); - CBS cbs; - CBS_init(&cbs, encoded_private_key.data(), encoded_private_key.size()); - KYBER_private_key priv2; - // Parsing should fail because the first coefficient is >= kPrime. - ASSERT_FALSE(KYBER_parse_private_key(&priv2, &cbs)); - - OPENSSL_memcpy(encoded_private_key.data(), first_two_bytes, - sizeof(first_two_bytes)); - CBS_init(&cbs, encoded_private_key.data(), encoded_private_key.size()); - ASSERT_TRUE(KYBER_parse_private_key(&priv2, &cbs)); - EXPECT_EQ(Bytes(encoded_private_key), - Bytes(Marshal(KYBER_marshal_private_key, &priv2))); - - uint8_t ciphertext[KYBER_CIPHERTEXT_BYTES]; - uint8_t shared_secret1[64]; - uint8_t shared_secret2[sizeof(shared_secret1)]; - KYBER_encap(ciphertext, shared_secret1, sizeof(shared_secret1), &pub); - KYBER_decap(shared_secret2, sizeof(shared_secret2), ciphertext, &priv); - EXPECT_EQ(Bytes(shared_secret1), Bytes(shared_secret2)); - KYBER_decap(shared_secret2, sizeof(shared_secret2), ciphertext, &priv2); - EXPECT_EQ(Bytes(shared_secret1), Bytes(shared_secret2)); -} - -static void KyberFileTest(FileTest *t) { - std::vector seed, public_key_expected, private_key_expected, - ciphertext_expected, shared_secret_expected, given_generate_entropy, - given_encap_entropy_pre_hash; - t->IgnoreAttribute("count"); - ASSERT_TRUE(t->GetBytes(&seed, "seed")); - ASSERT_TRUE(t->GetBytes(&public_key_expected, "pk")); - ASSERT_TRUE(t->GetBytes(&private_key_expected, "sk")); - ASSERT_TRUE(t->GetBytes(&ciphertext_expected, "ct")); - ASSERT_TRUE(t->GetBytes(&shared_secret_expected, "ss")); - ASSERT_TRUE(t->GetBytes(&given_generate_entropy, "generateEntropy")); - ASSERT_TRUE( - t->GetBytes(&given_encap_entropy_pre_hash, "encapEntropyPreHash")); - - KYBER_private_key priv; - uint8_t encoded_private_key[KYBER_PRIVATE_KEY_BYTES]; - KYBER_public_key pub; - uint8_t encoded_public_key[KYBER_PUBLIC_KEY_BYTES]; - uint8_t ciphertext[KYBER_CIPHERTEXT_BYTES]; - uint8_t gen_key_entropy[KYBER_GENERATE_KEY_ENTROPY]; - uint8_t encap_entropy[KYBER_ENCAP_ENTROPY]; - uint8_t encapsulated_key[32]; - uint8_t decapsulated_key[32]; - // The test vectors provide a CTR-DRBG seed which is used to generate the - // input entropy. - ASSERT_EQ(seed.size(), size_t{CTR_DRBG_ENTROPY_LEN}); - { - bssl::UniquePtr state( - CTR_DRBG_new(seed.data(), nullptr, 0)); - ASSERT_TRUE(state); - ASSERT_TRUE( - CTR_DRBG_generate(state.get(), gen_key_entropy, 32, nullptr, 0)); - ASSERT_TRUE( - CTR_DRBG_generate(state.get(), gen_key_entropy + 32, 32, nullptr, 0)); - ASSERT_TRUE(CTR_DRBG_generate(state.get(), encap_entropy, - KYBER_ENCAP_ENTROPY, nullptr, 0)); - } - - EXPECT_EQ(Bytes(gen_key_entropy), Bytes(given_generate_entropy)); - EXPECT_EQ(Bytes(encap_entropy), Bytes(given_encap_entropy_pre_hash)); - - BORINGSSL_keccak(encap_entropy, sizeof(encap_entropy), encap_entropy, - sizeof(encap_entropy), boringssl_sha3_256); - - KYBER_generate_key_external_entropy(encoded_public_key, &priv, - gen_key_entropy); - CBB cbb; - CBB_init_fixed(&cbb, encoded_private_key, sizeof(encoded_private_key)); - ASSERT_TRUE(KYBER_marshal_private_key(&cbb, &priv)); - CBS encoded_public_key_cbs; - CBS_init(&encoded_public_key_cbs, encoded_public_key, - sizeof(encoded_public_key)); - ASSERT_TRUE(KYBER_parse_public_key(&pub, &encoded_public_key_cbs)); - KYBER_encap_external_entropy(ciphertext, encapsulated_key, - sizeof(encapsulated_key), &pub, encap_entropy); - KYBER_decap(decapsulated_key, sizeof(decapsulated_key), ciphertext, &priv); - - EXPECT_EQ(Bytes(encapsulated_key), Bytes(decapsulated_key)); - EXPECT_EQ(Bytes(private_key_expected), Bytes(encoded_private_key)); - EXPECT_EQ(Bytes(public_key_expected), Bytes(encoded_public_key)); - EXPECT_EQ(Bytes(ciphertext_expected), Bytes(ciphertext)); - EXPECT_EQ(Bytes(shared_secret_expected), Bytes(encapsulated_key)); - - uint8_t corrupted_ciphertext[KYBER_CIPHERTEXT_BYTES]; - OPENSSL_memcpy(corrupted_ciphertext, ciphertext, KYBER_CIPHERTEXT_BYTES); - corrupted_ciphertext[3] ^= 0x40; - uint8_t corrupted_decapsulated_key[32]; - KYBER_decap(corrupted_decapsulated_key, sizeof(corrupted_decapsulated_key), - corrupted_ciphertext, &priv); - // It would be nice to have actual test vectors for the failure case, but the - // NIST submission currently does not include those, so we are just testing - // for inequality. - EXPECT_NE(Bytes(encapsulated_key), Bytes(corrupted_decapsulated_key)); -} - -TEST(KyberTest, TestVectors) { - FileTestGTest("crypto/kyber/kyber_tests.txt", KyberFileTest); -} diff --git a/src/crypto/obj/obj_dat.h b/src/crypto/obj/obj_dat.h index 654b3c08e..6cef2c079 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 965 +#define NUM_NID 969 static const uint8_t kObjectData[] = { /* NID_rsadsi */ @@ -8784,6 +8784,13 @@ static const ASN1_OBJECT kObjects[NUM_NID] = { {"HKDF", "hkdf", NID_hkdf, 0, NULL, 0}, {"X25519Kyber768Draft00", "X25519Kyber768Draft00", NID_X25519Kyber768Draft00, 0, NULL, 0}, + {"X25519Kyber512Draft00", "X25519Kyber512Draft00", + NID_X25519Kyber512Draft00, 0, NULL, 0}, + {"P256Kyber768Draft00", "P256Kyber768Draft00", NID_P256Kyber768Draft00, 0, + NULL, 0}, + {"X25519Kyber768Draft00Old", "X25519Kyber768Draft00Old", + NID_X25519Kyber768Draft00Old, 0, NULL, 0}, + {"X25519MLKEM768", "X25519MLKEM768", NID_X25519MLKEM768, 0, NULL, 0}, }; static const uint16_t kNIDsInShortNameOrder[] = { @@ -8916,6 +8923,7 @@ static const uint16_t kNIDsInShortNameOrder[] = { 18 /* OU */, 749 /* Oakley-EC2N-3 */, 750 /* Oakley-EC2N-4 */, + 966 /* P256Kyber768Draft00 */, 9 /* PBE-MD2-DES */, 168 /* PBE-MD2-RC2-64 */, 10 /* PBE-MD5-DES */, @@ -8982,7 +8990,10 @@ static const uint16_t kNIDsInShortNameOrder[] = { 458 /* UID */, 0 /* UNDEF */, 948 /* X25519 */, + 965 /* X25519Kyber512Draft00 */, 964 /* X25519Kyber768Draft00 */, + 967 /* X25519Kyber768Draft00Old */, + 968 /* X25519MLKEM768 */, 961 /* X448 */, 11 /* X500 */, 378 /* X500algorithms */, @@ -9829,6 +9840,7 @@ static const uint16_t kNIDsInLongNameOrder[] = { 366 /* OCSP Nonce */, 371 /* OCSP Service Locator */, 180 /* OCSP Signing */, + 966 /* P256Kyber768Draft00 */, 161 /* PBES2 */, 69 /* PBKDF2 */, 162 /* PBMAC1 */, @@ -9853,7 +9865,10 @@ static const uint16_t kNIDsInLongNameOrder[] = { 133 /* Time Stamping */, 375 /* Trust Root */, 948 /* X25519 */, + 965 /* X25519Kyber512Draft00 */, 964 /* X25519Kyber768Draft00 */, + 967 /* X25519Kyber768Draft00Old */, + 968 /* X25519MLKEM768 */, 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 a0519acee..2a46adfe8 100644 --- a/src/crypto/obj/obj_mac.num +++ b/src/crypto/obj/obj_mac.num @@ -952,3 +952,7 @@ X448 961 sha512_256 962 hkdf 963 X25519Kyber768Draft00 964 +X25519Kyber512Draft00 965 +P256Kyber768Draft00 966 +X25519Kyber768Draft00Old 967 +X25519MLKEM768 968 diff --git a/src/crypto/obj/objects.txt b/src/crypto/obj/objects.txt index 3ad32ea3d..347fc556a 100644 --- a/src/crypto/obj/objects.txt +++ b/src/crypto/obj/objects.txt @@ -1332,8 +1332,12 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme : dh-std-kdf : dh-cofactor-kdf -# NIDs for post quantum hybrid KEMs in TLS (no corresponding OIDs). +# NID for Kyber hybrids (no corresponding OID). + : X25519Kyber512Draft00 : X25519Kyber768Draft00 + : P256Kyber768Draft00 + : X25519Kyber768Draft00Old + : X25519MLKEM768 # See RFC 8410. 1 3 101 110 : X25519 diff --git a/src/include/openssl/kyber.h b/src/include/openssl/kyber.h index cafae9d17..a05eb8957 100644 --- a/src/include/openssl/kyber.h +++ b/src/include/openssl/kyber.h @@ -1,17 +1,3 @@ -/* Copyright (c) 2023, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - #ifndef OPENSSL_HEADER_KYBER_H #define OPENSSL_HEADER_KYBER_H @@ -21,105 +7,104 @@ 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 -// Kyber768. - - -// KYBER_public_key contains a Kyber768 public key. The contents of this -// object should never leave the address space since the format is unstable. -struct KYBER_public_key { - union { - uint8_t bytes[512 * (3 + 9) + 32 + 32]; - uint16_t alignment; - } opaque; +struct KYBER512_private_key { + uint8_t opaque[KYBER512_PRIVATE_KEY_BYTES]; }; - -// KYBER_private_key contains a Kyber768 private key. The contents of this -// object should never leave the address space since the format is unstable. -struct KYBER_private_key { - union { - uint8_t bytes[512 * (3 + 3 + 9) + 32 + 32 + 32]; - uint16_t alignment; - } opaque; +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_PUBLIC_KEY_BYTES is the number of bytes in an encoded Kyber768 public -// key. -#define KYBER_PUBLIC_KEY_BYTES 1184 - -// KYBER_generate_key generates a random public/private key pair, writes the -// encoded public key to |out_encoded_public_key| and sets |out_private_key| to -// the private key. -OPENSSL_EXPORT void KYBER_generate_key( - uint8_t out_encoded_public_key[KYBER_PUBLIC_KEY_BYTES], - struct KYBER_private_key *out_private_key); - -// KYBER_public_from_private sets |*out_public_key| to the public key that -// corresponds to |private_key|. (This is faster than parsing the output of -// |KYBER_generate_key| if, for some reason, you need to encapsulate to a key -// that was just generated.) -OPENSSL_EXPORT void KYBER_public_from_private( - struct KYBER_public_key *out_public_key, - const struct KYBER_private_key *private_key); - -// KYBER_CIPHERTEXT_BYTES is number of bytes in the Kyber768 ciphertext. -#define KYBER_CIPHERTEXT_BYTES 1088 - -// KYBER_encap encrypts a random secret key of length |out_shared_secret_len| to -// |public_key|, writes the ciphertext to |ciphertext|, and writes the random -// key to |out_shared_secret|. The party calling |KYBER_decap| must already know -// the correct value of |out_shared_secret_len|. -OPENSSL_EXPORT void KYBER_encap(uint8_t out_ciphertext[KYBER_CIPHERTEXT_BYTES], - uint8_t *out_shared_secret, - size_t out_shared_secret_len, - const struct KYBER_public_key *public_key); - -// KYBER_decap decrypts a key of length |out_shared_secret_len| from -// |ciphertext| using |private_key| and writes it to |out_shared_secret|. If -// |ciphertext| is invalid, |out_shared_secret| is filled with a key that -// will always be the same for the same |ciphertext| and |private_key|, but -// which appears to be random unless one has access to |private_key|. These -// alternatives occur in constant time. Any subsequent symmetric encryption -// using |out_shared_secret| must use an authenticated encryption scheme in -// order to discover the decapsulation failure. -OPENSSL_EXPORT void KYBER_decap( - uint8_t *out_shared_secret, size_t out_shared_secret_len, - const uint8_t ciphertext[KYBER_CIPHERTEXT_BYTES], - const struct KYBER_private_key *private_key); - - -// Serialisation of keys. - -// KYBER_marshal_public_key serializes |public_key| to |out| in the standard -// format for Kyber public keys. It returns one on success or zero on allocation -// error. -OPENSSL_EXPORT int KYBER_marshal_public_key( - CBB *out, const struct KYBER_public_key *public_key); - -// KYBER_parse_public_key parses a public key, in the format generated by -// |KYBER_marshal_public_key|, from |in| and writes the result to -// |out_public_key|. It returns one on success or zero on parse error or if -// there are trailing bytes in |in|. -OPENSSL_EXPORT int KYBER_parse_public_key( - struct KYBER_public_key *out_public_key, CBS *in); - -// KYBER_marshal_private_key serializes |private_key| to |out| in the standard -// format for Kyber private keys. It returns one on success or zero on -// allocation error. -OPENSSL_EXPORT int KYBER_marshal_private_key( - CBB *out, const struct KYBER_private_key *private_key); - -// KYBER_PRIVATE_KEY_BYTES is the length of the data produced by -// |KYBER_marshal_private_key|. -#define KYBER_PRIVATE_KEY_BYTES 2400 - -// KYBER_parse_private_key parses a private key, in the format generated by -// |KYBER_marshal_private_key|, from |in| and writes the result to -// |out_private_key|. It returns one on success or zero on parse error or if -// there are trailing bytes in |in|. -OPENSSL_EXPORT int KYBER_parse_private_key( - struct KYBER_private_key *out_private_key, CBS *in); - +// 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. If |mlkem| is 1, will use ML-KEM-512. +OPENSSL_EXPORT int 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], + int mlkem); + +// 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. If |mlkem| is 1, will use ML-KEM-768. +OPENSSL_EXPORT int 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], + int mlkem); + +// 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. If |mlkem| is 1, will use ML-KEM-512. +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, + int mlkem); + +// 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. If |mlkem| is 1, will use ML-KEM-768. +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, + int mlkem); + +// 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 diff --git a/src/include/openssl/nid.h b/src/include/openssl/nid.h index 4dd8841b1..23ffcd446 100644 --- a/src/include/openssl/nid.h +++ b/src/include/openssl/nid.h @@ -4255,6 +4255,23 @@ extern "C" { #define SN_X25519Kyber768Draft00 "X25519Kyber768Draft00" #define NID_X25519Kyber768Draft00 964 +#define SN_X25519Kyber512Draft00 "X25519Kyber512Draft00" +#define NID_X25519Kyber512Draft00 965 + +#define SN_P256Kyber768Draft00 "P256Kyber768Draft00" +#define NID_P256Kyber768Draft00 966 + +#define SN_X25519Kyber768Draft00Old "X25519Kyber768Draft00Old" +#define NID_X25519Kyber768Draft00Old 967 + +#define SN_X25519MLKEM768 "X25519MLKEM768" +#define NID_X25519MLKEM768 968 + +#define SN_ffdhe2048 "ffdhe2048" +#define NID_ffdhe2048 969 + +#define SN_ffdhe3072 "ffdhe3072" +#define NID_ffdhe3072 970 #if defined(__cplusplus) } /* extern C */ diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h index 53aa9b453..3791dfe5d 100644 --- a/src/include/openssl/ssl.h +++ b/src/include/openssl/ssl.h @@ -2378,6 +2378,13 @@ 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_X25519_KYBER768_DRAFT00 0x6399 +#define SSL_CURVE_X25519_KYBER512_DRAFT00 0xfe30 +#define SSL_CURVE_X25519_KYBER768_DRAFT00_OLD 0xfe31 +#define SSL_CURVE_P256_KYBER768_DRAFT00 0xfe32 +#define SSL_CURVE_X25519_MLKEM768 0x11ec + +#define SSL_CURVE_DHE2048 256 +#define SSL_CURVE_DHE3072 257 // SSL_get_curve_id returns the ID of the curve used by |ssl|'s most recently // completed handshake or 0 if not applicable. @@ -4570,6 +4577,22 @@ OPENSSL_EXPORT void SSL_CTX_set_permute_extensions(SSL_CTX *ctx, int enabled); // permute extensions. For now, this is only implemented for the ClientHello. OPENSSL_EXPORT void SSL_set_permute_extensions(SSL *ssl, int enabled); +// SSL_set_record_size_limit configures whether sockets on |ssl| should +// send record size limit extension. +OPENSSL_EXPORT void SSL_set_record_size_limit(SSL *ssl, uint16_t limit); + +// SSL_CTX_set_record_size_limit configures whether sockets on |ctx| should +// send record size limit extension. +OPENSSL_EXPORT void SSL_CTX_set_record_size_limit(SSL_CTX *ctx, uint16_t limit); + +// SSL_set_enable_three_key_shares configures whether sockets on |ssl| should +// send three key shares. +OPENSSL_EXPORT void SSL_set_enable_three_key_shares(SSL *ssl); + +// SSL_CTX_set_enable_three_key_shares configures whether sockets on |ctx| should +// send three key shares. +OPENSSL_EXPORT void SSL_CTX_set_enable_three_key_shares(SSL_CTX *ctx); + // SSL_max_seal_overhead returns the maximum overhead, in bytes, of sealing a // record with |ssl|. OPENSSL_EXPORT size_t SSL_max_seal_overhead(const SSL *ssl); @@ -4874,6 +4897,10 @@ OPENSSL_EXPORT int SSL_CTX_set1_sigalgs_list(SSL_CTX *ctx, const char *str); // more convenient to codesearch for specific algorithm values. OPENSSL_EXPORT int SSL_set1_sigalgs_list(SSL *ssl, const char *str); +// SSL_CTX_set_delegated_credentials sets the set of signature algorithms supported +// by the client. +OPENSSL_EXPORT int SSL_CTX_set_delegated_credentials(SSL_CTX *ctx, const char *str); + #define SSL_set_app_data(s, arg) (SSL_set_ex_data(s, 0, (char *)(arg))) #define SSL_get_app_data(s) (SSL_get_ex_data(s, 0)) #define SSL_SESSION_set_app_data(s, a) \ diff --git a/src/include/openssl/tls1.h b/src/include/openssl/tls1.h index 772fb87a3..4cb6b5667 100644 --- a/src/include/openssl/tls1.h +++ b/src/include/openssl/tls1.h @@ -181,6 +181,8 @@ extern "C" { #define TLS1_AD_NO_APPLICATION_PROTOCOL 120 #define TLS1_AD_ECH_REQUIRED 121 // draft-ietf-tls-esni-13 +#define TLSEXT_TYPE_record_size_limit 28 + // ExtensionType values from RFC 6066 #define TLSEXT_TYPE_server_name 0 #define TLSEXT_TYPE_status_request 5 diff --git a/src/sources.cmake b/src/sources.cmake index 5c7e881bf..3c0770cf3 100644 --- a/src/sources.cmake +++ b/src/sources.cmake @@ -66,8 +66,6 @@ set( crypto/fipsmodule/rand/ctrdrbg_vectors.txt crypto/hmac_extra/hmac_tests.txt crypto/hpke/hpke_test_vectors.txt - crypto/kyber/keccak_tests.txt - crypto/kyber/kyber_tests.txt crypto/pkcs8/test/empty_password.p12 crypto/pkcs8/test/no_encryption.p12 crypto/pkcs8/test/nss.p12 diff --git a/src/ssl/extensions.cc b/src/ssl/extensions.cc index 5ee280221..9a55a6b54 100644 --- a/src/ssl/extensions.cc +++ b/src/ssl/extensions.cc @@ -207,6 +207,10 @@ static bool tls1_check_duplicate_extensions(const CBS *cbs) { static bool is_post_quantum_group(uint16_t id) { switch (id) { case SSL_CURVE_X25519_KYBER768_DRAFT00: + case SSL_CURVE_X25519_KYBER768_DRAFT00_OLD: + case SSL_CURVE_X25519_KYBER512_DRAFT00: + case SSL_CURVE_P256_KYBER768_DRAFT00: + case SSL_CURVE_X25519_MLKEM768: return true; default: return false; @@ -2273,7 +2277,9 @@ bool ssl_setup_key_shares(SSL_HANDSHAKE *hs, uint16_t override_group_id) { SSL *const ssl = hs->ssl; hs->key_shares[0].reset(); hs->key_shares[1].reset(); + hs->key_shares[2].reset(); hs->key_share_bytes.Reset(); + const bool enable_three_key_shares = hs->ssl->config->three_key_shares; if (hs->max_version < TLS1_3_VERSION) { return true; @@ -2295,6 +2301,8 @@ bool ssl_setup_key_shares(SSL_HANDSHAKE *hs, uint16_t override_group_id) { uint16_t group_id = override_group_id; uint16_t second_group_id = 0; + uint16_t third_group_id = 0; + if (override_group_id == 0) { // Predict the most preferred group. Span groups = tls1_get_grouplist(hs); @@ -2305,12 +2313,21 @@ bool ssl_setup_key_shares(SSL_HANDSHAKE *hs, uint16_t override_group_id) { group_id = groups[0]; - // We'll try to include one post-quantum and one classical initial key - // share. - for (size_t i = 1; i < groups.size() && second_group_id == 0; i++) { - if (is_post_quantum_group(group_id) != is_post_quantum_group(groups[i])) { + // Include one post-quantum and one classical initial key share. + for (size_t i = 1; i < groups.size(); i++) { + if (second_group_id == 0 && is_post_quantum_group(group_id) != is_post_quantum_group(groups[i])) { second_group_id = groups[i]; - assert(second_group_id != group_id); + } else if (enable_three_key_shares && third_group_id == 0 && + is_post_quantum_group(group_id) != is_post_quantum_group(groups[i])) { + third_group_id = groups[i]; + } + + if (!enable_three_key_shares && second_group_id != 0) { + break; // Stop after finding the second group if three shares are not enabled. + } + + if (enable_three_key_shares && second_group_id != 0 && third_group_id != 0) { + break; // Stop after finding all three groups. } } } @@ -2334,6 +2351,16 @@ bool ssl_setup_key_shares(SSL_HANDSHAKE *hs, uint16_t override_group_id) { } } + if (enable_three_key_shares && third_group_id != 0) { + hs->key_shares[2] = SSLKeyShare::Create(third_group_id); + if (!hs->key_shares[2] || // + !CBB_add_u16(cbb.get(), third_group_id) || + !CBB_add_u16_length_prefixed(cbb.get(), &key_exchange) || + !hs->key_shares[2]->Generate(&key_exchange)) { + return false; + } + } + return CBBFinishArray(cbb.get(), &hs->key_share_bytes); } @@ -2808,9 +2835,30 @@ static bool ext_quic_transport_params_add_serverhello_legacy(SSL_HANDSHAKE *hs, static bool ext_delegated_credential_add_clienthello( const SSL_HANDSHAKE *hs, CBB *out, CBB *out_compressible, ssl_client_hello_type_t type) { + if (hs->config->delegated_credentials.empty()) { + return true; + } + + CBB contents, data; + const Array& signature_hash_algorithms = hs->config->delegated_credentials; + if (!CBB_add_u16(out, TLSEXT_TYPE_delegated_credential) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &data)) { + return false; + } + + for (const uint16_t alg : signature_hash_algorithms) { + if (!CBB_add_u16(&data, alg)) { + return false; + } + } + if (!CBB_flush(out)) { + return false; + } return true; } + static bool ext_delegated_credential_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert, CBS *contents) { @@ -3094,6 +3142,39 @@ bool ssl_negotiate_alps(SSL_HANDSHAKE *hs, uint8_t *out_alert, return true; } +static bool record_size_limit_add_clienthello(const SSL_HANDSHAKE* hs, CBB* out, + CBB* out_compressible, + ssl_client_hello_type_t type) { + if (hs->config->record_size_limit == 0) { + return true; + } + + CBB data; + const uint16_t data_ = hs->config->record_size_limit; + if (!CBB_add_u16(out, TLSEXT_TYPE_record_size_limit) || + !CBB_add_u16_length_prefixed(out, &data) || !CBB_add_u16(&data, data_) || + !CBB_flush(out)) { + return false; + } + return true; + } + +static bool record_size_limit_parse_serverhello(SSL_HANDSHAKE* hs, + uint8_t* out_alert, + CBS* contents) { + return true; +} + +static bool record_size_limit_parse_clienthello(SSL_HANDSHAKE* hs, + uint8_t* out_alert, + CBS* contents) { + return true; +} + +static bool record_size_limit_add_serverhello(SSL_HANDSHAKE* hs, CBB* out) { + return true; +} + // kExtensions contains all the supported extensions. static const struct tls_extension kExtensions[] = { { @@ -3267,6 +3348,13 @@ static const struct tls_extension kExtensions[] = { ignore_parse_clienthello, ext_alps_add_serverhello, }, + { + TLSEXT_TYPE_record_size_limit, + record_size_limit_add_clienthello, + record_size_limit_parse_serverhello, + record_size_limit_parse_clienthello, + record_size_limit_add_serverhello, + }, }; #define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension)) diff --git a/src/ssl/handshake_client.cc b/src/ssl/handshake_client.cc index 971ebd0b1..e70e6c868 100644 --- a/src/ssl/handshake_client.cc +++ b/src/ssl/handshake_client.cc @@ -215,13 +215,15 @@ static void ssl_get_client_disabled(const SSL_HANDSHAKE *hs, } } -static bool ssl_add_tls13_cipher(CBB *cbb, uint16_t cipher_id, - ssl_compliance_policy_t policy) { - if (ssl_tls13_cipher_meets_policy(cipher_id, policy)) { - return CBB_add_u16(cbb, cipher_id); - } - return true; -} +// Comment this part of the code to cancel the device AES encryption cipher sequence priority, which may affect performance. +// Compatible with some Firefox cipher sequence order +// static bool ssl_add_tls13_cipher(CBB *cbb, uint16_t cipher_id, +// ssl_compliance_policy_t policy) { +// if (ssl_tls13_cipher_meets_policy(cipher_id, policy)) { +// return CBB_add_u16(cbb, cipher_id); +// } +// return true; +// } static bool ssl_write_client_cipher_list(const SSL_HANDSHAKE *hs, CBB *out, ssl_client_hello_type_t type) { @@ -242,26 +244,28 @@ static bool ssl_write_client_cipher_list(const SSL_HANDSHAKE *hs, CBB *out, // Add TLS 1.3 ciphers. Order ChaCha20-Poly1305 relative to AES-GCM based on // hardware support. - if (hs->max_version >= TLS1_3_VERSION) { - const bool has_aes_hw = ssl->config->aes_hw_override - ? ssl->config->aes_hw_override_value - : EVP_has_aes_hardware(); - - if ((!has_aes_hw && // - !ssl_add_tls13_cipher(&child, - TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, - ssl->config->tls13_cipher_policy)) || - !ssl_add_tls13_cipher(&child, TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff, - ssl->config->tls13_cipher_policy) || - !ssl_add_tls13_cipher(&child, TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff, - ssl->config->tls13_cipher_policy) || - (has_aes_hw && // - !ssl_add_tls13_cipher(&child, - TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, - ssl->config->tls13_cipher_policy))) { - return false; - } - } + // Comment this part of the code to cancel the device AES encryption cipher sequence priority, which may affect performance. + // Compatible with some Firefox cipher sequence order + // if (hs->max_version >= TLS1_3_VERSION) { + // const bool has_aes_hw = ssl->config->aes_hw_override + // ? ssl->config->aes_hw_override_value + // : EVP_has_aes_hardware(); + + // if ((!has_aes_hw && // + // !ssl_add_tls13_cipher(&child, + // TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, + // ssl->config->tls13_cipher_policy)) || + // !ssl_add_tls13_cipher(&child, TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff, + // ssl->config->tls13_cipher_policy) || + // !ssl_add_tls13_cipher(&child, TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff, + // ssl->config->tls13_cipher_policy) || + // (has_aes_hw && // + // !ssl_add_tls13_cipher(&child, + // TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, + // ssl->config->tls13_cipher_policy))) { + // return false; + // } + // } if (hs->min_version < TLS1_3_VERSION && type != ssl_client_hello_inner) { bool any_enabled = false; diff --git a/src/ssl/internal.h b/src/ssl/internal.h index 1e6da2153..95e94e5ad 100644 --- a/src/ssl/internal.h +++ b/src/ssl/internal.h @@ -554,8 +554,13 @@ BSSL_NAMESPACE_BEGIN // Bits for |algorithm_mac| (symmetric authentication). #define SSL_SHA1 0x00000001u #define SSL_SHA256 0x00000002u +// +// SSL_SHA384 was removed in +// https://boringssl-review.googlesource.com/c/boringssl/+/27944/ +// but restored to impersonate browsers with older ciphers. +#define SSL_SHA384 0x00000004u // SSL_AEAD is set for all AEADs. -#define SSL_AEAD 0x00000004u +#define SSL_AEAD 0x00000008u // Bits for |algorithm_prf| (handshake digest). #define SSL_HANDSHAKE_MAC_DEFAULT 0x1 @@ -1818,7 +1823,7 @@ struct SSL_HANDSHAKE { // key_shares are the current key exchange instances. The second is only used // as a client if we believe that we should offer two key shares in a // ClientHello. - UniquePtr key_shares[2]; + UniquePtr key_shares[3]; // transcript is the current handshake transcript. SSLTranscript transcript; @@ -3058,6 +3063,10 @@ struct SSL_CONFIG { // verify_sigalgs, if not empty, is the set of signature algorithms // accepted from the peer in decreasing order of preference. Array verify_sigalgs; + + // delegated_credentials, if not empty, is the set of signature algorithms + // supported by the client. + Array delegated_credentials; // srtp_profiles is the list of configured SRTP protection profiles for // DTLS-SRTP. @@ -3128,6 +3137,12 @@ struct SSL_CONFIG { // of support for AES hw. The value is only considered if |aes_hw_override| is // true. bool aes_hw_override_value : 1; + + // record_size_limit is whether to send record size limit extension. + uint16_t record_size_limit = 0; + + // enable_three_key_shares is whether to send three key shares. + bool three_key_shares : 1; }; // From RFC 8446, used in determining PSK modes. @@ -3696,6 +3711,10 @@ struct ssl_ctx_st { // accepted from the peer in decreasing order of preference. bssl::Array verify_sigalgs; + // delegated_credentials, if not empty, is the set of signature algorithms + // supported by the client. + bssl::Array delegated_credentials; + // retain_only_sha256_of_client_certs is true if we should compute the SHA256 // hash of the peer's certificate and then discard it to save memory and // session space. Only effective on the server side. @@ -3748,6 +3767,12 @@ struct ssl_ctx_st { // |aes_hw_override| is true. bool aes_hw_override_value : 1; + // record_size_limit is whether to send record size limit extension. + uint16_t record_size_limit = 0; + + // enable_three_key_shares is whether to send three key shares. + bool three_key_shares : 1; + private: ~ssl_ctx_st(); friend OPENSSL_EXPORT void SSL_CTX_free(SSL_CTX *); diff --git a/src/ssl/ssl_cipher.cc b/src/ssl/ssl_cipher.cc index ebb075351..17fcaa13c 100644 --- a/src/ssl/ssl_cipher.cc +++ b/src/ssl/ssl_cipher.cc @@ -197,6 +197,37 @@ static constexpr SSL_CIPHER kCiphers[] = { SSL_HANDSHAKE_MAC_DEFAULT, }, + // Ciphers 3C, 3D were removed in + // https://boringssl-review.googlesource.com/c/boringssl/+/27944/ + // but restored here to impersonate browsers with older ciphers. They are + // not expected to actually work; but just to be included in the TLS + // Client Hello. + + // TLS v1.2 ciphersuites + + // Cipher 3C + { + TLS1_TXT_RSA_WITH_AES_128_SHA256, + "TLS_RSA_WITH_AES_128_CBC_SHA256", + TLS1_CK_RSA_WITH_AES_128_SHA256, + SSL_kRSA, + SSL_aRSA, + SSL_AES128, + SSL_SHA256, + SSL_HANDSHAKE_MAC_SHA256, + }, + // Cipher 3D + { + TLS1_TXT_RSA_WITH_AES_256_SHA256, + "TLS_RSA_WITH_AES_256_CBC_SHA256", + TLS1_CK_RSA_WITH_AES_256_SHA256, + SSL_kRSA, + SSL_aRSA, + SSL_AES256, + SSL_SHA256, + SSL_HANDSHAKE_MAC_SHA256, + }, + // PSK cipher suites. // Cipher 8C @@ -287,6 +318,23 @@ static constexpr SSL_CIPHER kCiphers[] = { SSL_HANDSHAKE_MAC_SHA256, }, + // Cipher C008 was missing from BoringSSL, + // probably because it is weak. Add it back from OpenSSL (ssl/s3_lib.c) + // where it is called ECDHE-ECDSA-DES-CBC3-SHA. + // It's not supposed to really work but just appear in the TLS client hello. + + // Cipher C008 + { + "ECDHE-ECDSA-DES-CBC3-SHA", + "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", + 0x0300C008, + SSL_kECDHE, + SSL_aECDSA, + SSL_3DES, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + // Cipher C009 { TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, @@ -311,6 +359,21 @@ static constexpr SSL_CIPHER kCiphers[] = { SSL_HANDSHAKE_MAC_DEFAULT, }, + // Cipher C012 was missing from BoringSSL, + // probably because it is weak. Add it back from OpenSSL (ssl/s3_lib.c) + // where it is called ECDHE-RSA-DES-CBC3-SHA + // It's not supposed to really work but just appear in the TLS client hello. + { + "ECDHE-RSA-DES-CBC3-SHA", + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", + 0x0300C012, + SSL_kECDHE, + SSL_aRSA, + SSL_3DES, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + // Cipher C013 { TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA, @@ -335,6 +398,37 @@ static constexpr SSL_CIPHER kCiphers[] = { SSL_HANDSHAKE_MAC_DEFAULT, }, + // Ciphers C023, C024, C028 were removed in + // https://boringssl-review.googlesource.com/c/boringssl/+/27944/ + // but restored here to impersonate browsers with older ciphers. They are + // not expected to actually work; but just to be included in the TLS + // Client Hello. + + // HMAC based TLS v1.2 ciphersuites from RFC5289 + + // Cipher C023 + { + TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_SHA256, + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256, + SSL_kECDHE, + SSL_aECDSA, + SSL_AES128, + SSL_SHA256, + SSL_HANDSHAKE_MAC_SHA256, + }, + // Cipher C024 + { + TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_SHA384, + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", + TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384, + SSL_kECDHE, + SSL_aECDSA, + SSL_AES256, + SSL_SHA384, + SSL_HANDSHAKE_MAC_SHA384, + }, + // Cipher C027 { TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA256, @@ -347,6 +441,18 @@ static constexpr SSL_CIPHER kCiphers[] = { SSL_HANDSHAKE_MAC_SHA256, }, + // Cipher C028 + { + TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384, + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", + TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384, + SSL_kECDHE, + SSL_aRSA, + SSL_AES256, + SSL_SHA384, + SSL_HANDSHAKE_MAC_SHA384, + }, + // GCM based TLS v1.2 ciphersuites from RFC 5289 // Cipher C02B @@ -467,15 +573,17 @@ Span AllCiphers() { return MakeConstSpan(kCiphers, OPENSSL_ARRAY_SIZE(kCiphers)); } -static constexpr size_t NumTLS13Ciphers() { - size_t num = 0; - for (const auto &cipher : kCiphers) { - if (cipher.algorithm_mkey == SSL_kGENERIC) { - num++; - } - } - return num; -} +// Comment this part of the code to cancel the device AES encryption cipher sequence priority, which may affect performance. +// Compatible with some Firefox cipher sequence order +// static constexpr size_t NumTLS13Ciphers() { +// size_t num = 0; +// for (const auto &cipher : kCiphers) { +// if (cipher.algorithm_mkey == SSL_kGENERIC) { +// num++; +// } +// } +// return num; +// } #define CIPHER_ADD 1 #define CIPHER_KILL 2 @@ -549,6 +657,11 @@ static const CIPHER_ALIAS kCipherAliases[] = { // MAC aliases {"SHA1", ~0u, ~0u, ~0u, SSL_SHA1, 0}, + // + // Removed in https://boringssl-review.googlesource.com/c/boringssl/+/27944/ + // but restored to impersonate browsers with older ciphers. + {"SHA256", ~0u, ~0u, ~0u, SSL_SHA256, 0}, + {"SHA384", ~0u, ~0u, ~0u, SSL_SHA384, 0}, {"SHA", ~0u, ~0u, ~0u, SSL_SHA1, 0}, // Legacy protocol minimum version aliases. "TLSv1" is intentionally the @@ -1166,12 +1279,30 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, TLS1_CK_RSA_WITH_AES_256_SHA & 0xffff, TLS1_CK_PSK_WITH_AES_256_CBC_SHA & 0xffff, SSL3_CK_RSA_DES_192_CBC3_SHA & 0xffff, + // add legacy cipehrs. + TLS1_CK_RSA_WITH_AES_128_SHA256 & 0xffff, + TLS1_CK_RSA_WITH_AES_256_SHA256 & 0xffff, + 0x0300C008 & 0xffff, + 0x0300C012 & 0xffff, + TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256 & 0xffff, + TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384 & 0xffff, + TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384 & 0xffff, + }; + // Comment this part of the code to cancel the device AES encryption cipher sequence priority, which may affect performance. + // Compatible with some Firefox cipher sequence order + static const uint16_t kTLS13Ciphers[] = { + TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff, + TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff, + TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, }; // Set up a linked list of ciphers. - CIPHER_ORDER co_list[OPENSSL_ARRAY_SIZE(kAESCiphers) + - OPENSSL_ARRAY_SIZE(kChaChaCiphers) + - OPENSSL_ARRAY_SIZE(kLegacyCiphers)]; + // Comment this part of the code to cancel the device AES encryption cipher sequence priority, which may affect performance. + // Compatible with some Firefox cipher sequence order + // CIPHER_ORDER co_list[OPENSSL_ARRAY_SIZE(kAESCiphers) + + // OPENSSL_ARRAY_SIZE(kChaChaCiphers) + + // OPENSSL_ARRAY_SIZE(kLegacyCiphers)]; + CIPHER_ORDER co_list[OPENSSL_ARRAY_SIZE(kCiphers)]; for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(co_list); i++) { co_list[i].next = i + 1 < OPENSSL_ARRAY_SIZE(co_list) ? &co_list[i + 1] : nullptr; @@ -1207,8 +1338,17 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, co_list[num++].cipher = SSL_get_cipher_by_value(id); assert(co_list[num - 1].cipher != nullptr); } + for (uint16_t id: kTLS13Ciphers) { + co_list[num++].cipher = SSL_get_cipher_by_value(id); + assert(co_list[num - 1].cipher != nullptr); + } assert(num == OPENSSL_ARRAY_SIZE(co_list)); - static_assert(OPENSSL_ARRAY_SIZE(co_list) + NumTLS13Ciphers() == + // Comment this part of the code to cancel the device AES encryption cipher sequence priority, which may affect performance. + // Compatible with some Firefox cipher sequence order + // static_assert(OPENSSL_ARRAY_SIZE(co_list) + NumTLS13Ciphers() == + // OPENSSL_ARRAY_SIZE(kCiphers), + // "Not all ciphers are included in the cipher order"); + static_assert(OPENSSL_ARRAY_SIZE(co_list) == OPENSSL_ARRAY_SIZE(kCiphers), "Not all ciphers are included in the cipher order"); diff --git a/src/ssl/ssl_key_share.cc b/src/ssl/ssl_key_share.cc index 09a9ad380..a972e8dd1 100644 --- a/src/ssl/ssl_key_share.cc +++ b/src/ssl/ssl_key_share.cc @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -193,63 +194,292 @@ class X25519KeyShare : public SSLKeyShare { uint8_t private_key_[32]; }; -class X25519Kyber768KeyShare : public SSLKeyShare { +class P256Kyber768Draft00KeyShare : public SSLKeyShare { public: - X25519Kyber768KeyShare() {} + P256Kyber768Draft00KeyShare() {} + + uint16_t GroupID() const override { return SSL_CURVE_P256_KYBER768_DRAFT00; } + + bool Generate(CBB *out) override { + assert(!p256_private_key_); + + // Set up a shared |BN_CTX| for P-256 operations. + UniquePtr bn_ctx(BN_CTX_new()); + if (!bn_ctx) { + return false; + } + + BN_CTXScope scope(bn_ctx.get()); + + // Generate a P-256 private key. + UniquePtr 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 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); - uint16_t GroupID() const override { - return SSL_CURVE_X25519_KYBER768_DRAFT00; + if (!CBB_add_bytes(out, kyber_public_key_bytes, + sizeof(kyber_public_key_bytes))) { + return false; + } + + return true; } + bool Encap(CBB *out_public_key, Array *out_secret, + uint8_t *out_alert, Span 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_new()); + if (!bn_ctx) { + return false; + } + + BN_CTXScope scope(bn_ctx.get()); + + UniquePtr group; + group.reset(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); + if (!group) { + return false; + } + + // Parse peer point + UniquePtr peer_point(EC_POINT_new(group.get())); + UniquePtr 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 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 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)); + + if(!KYBER768_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy, 0)) { + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return false; + } + if(!CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { + return false; + } + + *out_secret = std::move(secret); + return true; + } + + bool Decap(Array *out_secret, uint8_t *out_alert, + Span peer_key) override { + assert(p256_private_key_); + *out_alert = SSL_AD_INTERNAL_ERROR; + + Array secret; + if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return false; + } + + if (peer_key.size() != 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_new()); + if (!bn_ctx) { + return false; + } + + BN_CTXScope scope(bn_ctx.get()); + + UniquePtr group; + group.reset(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); + if (!group) { + return false; + } + + // Parse peer point + UniquePtr peer_point(EC_POINT_new(group.get())); + UniquePtr 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, 0); + + *out_secret = std::move(secret); + return true; + } + + private: + UniquePtr p256_private_key_; + KYBER768_private_key kyber_private_key_; +}; + +class X25519Kyber768Draft00KeyShare : public SSLKeyShare { + public: + X25519Kyber768Draft00KeyShare(uint16_t group_id) : group_id_(group_id) { + assert(group_id == SSL_CURVE_X25519_KYBER768_DRAFT00 + || group_id == SSL_CURVE_X25519_KYBER768_DRAFT00_OLD); + } + + uint16_t GroupID() const override { return group_id_; } + bool Generate(CBB *out) override { uint8_t x25519_public_key[32]; X25519_keypair(x25519_public_key, x25519_private_key_); - uint8_t kyber_public_key[KYBER_PUBLIC_KEY_BYTES]; - KYBER_generate_key(kyber_public_key, &kyber_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, sizeof(kyber_public_key))) { + !CBB_add_bytes(out, kyber_public_key_bytes, + sizeof(kyber_public_key_bytes))) { return false; } return true; } - bool Encap(CBB *out_ciphertext, Array *out_secret, - uint8_t *out_alert, Span peer_key) override { + bool Encap(CBB *out_public_key, Array *out_secret, + uint8_t *out_alert, Span peer_key) override { Array secret; - if (!secret.Init(32 + 32)) { + 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_); - KYBER_public_key peer_kyber_pub; - CBS peer_key_cbs; - CBS peer_x25519_cbs; - CBS peer_kyber_cbs; - CBS_init(&peer_key_cbs, peer_key.data(), peer_key.size()); - if (!CBS_get_bytes(&peer_key_cbs, &peer_x25519_cbs, 32) || - !CBS_get_bytes(&peer_key_cbs, &peer_kyber_cbs, - KYBER_PUBLIC_KEY_BYTES) || - CBS_len(&peer_key_cbs) != 0 || - !X25519(secret.data(), x25519_private_key_, - CBS_data(&peer_x25519_cbs)) || - !KYBER_parse_public_key(&peer_kyber_pub, &peer_kyber_cbs)) { + + 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 kyber_ciphertext[KYBER_CIPHERTEXT_BYTES]; - KYBER_encap(kyber_ciphertext, secret.data() + 32, secret.size() - 32, - &peer_kyber_pub); + uint8_t ciphertext[KYBER768_CIPHERTEXT_BYTES]; + uint8_t entropy[KYBER_ENCAP_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); - if (!CBB_add_bytes(out_ciphertext, x25519_public_key, + if(!KYBER768_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy, 0)) { + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return false; + } + if(!CBB_add_bytes(out_public_key, x25519_public_key, sizeof(x25519_public_key)) || - !CBB_add_bytes(out_ciphertext, kyber_ciphertext, - sizeof(kyber_ciphertext))) { + !CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) { return false; } @@ -258,30 +488,233 @@ class X25519Kyber768KeyShare : public SSLKeyShare { } bool Decap(Array *out_secret, uint8_t *out_alert, - Span ciphertext) override { + Span peer_key) override { + *out_alert = SSL_AD_INTERNAL_ERROR; + + Array secret; + if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return false; + } + + if (peer_key.size() != 32 + KYBER768_CIPHERTEXT_BYTES || + !X25519(secret.data(), x25519_private_key_, peer_key.data())) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + + KYBER768_decap(secret.data() + 32, &kyber_private_key_, + peer_key.data() + 32, peer_key.size() - 32, 0); + + *out_secret = std::move(secret); + return true; + } + + private: + uint8_t x25519_private_key_[32]; + KYBER768_private_key kyber_private_key_; + uint16_t group_id_; +}; + +class X25519MLKEM768KeyShare : public SSLKeyShare { + public: + X25519MLKEM768KeyShare() {} + + uint16_t GroupID() const override { return SSL_CURVE_X25519_MLKEM768; } + + bool Generate(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, kyber_public_key_bytes, sizeof(kyber_public_key_bytes)) || + !CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key))) { + return false; + } + + return true; + } + + bool Encap(CBB *out_public_key, Array *out_secret, + uint8_t *out_alert, Span peer_key) override { + Array secret; + if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return false; + } + + uint8_t x25519_public_key[32]; + X25519_keypair(x25519_public_key, x25519_private_key_); + + KYBER768_public_key peer_public_key; + if (peer_key.size() != KYBER768_PUBLIC_KEY_BYTES + 32) { + *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()); + + if (!X25519(secret.data() + 32, x25519_private_key_, + peer_key.data() + KYBER768_PUBLIC_KEY_BYTES)) { + *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)); + + if(!KYBER768_encap(ciphertext, secret.data(), &peer_public_key, entropy, 1)) { + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return false; + } + if(!CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext)) || + !CBB_add_bytes(out_public_key, x25519_public_key, sizeof(x25519_public_key))) { + return false; + } + + *out_secret = std::move(secret); + return true; + } + + bool Decap(Array *out_secret, uint8_t *out_alert, + Span peer_key) override { *out_alert = SSL_AD_INTERNAL_ERROR; Array secret; - if (!secret.Init(32 + 32)) { + if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return false; } - if (ciphertext.size() != 32 + KYBER_CIPHERTEXT_BYTES || - !X25519(secret.data(), x25519_private_key_, ciphertext.data())) { + if (peer_key.size() != KYBER768_CIPHERTEXT_BYTES + 32 || + !X25519(secret.data() + 32, x25519_private_key_, + peer_key.data() + KYBER768_CIPHERTEXT_BYTES )) { *out_alert = SSL_AD_DECODE_ERROR; OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); return false; } - KYBER_decap(secret.data() + 32, secret.size() - 32, ciphertext.data() + 32, - &kyber_private_key_); + KYBER768_decap(secret.data(), &kyber_private_key_, + peer_key.data(), peer_key.size() - 32, 1); + + *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_X25519_KYBER512_DRAFT00; } + + bool Generate(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 Encap(CBB *out_public_key, Array *out_secret, + uint8_t *out_alert, Span peer_key) override { + Array secret; + if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return false; + } + + uint8_t x25519_public_key[32]; + X25519_keypair(x25519_public_key, x25519_private_key_); + + KYBER512_public_key peer_public_key; + if (peer_key.size() != 32 + KYBER512_PUBLIC_KEY_BYTES) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + + KYBER512_parse_public_key(&peer_public_key, peer_key.data() + 32); + + if (!X25519(secret.data(), x25519_private_key_, peer_key.data())) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + + uint8_t ciphertext[KYBER512_CIPHERTEXT_BYTES]; + uint8_t entropy[KYBER_ENCAP_BYTES]; + RAND_bytes(entropy, sizeof(entropy)); + + if(!KYBER512_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy, 0)) { + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return false; + } + 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 Decap(Array *out_secret, uint8_t *out_alert, + Span peer_key) override { + *out_alert = SSL_AD_INTERNAL_ERROR; + + Array secret; + if (!secret.Init(32 + KYBER_KEY_BYTES)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return false; + } + + if (peer_key.size() != 32 + KYBER512_CIPHERTEXT_BYTES || + !X25519(secret.data(), x25519_private_key_, peer_key.data())) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return false; + } + + KYBER512_decap(secret.data() + 32, &kyber_private_key_, + peer_key.data() + 32, peer_key.size() - 32, 0); + *out_secret = std::move(secret); return true; } private: uint8_t x25519_private_key_[32]; - KYBER_private_key kyber_private_key_; + KYBER512_private_key kyber_private_key_; }; constexpr NamedGroup kNamedGroups[] = { @@ -290,8 +723,20 @@ constexpr NamedGroup kNamedGroups[] = { {NID_secp384r1, SSL_CURVE_SECP384R1, "P-384", "secp384r1"}, {NID_secp521r1, SSL_CURVE_SECP521R1, "P-521", "secp521r1"}, {NID_X25519, SSL_CURVE_X25519, "X25519", "x25519"}, + {NID_X25519Kyber512Draft00, SSL_CURVE_X25519_KYBER512_DRAFT00, + "X25519Kyber512Draft00", "Xyber512D00"}, {NID_X25519Kyber768Draft00, SSL_CURVE_X25519_KYBER768_DRAFT00, - "X25519Kyber768Draft00", ""}, + "X25519Kyber768Draft00", "Xyber768D00"}, + {NID_X25519Kyber768Draft00Old, SSL_CURVE_X25519_KYBER768_DRAFT00_OLD, + "X25519Kyber768Draft00Old", "Xyber768D00Old"}, + {NID_P256Kyber768Draft00, SSL_CURVE_P256_KYBER768_DRAFT00, + "P256Kyber768Draft00", "P256Kyber768D00"}, + {NID_X25519MLKEM768, SSL_CURVE_X25519_MLKEM768, + "X25519MLKEM768", "X25519MLKEM768"}, + + //unspport group but add them + {NID_ffdhe2048, SSL_CURVE_DHE2048, "dhe2048", "ffdhe2048"}, + {NID_ffdhe3072, SSL_CURVE_DHE3072, "dhe3072", "ffdhe3072"}, }; } // namespace @@ -312,8 +757,18 @@ UniquePtr SSLKeyShare::Create(uint16_t group_id) { return MakeUnique(NID_secp521r1, SSL_CURVE_SECP521R1); case SSL_CURVE_X25519: return MakeUnique(); + case SSL_CURVE_X25519_KYBER512_DRAFT00: + return UniquePtr(New()); case SSL_CURVE_X25519_KYBER768_DRAFT00: - return MakeUnique(); + return UniquePtr(New( + group_id)); + case SSL_CURVE_X25519_KYBER768_DRAFT00_OLD: + return UniquePtr(New( + group_id)); + case SSL_CURVE_P256_KYBER768_DRAFT00: + return UniquePtr(New()); + case SSL_CURVE_X25519_MLKEM768: + return UniquePtr(New()); default: return nullptr; } diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc index 838761af5..5eaa8953b 100644 --- a/src/ssl/ssl_lib.cc +++ b/src/ssl/ssl_lib.cc @@ -537,7 +537,8 @@ ssl_ctx_st::ssl_ctx_st(const SSL_METHOD *ssl_method) handoff(false), enable_early_data(false), aes_hw_override(false), - aes_hw_override_value(false) { + aes_hw_override_value(false), + three_key_shares(false) { CRYPTO_MUTEX_init(&lock); CRYPTO_new_ex_data(&ex_data); } @@ -660,11 +661,13 @@ SSL *SSL_new(SSL_CTX *ctx) { ssl->config->aes_hw_override = ctx->aes_hw_override; ssl->config->aes_hw_override_value = ctx->aes_hw_override_value; ssl->config->tls13_cipher_policy = ctx->tls13_cipher_policy; + ssl->config->three_key_shares = ctx->three_key_shares; if (!ssl->config->supported_group_list.CopyFrom(ctx->supported_group_list) || !ssl->config->alpn_client_proto_list.CopyFrom( ctx->alpn_client_proto_list) || - !ssl->config->verify_sigalgs.CopyFrom(ctx->verify_sigalgs)) { + !ssl->config->verify_sigalgs.CopyFrom(ctx->verify_sigalgs) || + !ssl->config->delegated_credentials.CopyFrom(ctx->delegated_credentials)) { return nullptr; } @@ -684,6 +687,7 @@ SSL *SSL_new(SSL_CTX *ctx) { ssl->config->signed_cert_timestamps_enabled = ctx->signed_cert_timestamps_enabled; ssl->config->ocsp_stapling_enabled = ctx->ocsp_stapling_enabled; + ssl->config->record_size_limit = ctx->record_size_limit; ssl->config->handoff = ctx->handoff; ssl->quic_method = ctx->quic_method; @@ -707,7 +711,8 @@ SSL_CONFIG::SSL_CONFIG(SSL *ssl_arg) shed_handshake_config(false), jdk11_workaround(false), quic_use_legacy_codepoint(false), - permute_extensions(false) { + permute_extensions(false), + three_key_shares(false) { assert(ssl); } @@ -2134,6 +2139,28 @@ void SSL_enable_ocsp_stapling(SSL *ssl) { ssl->config->ocsp_stapling_enabled = true; } +void SSL_set_record_size_limit(SSL *ssl, uint16_t limit) { + if (!ssl->config) { + return; + } + ssl->config->record_size_limit = limit; +} + +void SSL_CTX_set_record_size_limit(SSL_CTX *ctx, uint16_t limit) { + ctx->record_size_limit = limit; +} + +void SSL_set_enable_three_key_shares(SSL *ssl) { + if (!ssl->config) { + return; + } + ssl->config->three_key_shares = true; +} + +void SSL_CTX_set_enable_three_key_shares(SSL_CTX *ctx) { + ctx->three_key_shares = true; +} + void SSL_get0_signed_cert_timestamp_list(const SSL *ssl, const uint8_t **out, size_t *out_len) { SSL_SESSION *session = SSL_get_session(ssl); @@ -3151,7 +3178,7 @@ namespace fips202205 { // Section 3.3.1 // "The server shall be configured to only use cipher suites that are // composed entirely of NIST approved algorithms" -static const int kCurves[] = {NID_X9_62_prime256v1, NID_secp384r1}; +static const int kCurves[] = {NID_P256Kyber768Draft00, NID_X9_62_prime256v1, NID_secp384r1}; static const uint16_t kSigAlgs[] = { SSL_SIGN_RSA_PKCS1_SHA256, diff --git a/src/ssl/ssl_privkey.cc b/src/ssl/ssl_privkey.cc index 46bef32e8..193ca5c84 100644 --- a/src/ssl/ssl_privkey.cc +++ b/src/ssl/ssl_privkey.cc @@ -567,44 +567,49 @@ int SSL_is_signature_algorithm_rsa_pss(uint16_t sigalg) { return alg != nullptr && alg->is_rsa_pss; } -static int compare_uint16_t(const void *p1, const void *p2) { - uint16_t u1 = *((const uint16_t *)p1); - uint16_t u2 = *((const uint16_t *)p2); - if (u1 < u2) { - return -1; - } else if (u1 > u2) { - return 1; - } else { - return 0; - } -} - -static bool sigalgs_unique(Span in_sigalgs) { - if (in_sigalgs.size() < 2) { - return true; - } - - Array sigalgs; - if (!sigalgs.CopyFrom(in_sigalgs)) { - return false; - } - - qsort(sigalgs.data(), sigalgs.size(), sizeof(uint16_t), compare_uint16_t); - - for (size_t i = 1; i < sigalgs.size(); i++) { - if (sigalgs[i - 1] == sigalgs[i]) { - OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_SIGNATURE_ALGORITHM); - return false; - } - } - - return true; -} +// Remove the uniqueness check. Older Safari versions (15) +// send out duplicated algorithm prefs. +// static int compare_uint16_t(const void *p1, const void *p2) { +// uint16_t u1 = *((const uint16_t *)p1); +// uint16_t u2 = *((const uint16_t *)p2); +// if (u1 < u2) { +// return -1; +// } else if (u1 > u2) { +// return 1; +// } else { +// return 0; +// } +// } + +// static bool sigalgs_unique(Span in_sigalgs) { +// if (in_sigalgs.size() < 2) { +// return true; +// } +// +// Array sigalgs; +// if (!sigalgs.CopyFrom(in_sigalgs)) { +// return false; +// } +// +// qsort(sigalgs.data(), sigalgs.size(), sizeof(uint16_t), compare_uint16_t); +// +// for (size_t i = 1; i < sigalgs.size(); i++) { +// if (sigalgs[i - 1] == sigalgs[i]) { +// OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_SIGNATURE_ALGORITHM); +// return false; +// } +// } +// +// return true; +// } static bool set_sigalg_prefs(Array *out, Span prefs) { - if (!sigalgs_unique(prefs)) { - return false; - } + // Remove the uniqueness check. Older Safari versions (15) + // send out duplicated algorithm prefs. + + // if (!sigalgs_unique(prefs)) { + // return false; + // } // Check for invalid algorithms, and filter out |SSL_SIGN_RSA_PKCS1_MD5_SHA1|. Array filtered; @@ -947,3 +952,13 @@ int SSL_set_verify_algorithm_prefs(SSL *ssl, const uint16_t *prefs, return set_sigalg_prefs(&ssl->config->verify_sigalgs, MakeConstSpan(prefs, num_prefs)); } + +int SSL_CTX_set_delegated_credentials(SSL_CTX *ctx, const char *str) { + Array sigalgs; + if (!parse_sigalgs_list(&sigalgs, str)) { + return 0; + } + + return set_sigalg_prefs(&ctx->delegated_credentials, + MakeConstSpan(sigalgs.data(), sigalgs.size())); +} diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc index ef43a9e98..22178b5f6 100644 --- a/src/ssl/ssl_test.cc +++ b/src/ssl/ssl_test.cc @@ -409,7 +409,34 @@ static const CurveTest kCurveTests[] = { "P-256:X25519Kyber768Draft00", { SSL_CURVE_SECP256R1, SSL_CURVE_X25519_KYBER768_DRAFT00 }, }, - + { + "Xyber512D00", + { SSL_CURVE_X25519_KYBER512_DRAFT00 }, + }, + { + "Xyber768D00", + { SSL_CURVE_X25519_KYBER768_DRAFT00 }, + }, + { + "Xyber768D00:Xyber768D00Old", + { SSL_CURVE_X25519_KYBER768_DRAFT00, SSL_CURVE_X25519_KYBER768_DRAFT00_OLD }, + }, + { + "P-256:Xyber512D00", + { SSL_CURVE_SECP256R1, SSL_CURVE_X25519_KYBER512_DRAFT00 }, + }, + { + "P256Kyber768D00", + { SSL_CURVE_P256_KYBER768_DRAFT00 }, + }, + { + "X25519MLKEM768", + { SSL_CURVE_X25519_MLKEM768 }, + }, + { + "P-256:P256Kyber768D00", + { SSL_CURVE_SECP256R1, SSL_CURVE_P256_KYBER768_DRAFT00 }, + }, { "P-256:P-384:P-521:X25519", { diff --git a/src/tool/speed.cc b/src/tool/speed.cc index 5b0205953..6b3c67dab 100644 --- a/src/tool/speed.cc +++ b/src/tool/speed.cc @@ -904,6 +904,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, 0); + 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), 0); + 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, 0); + 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), 0); + 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; @@ -958,55 +1068,6 @@ static bool SpeedHRSS(const std::string &selected) { return true; } -static bool SpeedKyber(const std::string &selected) { - if (!selected.empty() && selected != "Kyber") { - return true; - } - - TimeResults results; - - KYBER_private_key priv; - uint8_t encoded_public_key[KYBER_PUBLIC_KEY_BYTES]; - uint8_t ciphertext[KYBER_CIPHERTEXT_BYTES]; - // This ciphertext is nonsense, but Kyber decap is constant-time so, for the - // purposes of timing, it's fine. - memset(ciphertext, 42, sizeof(ciphertext)); - if (!TimeFunction(&results, - [&priv, &encoded_public_key, &ciphertext]() -> bool { - uint8_t shared_secret[32]; - KYBER_generate_key(encoded_public_key, &priv); - KYBER_decap(shared_secret, sizeof(shared_secret), - ciphertext, &priv); - return true; - })) { - fprintf(stderr, "Failed to time KYBER_generate_key + KYBER_decap.\n"); - return false; - } - - results.Print("Kyber generate + decap"); - - KYBER_public_key pub; - if (!TimeFunction( - &results, [&pub, &ciphertext, &encoded_public_key]() -> bool { - CBS encoded_public_key_cbs; - CBS_init(&encoded_public_key_cbs, encoded_public_key, - sizeof(encoded_public_key)); - if (!KYBER_parse_public_key(&pub, &encoded_public_key_cbs)) { - return false; - } - uint8_t shared_secret[32]; - KYBER_encap(ciphertext, shared_secret, sizeof(shared_secret), &pub); - return true; - })) { - fprintf(stderr, "Failed to time KYBER_encap.\n"); - return false; - } - - results.Print("Kyber parse + encap"); - - return true; -} - static bool SpeedHashToCurve(const std::string &selected) { if (!selected.empty() && selected.find("hashtocurve") == std::string::npos) { return true; @@ -1487,7 +1548,8 @@ bool Speed(const std::vector &args) { !SpeedScrypt(selected) || !SpeedRSAKeyGen(selected) || !SpeedHRSS(selected) || - !SpeedKyber(selected) || + !SpeedKyber512(selected) || + !SpeedKyber768(selected) || !SpeedHashToCurve(selected) || !SpeedTrustToken("TrustToken-Exp1-Batch1", TRUST_TOKEN_experiment_v1(), 1, selected) ||