5840 lines
207 KiB
Diff
5840 lines
207 KiB
Diff
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 <openssl/base.h>
|
||
-#include <openssl/kyber.h>
|
||
-
|
||
-#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 <openssl/base.h>
|
||
-
|
||
-#include <assert.h>
|
||
-#include <stdlib.h>
|
||
-
|
||
-#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 <openssl/kyber.h>
|
||
+#ifndef KYBER_K
|
||
+#error "Don't compile this file direcly"
|
||
+#endif
|
||
|
||
-#include <assert.h>
|
||
-#include <stdlib.h>
|
||
+#include <openssl/kyber.h>
|
||
+#include <openssl/base.h>
|
||
|
||
-#include <openssl/bytestring.h>
|
||
-#include <openssl/rand.h>
|
||
+#include <stddef.h>
|
||
+#include <stdint.h>
|
||
+#include <string.h>
|
||
|
||
#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<len;i++)
|
||
+ r |= a[i] ^ b[i];
|
||
+
|
||
+ return (-(uint64_t)r) >> 63;
|
||
+}
|
||
+
|
||
+/*************************************************
|
||
+* Name: cmov
|
||
+*
|
||
+* Description: Copy len bytes from x to r if b is 1;
|
||
+* don't modify x if b is 0. Requires b to be in {0,1};
|
||
+* assumes two's complement representation of negative integers.
|
||
+* Runs in constant time.
|
||
+*
|
||
+* Arguments: uint8_t *r: pointer to output byte array
|
||
+* const uint8_t *x: pointer to input byte array
|
||
+* size_t len: Amount of bytes to be copied
|
||
+* uint8_t b: Condition bit; has to be in {0,1}
|
||
+**************************************************/
|
||
+static void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b)
|
||
+{
|
||
+ size_t i;
|
||
+
|
||
+ b = -b;
|
||
+ for(i=0;i<len;i++)
|
||
+ r[i] ^= b & (r[i] ^ x[i]);
|
||
+}
|
||
+
|
||
+//
|
||
+// reduce.c
|
||
+//
|
||
+
|
||
+/*************************************************
|
||
+* Name: montgomery_reduce
|
||
+*
|
||
+* Description: Montgomery reduction; given a 32-bit integer a, computes
|
||
+* 16-bit integer congruent to a * R^-1 mod q, where R=2^16
|
||
+*
|
||
+* Arguments: - int32_t a: input integer to be reduced;
|
||
+* has to be in {-q2^15,...,q2^15-1}
|
||
+*
|
||
+* Returns: integer in {-q+1,...,q-1} congruent to a * R^-1 modulo q.
|
||
+**************************************************/
|
||
+static int16_t montgomery_reduce(int32_t a)
|
||
+{
|
||
+ int16_t t;
|
||
+
|
||
+ t = (int16_t)a*QINV;
|
||
+ t = (a - (int32_t)t*KYBER_Q) >> 16;
|
||
+ return t;
|
||
+}
|
||
+
|
||
+/*************************************************
|
||
+* Name: barrett_reduce
|
||
+*
|
||
+* Description: Barrett reduction; given a 16-bit integer a, computes
|
||
+* centered representative congruent to a mod q in {-(q-1)/2,...,(q-1)/2}
|
||
+*
|
||
+* Arguments: - int16_t a: input integer to be reduced
|
||
+*
|
||
+* Returns: integer in {-(q-1)/2,...,(q-1)/2} congruent to a modulo q.
|
||
+**************************************************/
|
||
+static int16_t barrett_reduce(int16_t a) {
|
||
+ int16_t t;
|
||
+ const int16_t v = ((1<<26) + KYBER_Q/2)/KYBER_Q;
|
||
+
|
||
+ t = ((int32_t)v*a + (1<<25)) >> 26;
|
||
+ t *= KYBER_Q;
|
||
+ return a - t;
|
||
+}
|
||
+
|
||
+//
|
||
+// cbd.c
|
||
+//
|
||
+
|
||
+/*************************************************
|
||
+* Name: load32_littleendian
|
||
+*
|
||
+* Description: load 4 bytes into a 32-bit integer
|
||
+* in little-endian order
|
||
+*
|
||
+* Arguments: - const uint8_t *x: pointer to input byte array
|
||
+*
|
||
+* Returns 32-bit unsigned integer loaded from x
|
||
+**************************************************/
|
||
+static uint32_t load32_littleendian(const uint8_t x[4])
|
||
+{
|
||
+ uint32_t r;
|
||
+ r = (uint32_t)x[0];
|
||
+ r |= (uint32_t)x[1] << 8;
|
||
+ r |= (uint32_t)x[2] << 16;
|
||
+ r |= (uint32_t)x[3] << 24;
|
||
+ return r;
|
||
+}
|
||
+
|
||
+/*************************************************
|
||
+* Name: load24_littleendian
|
||
+*
|
||
+* Description: load 3 bytes into a 32-bit integer
|
||
+* in little-endian order.
|
||
+* This function is only needed for Kyber-512
|
||
+*
|
||
+* Arguments: - const uint8_t *x: pointer to input byte array
|
||
+*
|
||
+* Returns 32-bit unsigned integer loaded from x (most significant byte is zero)
|
||
+**************************************************/
|
||
+#if KYBER_ETA1 == 3
|
||
+static uint32_t load24_littleendian(const uint8_t x[3])
|
||
+{
|
||
+ uint32_t r;
|
||
+ r = (uint32_t)x[0];
|
||
+ r |= (uint32_t)x[1] << 8;
|
||
+ r |= (uint32_t)x[2] << 16;
|
||
+ return r;
|
||
+}
|
||
+#endif
|
||
+
|
||
+
|
||
+/*************************************************
|
||
+* Name: cbd2
|
||
+*
|
||
+* Description: Given an array of uniformly random bytes, compute
|
||
+* polynomial with coefficients distributed according to
|
||
+* a centered binomial distribution with parameter eta=2
|
||
+*
|
||
+* Arguments: - poly *r: pointer to output polynomial
|
||
+* - const uint8_t *buf: pointer to input byte array
|
||
+**************************************************/
|
||
+static void cbd2(poly *r, const uint8_t buf[2*KYBER_N/4])
|
||
+{
|
||
+ unsigned int i,j;
|
||
+ uint32_t t,d;
|
||
+ int16_t a,b;
|
||
+
|
||
+ for(i=0;i<KYBER_N/8;i++) {
|
||
+ t = load32_littleendian(buf+4*i);
|
||
+ d = t & 0x55555555;
|
||
+ d += (t>>1) & 0x55555555;
|
||
+
|
||
+ for(j=0;j<8;j++) {
|
||
+ a = (d >> (4*j+0)) & 0x3;
|
||
+ b = (d >> (4*j+2)) & 0x3;
|
||
+ r->coeffs[8*i+j] = a - b;
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+/*************************************************
|
||
+* Name: cbd3
|
||
+*
|
||
+* Description: Given an array of uniformly random bytes, compute
|
||
+* polynomial with coefficients distributed according to
|
||
+* a centered binomial distribution with parameter eta=3.
|
||
+* This function is only needed for Kyber-512
|
||
+*
|
||
+* Arguments: - poly *r: pointer to output polynomial
|
||
+* - const uint8_t *buf: pointer to input byte array
|
||
+**************************************************/
|
||
+#if KYBER_ETA1 == 3
|
||
+static void cbd3(poly *r, const uint8_t buf[3*KYBER_N/4])
|
||
+{
|
||
+ unsigned int i,j;
|
||
+ uint32_t t,d;
|
||
+ int16_t a,b;
|
||
+
|
||
+ for(i=0;i<KYBER_N/4;i++) {
|
||
+ t = load24_littleendian(buf+3*i);
|
||
+ d = t & 0x00249249;
|
||
+ d += (t>>1) & 0x00249249;
|
||
+ d += (t>>2) & 0x00249249;
|
||
+
|
||
+ for(j=0;j<4;j++) {
|
||
+ a = (d >> (6*j+0)) & 0x7;
|
||
+ b = (d >> (6*j+3)) & 0x7;
|
||
+ r->coeffs[4*i+j] = a - b;
|
||
}
|
||
}
|
||
}
|
||
+#endif
|
||
+
|
||
+static void poly_cbd_eta1(poly *r, const uint8_t buf[KYBER_ETA1*KYBER_N/4])
|
||
+{
|
||
+#if KYBER_ETA1 == 2
|
||
+ cbd2(r, buf);
|
||
+#elif KYBER_ETA1 == 3
|
||
+ cbd3(r, buf);
|
||
+#else
|
||
+#error "This implementation requires eta1 in {2,3}"
|
||
+#endif
|
||
+}
|
||
|
||
-static void 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;i<KYBER_N/8;i++) {
|
||
+ for(j=0;j<8;j++) {
|
||
+ // map to positive standard representatives
|
||
+ u = a->coeffs[8*i+j];
|
||
+ u += (u >> 15) & KYBER_Q;
|
||
+ 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;i<KYBER_N/8;i++) {
|
||
+ for(j=0;j<8;j++) {
|
||
+ // map to positive standard representatives
|
||
+ u = a->coeffs[8*i+j];
|
||
+ u += (u >> 15) & KYBER_Q;
|
||
+ 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;i<KYBER_N/2;i++) {
|
||
+ r->coeffs[2*i+0] = (((uint16_t)(a[0] & 15)*KYBER_Q) + 8) >> 4;
|
||
+ r->coeffs[2*i+1] = (((uint16_t)(a[0] >> 4)*KYBER_Q) + 8) >> 4;
|
||
+ a += 1;
|
||
+ }
|
||
+#elif (KYBER_POLYCOMPRESSEDBYTES == 160)
|
||
+ unsigned int j;
|
||
+ uint8_t t[8];
|
||
+ for(i=0;i<KYBER_N/8;i++) {
|
||
+ t[0] = (a[0] >> 0);
|
||
+ t[1] = (a[0] >> 5) | (a[1] << 3);
|
||
+ t[2] = (a[1] >> 2);
|
||
+ t[3] = (a[1] >> 7) | (a[2] << 1);
|
||
+ t[4] = (a[2] >> 4) | (a[3] << 4);
|
||
+ t[5] = (a[3] >> 1);
|
||
+ t[6] = (a[3] >> 6) | (a[4] << 2);
|
||
+ t[7] = (a[4] >> 3);
|
||
+ a += 5;
|
||
+
|
||
+ for(j=0;j<8;j++)
|
||
+ r->coeffs[8*i+j] = ((uint32_t)(t[j] & 31)*KYBER_Q + 16) >> 5;
|
||
}
|
||
+#else
|
||
+#error "KYBER_POLYCOMPRESSEDBYTES needs to be in {128, 160}"
|
||
+#endif
|
||
}
|
||
|
||
-// 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;i<KYBER_N/2;i++) {
|
||
+ // map to positive standard representatives
|
||
+ t0 = a->coeffs[2*i];
|
||
+ t0 += ((int16_t)t0 >> 15) & KYBER_Q;
|
||
+ t1 = a->coeffs[2*i+1];
|
||
+ t1 += ((int16_t)t1 >> 15) & KYBER_Q;
|
||
+ r[3*i+0] = (t0 >> 0);
|
||
+ r[3*i+1] = (t0 >> 8) | (t1 << 4);
|
||
+ r[3*i+2] = (t1 >> 4);
|
||
}
|
||
}
|
||
|
||
-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;i<KYBER_N/2;i++) {
|
||
+ r->coeffs[2*i] = ((a[3*i+0] >> 0) | ((uint16_t)a[3*i+1] << 8)) & 0xFFF;
|
||
+ r->coeffs[2*i+1] = ((a[3*i+1] >> 4) | ((uint16_t)a[3*i+2] << 4)) & 0xFFF;
|
||
}
|
||
}
|
||
|
||
-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<KYBER_N/8;i++) {
|
||
+ for(j=0;j<8;j++) {
|
||
+ mask = -(int16_t)value_barrier_u32((msg[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;i<KYBER_N/8;i++) {
|
||
+ msg[i] = 0;
|
||
+ for(j=0;j<8;j++) {
|
||
+ t = a->coeffs[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;i<KYBER_N/4;i++) {
|
||
+ basemul(&r->coeffs[4*i], &a->coeffs[4*i], &b->coeffs[4*i], zetas[64+i]);
|
||
+ basemul(&r->coeffs[4*i+2], &a->coeffs[4*i+2], &b->coeffs[4*i+2], -zetas[64+i]);
|
||
}
|
||
}
|
||
|
||
-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;i<KYBER_N;i++)
|
||
+ r->coeffs[i] = montgomery_reduce((int32_t)r->coeffs[i]*f);
|
||
+}
|
||
+
|
||
+/*************************************************
|
||
+* Name: poly_reduce
|
||
+*
|
||
+* Description: Applies Barrett reduction to all coefficients of a polynomial
|
||
+* for details of the Barrett reduction see comments in reduce.c
|
||
+*
|
||
+* Arguments: - poly *r: pointer to input/output polynomial
|
||
+**************************************************/
|
||
+static void poly_reduce(poly *r)
|
||
+{
|
||
+ unsigned int i;
|
||
+ for(i=0;i<KYBER_N;i++)
|
||
+ r->coeffs[i] = barrett_reduce(r->coeffs[i]);
|
||
+}
|
||
+
|
||
+/*************************************************
|
||
+* Name: poly_add
|
||
+*
|
||
+* Description: Add two polynomials; no modular reduction is performed
|
||
+*
|
||
+* Arguments: - poly *r: pointer to output polynomial
|
||
+* - const poly *a: pointer to first input polynomial
|
||
+* - const poly *b: pointer to second input polynomial
|
||
+**************************************************/
|
||
+static void poly_add(poly *r, const poly *a, const poly *b)
|
||
+{
|
||
+ unsigned int i;
|
||
+ for(i=0;i<KYBER_N;i++)
|
||
+ r->coeffs[i] = a->coeffs[i] + b->coeffs[i];
|
||
+}
|
||
+
|
||
+/*************************************************
|
||
+* Name: poly_sub
|
||
+*
|
||
+* Description: Subtract two polynomials; no modular reduction is performed
|
||
+*
|
||
+* Arguments: - poly *r: pointer to output polynomial
|
||
+* - const poly *a: pointer to first input polynomial
|
||
+* - const poly *b: pointer to second input polynomial
|
||
+**************************************************/
|
||
+static void poly_sub(poly *r, const poly *a, const poly *b)
|
||
+{
|
||
+ unsigned int i;
|
||
+ for(i=0;i<KYBER_N;i++)
|
||
+ r->coeffs[i] = a->coeffs[i] - b->coeffs[i];
|
||
+}
|
||
+
|
||
+//
|
||
+// polyvec.c
|
||
+//
|
||
+
|
||
+/*************************************************
|
||
+* Name: polyvec_compress
|
||
+*
|
||
+* Description: Compress and serialize vector of polynomials
|
||
+*
|
||
+* Arguments: - uint8_t *r: pointer to output byte array
|
||
+* (needs space for KYBER_POLYVECCOMPRESSEDBYTES)
|
||
+* - const polyvec *a: pointer to input vector of polynomials
|
||
+**************************************************/
|
||
+static void polyvec_compress(uint8_t r[KYBER_POLYVECCOMPRESSEDBYTES], const polyvec *a)
|
||
+{
|
||
+ unsigned int i,j,k;
|
||
+ uint64_t d0;
|
||
+
|
||
+#if (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 352))
|
||
+ uint16_t t[8];
|
||
+ for(i=0;i<KYBER_K;i++) {
|
||
+ for(j=0;j<KYBER_N/8;j++) {
|
||
+ for(k=0;k<8;k++) {
|
||
+ t[k] = a->vec[i].coeffs[8*j+k];
|
||
+ t[k] += ((int16_t)t[k] >> 15) & KYBER_Q;
|
||
+ 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;i<KYBER_K;i++) {
|
||
+ for(j=0;j<KYBER_N/4;j++) {
|
||
+ for(k=0;k<4;k++) {
|
||
+ t[k] = a->vec[i].coeffs[4*j+k];
|
||
+ t[k] += ((int16_t)t[k] >> 15) & KYBER_Q;
|
||
+ 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<KYBER_K;i++) {
|
||
+ for(j=0;j<KYBER_N/8;j++) {
|
||
+ t[0] = (a[0] >> 0) | ((uint16_t)a[ 1] << 8);
|
||
+ t[1] = (a[1] >> 3) | ((uint16_t)a[ 2] << 5);
|
||
+ t[2] = (a[2] >> 6) | ((uint16_t)a[ 3] << 2) | ((uint16_t)a[4] << 10);
|
||
+ t[3] = (a[4] >> 1) | ((uint16_t)a[ 5] << 7);
|
||
+ t[4] = (a[5] >> 4) | ((uint16_t)a[ 6] << 4);
|
||
+ t[5] = (a[6] >> 7) | ((uint16_t)a[ 7] << 1) | ((uint16_t)a[8] << 9);
|
||
+ t[6] = (a[8] >> 2) | ((uint16_t)a[ 9] << 6);
|
||
+ t[7] = (a[9] >> 5) | ((uint16_t)a[10] << 3);
|
||
+ a += 11;
|
||
+
|
||
+ for(k=0;k<8;k++)
|
||
+ r->vec[i].coeffs[8*j+k] = ((uint32_t)(t[k] & 0x7FF)*KYBER_Q + 1024) >> 11;
|
||
+ }
|
||
+ }
|
||
+#elif (KYBER_POLYVECCOMPRESSEDBYTES == (KYBER_K * 320))
|
||
+ uint16_t t[4];
|
||
+ for(i=0;i<KYBER_K;i++) {
|
||
+ for(j=0;j<KYBER_N/4;j++) {
|
||
+ t[0] = (a[0] >> 0) | ((uint16_t)a[1] << 8);
|
||
+ t[1] = (a[1] >> 2) | ((uint16_t)a[2] << 6);
|
||
+ t[2] = (a[2] >> 4) | ((uint16_t)a[3] << 4);
|
||
+ t[3] = (a[3] >> 6) | ((uint16_t)a[4] << 2);
|
||
+ a += 5;
|
||
+
|
||
+ for(k=0;k<4;k++)
|
||
+ r->vec[i].coeffs[4*j+k] = ((uint32_t)(t[k] & 0x3FF)*KYBER_Q + 512) >> 10;
|
||
}
|
||
- *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;i<KYBER_K;i++)
|
||
+ poly_tobytes(r+i*KYBER_POLYBYTES, &a->vec[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;i<KYBER_K;i++)
|
||
+ poly_frombytes(&r->vec[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;i<KYBER_K;i++)
|
||
+ poly_ntt(&r->vec[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;i<KYBER_K;i++)
|
||
+ poly_invntt_tomont(&r->vec[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;i<KYBER_K;i++) {
|
||
+ poly_basemul_montgomery(&t, &a->vec[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;i<KYBER_K;i++)
|
||
+ poly_reduce(&r->vec[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;i<KYBER_K;i++)
|
||
+ poly_add(&r->vec[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;i<KYBER_SYMBYTES;i++)
|
||
+ r[i+KYBER_POLYVECBYTES] = seed[i];
|
||
+}
|
||
+
|
||
+/*************************************************
|
||
+* Name: unpack_pk
|
||
+*
|
||
+* Description: De-serialize public key from a byte array;
|
||
+* approximate inverse of pack_pk
|
||
+*
|
||
+* Arguments: - polyvec *pk: pointer to output public-key polynomial vector
|
||
+* - uint8_t *seed: pointer to output seed to generate matrix A
|
||
+* - const uint8_t *packedpk: pointer to input serialized public key
|
||
+**************************************************/
|
||
+static int unpack_pk(polyvec *pk,
|
||
+ uint8_t seed[KYBER_SYMBYTES],
|
||
+ const uint8_t packedpk[KYBER_INDCPA_PUBLICKEYBYTES])
|
||
+{
|
||
+ size_t i;
|
||
+ polyvec_frombytes(pk, packedpk);
|
||
+
|
||
+ // FIPS 203 encapsulation key check. We'll perform it even for Kyber.
|
||
+ uint8_t repacked[KYBER_POLYVECBYTES];
|
||
+ polyvec_tobytes(repacked, pk);
|
||
+
|
||
+ if(verify(repacked, packedpk, KYBER_POLYVECBYTES) != 0)
|
||
+ return 0;
|
||
|
||
+ for(i=0;i<KYBER_SYMBYTES;i++)
|
||
+ seed[i] = packedpk[i+KYBER_POLYVECBYTES];
|
||
return 1;
|
||
}
|
||
|
||
-// scalar_decode_1 is |scalar_decode| specialised for |bits| == 1.
|
||
-static void scalar_decode_1(scalar *out, const uint8_t in[32]) {
|
||
- for (int i = 0; i < DEGREE; i += 8) {
|
||
- uint8_t in_byte = *in;
|
||
- in++;
|
||
- for (int j = 0; j < 8; j++) {
|
||
- out->c[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<KYBER_K;i++) {
|
||
+ for(j=0;j<KYBER_K;j++) {
|
||
+ if(transposed)
|
||
+ xof_absorb(&state, seed, i, j);
|
||
+ else
|
||
+ xof_absorb(&state, seed, j, i);
|
||
+
|
||
+ xof_squeezeblocks(buf, GEN_MATRIX_NBLOCKS, &state);
|
||
+ buflen = GEN_MATRIX_NBLOCKS*XOF_BLOCKBYTES;
|
||
+ ctr = rej_uniform(a[i].vec[j].coeffs, KYBER_N, buf, buflen);
|
||
+
|
||
+ while(ctr < KYBER_N) {
|
||
+ off = buflen % 3;
|
||
+ for(k = 0; k < off; k++)
|
||
+ buf[k] = buf[buflen - off + k];
|
||
+ xof_squeezeblocks(buf + off, 1, &state);
|
||
+ buflen = off + XOF_BLOCKBYTES;
|
||
+ ctr += rej_uniform(a[i].vec[j].coeffs + ctr, KYBER_N - ctr, buf, buflen);
|
||
+ }
|
||
}
|
||
}
|
||
- return 1;
|
||
}
|
||
|
||
-// Compresses (lossily) an input |x| mod 3329 into |bits| many bits by grouping
|
||
-// numbers close to each other together. The formula used is
|
||
-// round(2^|bits|/kPrime*x) mod 2^|bits|.
|
||
-// Uses Barrett reduction to achieve constant time. Since we need both the
|
||
-// remainder (for rounding) and the quotient (as the result), we cannot use
|
||
-// |reduce| here, but need to do the Barrett reduction directly.
|
||
-static uint16_t compress(uint16_t x, int bits) {
|
||
- uint32_t product = (uint32_t)x << bits;
|
||
- uint32_t quotient = ((uint64_t)product * kBarrettMultiplier) >> 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;i<KYBER_K;i++)
|
||
+ poly_getnoise_eta1(&skpv.vec[i], noiseseed, nonce++);
|
||
+ for(i=0;i<KYBER_K;i++)
|
||
+ poly_getnoise_eta1(&e.vec[i], noiseseed, nonce++);
|
||
+
|
||
+ polyvec_ntt(&skpv);
|
||
+ polyvec_ntt(&e);
|
||
+
|
||
+ // matrix-vector multiplication
|
||
+ for(i=0;i<KYBER_K;i++) {
|
||
+ polyvec_basemul_acc_montgomery(&pkpv.vec[i], &a[i], &skpv);
|
||
+ poly_tomont(&pkpv.vec[i]);
|
||
}
|
||
+
|
||
+ polyvec_add(&pkpv, &pkpv, &e);
|
||
+ polyvec_reduce(&pkpv);
|
||
+
|
||
+ pack_sk(sk, &skpv);
|
||
+ pack_pk(pk, &pkpv, publicseed);
|
||
}
|
||
|
||
-static void scalar_decompress(scalar *s, int bits) {
|
||
- for (int i = 0; i < DEGREE; i++) {
|
||
- s->c[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;i<KYBER_K;i++)
|
||
+ poly_getnoise_eta1(sp.vec+i, coins, nonce++);
|
||
+ for(i=0;i<KYBER_K;i++)
|
||
+ poly_getnoise_eta2(ep.vec+i, coins, nonce++);
|
||
+ poly_getnoise_eta2(&epp, coins, nonce++);
|
||
+
|
||
+ polyvec_ntt(&sp);
|
||
+
|
||
+ // matrix-vector multiplication
|
||
+ for(i=0;i<KYBER_K;i++)
|
||
+ polyvec_basemul_acc_montgomery(&b.vec[i], &at[i], &sp);
|
||
+
|
||
+ polyvec_basemul_acc_montgomery(&v, &pkpv, &sp);
|
||
+
|
||
+ polyvec_invntt_tomont(&b);
|
||
+ poly_invntt_tomont(&v);
|
||
+
|
||
+ polyvec_add(&b, &b, &ep);
|
||
+ poly_add(&v, &v, &epp);
|
||
+ poly_add(&v, &v, &k);
|
||
+ polyvec_reduce(&b);
|
||
+ poly_reduce(&v);
|
||
+
|
||
+ pack_ciphertext(c, &b, &v);
|
||
+ return 1;
|
||
}
|
||
|
||
-static void vector_compress(vector *a, int bits) {
|
||
- for (int i = 0; i < RANK; i++) {
|
||
- scalar_compress(&a->v[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;i<r;i++)
|
||
+ s[i/8] ^= (uint64_t)*in++ << 8*(i%8);
|
||
+ inlen -= r-pos;
|
||
+ KeccakF1600_StatePermute(s);
|
||
+ pos = 0;
|
||
}
|
||
- return 1;
|
||
+
|
||
+ for(i=pos;i<pos+inlen;i++)
|
||
+ s[i/8] ^= (uint64_t)*in++ << 8*(i%8);
|
||
+
|
||
+ return i;
|
||
+}
|
||
+
|
||
+/*************************************************
|
||
+* Name: keccak_finalize
|
||
+*
|
||
+* Description: Finalize absorb step.
|
||
+*
|
||
+* 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)
|
||
+* - uint8_t p: domain separation byte
|
||
+**************************************************/
|
||
+static void keccak_finalize(uint64_t s[25], unsigned int pos, unsigned int r, uint8_t p)
|
||
+{
|
||
+ s[pos/8] ^= (uint64_t)p << 8*(pos%8);
|
||
+ s[r/8-1] ^= 1ULL << 63;
|
||
}
|
||
|
||
-// Algorithms 4 and 7 of the Kyber spec. Algorithms are combined since key
|
||
-// generation is not part of the FO transform, and the spec uses Algorithm 7 to
|
||
-// specify the actual key format.
|
||
-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]) {
|
||
- struct private_key *priv = private_key_from_external(out_private_key);
|
||
- uint8_t hashed[64];
|
||
- BORINGSSL_keccak(hashed, sizeof(hashed), entropy, 32, boringssl_sha3_512);
|
||
- const uint8_t *const rho = hashed;
|
||
- const uint8_t *const sigma = hashed + 32;
|
||
- OPENSSL_memcpy(priv->pub.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;i<r/8;i++)
|
||
+ s[i] ^= load64(in+8*i);
|
||
+ in += r;
|
||
+ inlen -= r;
|
||
+ KeccakF1600_StatePermute(s);
|
||
}
|
||
|
||
- BORINGSSL_keccak(priv->pub.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;i<inlen;i++)
|
||
+ s[i/8] ^= (uint64_t)in[i] << 8*(i%8);
|
||
+
|
||
+ s[i/8] ^= (uint64_t)p << 8*(i%8);
|
||
+ s[(r-1)/8] ^= 1ULL << 63;
|
||
+}
|
||
+
|
||
+/*************************************************
|
||
+* Name: keccak_squeezeblocks
|
||
+*
|
||
+* Description: Squeeze step of Keccak. Squeezes full blocks of r bytes each.
|
||
+* Modifies the state. Can be called multiple times to keep
|
||
+* squeezing, i.e., is incremental. Assumes zero bytes of current
|
||
+* block have already been squeezed.
|
||
+*
|
||
+* Arguments: - uint8_t *out: pointer to output blocks
|
||
+* - size_t nblocks: number of blocks to be squeezed (written to out)
|
||
+* - uint64_t *s: pointer to input/output Keccak state
|
||
+* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128)
|
||
+**************************************************/
|
||
+static void keccak_squeezeblocks(uint8_t *out,
|
||
+ size_t nblocks,
|
||
+ uint64_t s[25],
|
||
+ unsigned int r)
|
||
+{
|
||
+ unsigned int i;
|
||
+
|
||
+ while(nblocks) {
|
||
+ KeccakF1600_StatePermute(s);
|
||
+ for(i=0;i<r/8;i++)
|
||
+ store64(out+8*i, s[i]);
|
||
+ out += r;
|
||
+ nblocks -= 1;
|
||
}
|
||
- BORINGSSL_keccak(input + 32, 32, ciphertext, KYBER_CIPHERTEXT_BYTES,
|
||
- boringssl_sha3_256);
|
||
- BORINGSSL_keccak(out_shared_secret, out_shared_secret_len, input,
|
||
- sizeof(input), boringssl_shake256);
|
||
}
|
||
|
||
-int KYBER_marshal_public_key(CBB *out,
|
||
- const struct KYBER_public_key *public_key) {
|
||
- return kyber_marshal_public_key(out, public_key_from_external(public_key));
|
||
+
|
||
+/*************************************************
|
||
+* Name: shake128_absorb_once
|
||
+*
|
||
+* Description: Initialize, absorb into and finalize SHAKE128 XOF; non-incremental.
|
||
+*
|
||
+* Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak state
|
||
+* - const uint8_t *in: pointer to input to be absorbed into s
|
||
+* - size_t inlen: length of input in bytes
|
||
+**************************************************/
|
||
+static void shake128_absorb_once(keccak_state *state, const uint8_t *in, size_t inlen)
|
||
+{
|
||
+ keccak_absorb_once(state->s, SHAKE128_RATE, in, inlen, 0x1F);
|
||
+ state->pos = SHAKE128_RATE;
|
||
}
|
||
|
||
-// 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;i<KYBER_INDCPA_PUBLICKEYBYTES;i++)
|
||
+ sk[i+KYBER_INDCPA_SECRETKEYBYTES] = pk[i];
|
||
+ hash_h(sk+KYBER_SECRETKEYBYTES-2*KYBER_SYMBYTES, pk, KYBER_PUBLICKEYBYTES);
|
||
+ /* Value z for pseudo-random output on reject */
|
||
+ memcpy(sk+KYBER_SECRETKEYBYTES-KYBER_SYMBYTES, seed+KYBER_SYMBYTES, KYBER_SYMBYTES);
|
||
+}
|
||
+
|
||
+// Modified crypto_kem_enc to BoringSSL style API
|
||
+int encap(uint8_t out_ciphertext[KYBER_CIPHERTEXTBYTES],
|
||
+ uint8_t ss[KYBER_KEY_BYTES],
|
||
+ const struct public_key *in_pub,
|
||
+ const uint8_t seed[KYBER_ENCAP_BYTES], int mlkem)
|
||
+{
|
||
+ const uint8_t *pk = &in_pub->opaque[0];
|
||
+ uint8_t *ct = out_ciphertext;
|
||
+
|
||
+ uint8_t buf[2*KYBER_SYMBYTES];
|
||
+ /* Will contain key, coins */
|
||
+ uint8_t kr[2*KYBER_SYMBYTES];
|
||
+
|
||
+ memcpy(buf, seed, KYBER_SYMBYTES);
|
||
+
|
||
+ /* Don't release system RNG output */
|
||
+ hash_h(buf, buf, KYBER_SYMBYTES);
|
||
+
|
||
+ /* Multitarget countermeasure for coins + contributory KEM */
|
||
+ hash_h(buf+KYBER_SYMBYTES, pk, KYBER_PUBLICKEYBYTES);
|
||
+ hash_g(kr, buf, 2*KYBER_SYMBYTES);
|
||
+
|
||
+ /* coins are in kr+KYBER_SYMBYTES */
|
||
+ 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;i<KYBER_SYMBYTES;i++)
|
||
+ buf[KYBER_SYMBYTES+i] = sk[KYBER_SECRETKEYBYTES-2*KYBER_SYMBYTES+i];
|
||
+ hash_g(kr, buf, 2*KYBER_SYMBYTES);
|
||
+
|
||
+ /* coins are in kr+KYBER_SYMBYTES */
|
||
+ indcpa_enc(cmp, buf, pk, kr+KYBER_SYMBYTES);
|
||
+
|
||
+ fail = verify(ct, cmp, KYBER_CIPHERTEXTBYTES);
|
||
}
|
||
- vector_encode(s_output, &priv->s, 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 <vector>
|
||
-
|
||
-#include <string.h>
|
||
-
|
||
-#include <gtest/gtest.h>
|
||
-
|
||
-#include <openssl/bytestring.h>
|
||
-#include <openssl/ctrdrbg.h>
|
||
-#include <openssl/kyber.h>
|
||
-
|
||
-#include "../test/file_test.h"
|
||
-#include "../test/test_util.h"
|
||
-#include "./internal.h"
|
||
-
|
||
-
|
||
-static void KeccakFileTest(FileTest *t) {
|
||
- std::vector<uint8_t> 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 <typename T>
|
||
-static std::vector<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<CTR_DRBG_STATE> 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<const uint16_t> 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<uint16_t>& 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<SSLKeyShare> key_shares[2];
|
||
+ UniquePtr<SSLKeyShare> 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<uint16_t> verify_sigalgs;
|
||
+
|
||
+ // delegated_credentials, if not empty, is the set of signature algorithms
|
||
+ // supported by the client.
|
||
+ Array<uint16_t> 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<uint16_t> verify_sigalgs;
|
||
|
||
+ // delegated_credentials, if not empty, is the set of signature algorithms
|
||
+ // supported by the client.
|
||
+ bssl::Array<uint16_t> 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<const SSL_CIPHER> 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<SSLCipherPreferenceList> *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<SSLCipherPreferenceList> *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 <openssl/err.h>
|
||
#include <openssl/kyber.h>
|
||
#include <openssl/hrss.h>
|
||
+#include <openssl/kyber.h>
|
||
#include <openssl/mem.h>
|
||
#include <openssl/nid.h>
|
||
#include <openssl/rand.h>
|
||
@@ -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(BN_CTX_new());
|
||
+ if (!bn_ctx) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ BN_CTXScope scope(bn_ctx.get());
|
||
+
|
||
+ // Generate a P-256 private key.
|
||
+ UniquePtr<EC_GROUP> group;
|
||
+ group.reset(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
|
||
+ p256_private_key_.reset(BN_new());
|
||
+ if (!group || !p256_private_key_ ||
|
||
+ !BN_rand_range_ex(p256_private_key_.get(), 1,
|
||
+ EC_GROUP_get0_order(group.get()))) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ // Compute the corresponding P-256 public key and serialize it.
|
||
+ UniquePtr<EC_POINT> p256_public_key(EC_POINT_new(group.get()));
|
||
+ if (!p256_public_key ||
|
||
+ !EC_POINT_mul(group.get(), p256_public_key.get(), p256_private_key_.get(),
|
||
+ NULL, NULL, bn_ctx.get()) ||
|
||
+ !EC_POINT_point2cbb(out, group.get(), p256_public_key.get(),
|
||
+ POINT_CONVERSION_UNCOMPRESSED, bn_ctx.get())) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+
|
||
+ // Kyber
|
||
+ uint8_t kyber_entropy[KYBER_GENERATE_KEY_BYTES];
|
||
+ KYBER768_public_key kyber_public_key;
|
||
+ RAND_bytes(kyber_entropy, sizeof(kyber_entropy));
|
||
+ KYBER768_generate_key(&kyber_public_key, &kyber_private_key_, kyber_entropy);
|
||
+
|
||
+ uint8_t kyber_public_key_bytes[KYBER768_PUBLIC_KEY_BYTES];
|
||
+ KYBER768_marshal_public_key(kyber_public_key_bytes, &kyber_public_key);
|
||
|
||
- 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<uint8_t> *out_secret,
|
||
+ uint8_t *out_alert, Span<const uint8_t> peer_key) override {
|
||
+ assert(!p256_private_key_);
|
||
+
|
||
+ if (peer_key.size() != 65 + KYBER768_PUBLIC_KEY_BYTES) {
|
||
+ *out_alert = SSL_AD_DECODE_ERROR;
|
||
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ // Set up a shared |BN_CTX| for P-256 operations.
|
||
+ UniquePtr<BN_CTX> bn_ctx(BN_CTX_new());
|
||
+ if (!bn_ctx) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ BN_CTXScope scope(bn_ctx.get());
|
||
+
|
||
+ UniquePtr<EC_GROUP> group;
|
||
+ group.reset(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
|
||
+ if (!group) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ // Parse peer point
|
||
+ UniquePtr<EC_POINT> peer_point(EC_POINT_new(group.get()));
|
||
+ UniquePtr<EC_POINT> result(EC_POINT_new(group.get()));
|
||
+ BIGNUM *x = BN_CTX_get(bn_ctx.get());
|
||
+ if (!peer_point || !result || !x) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ if (peer_key.empty() || peer_key[0] != POINT_CONVERSION_UNCOMPRESSED ||
|
||
+ !EC_POINT_oct2point(group.get(), peer_point.get(), peer_key.data(),
|
||
+ 65, bn_ctx.get())) {
|
||
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
|
||
+ *out_alert = SSL_AD_DECODE_ERROR;
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ p256_private_key_.reset(BN_new());
|
||
+ if (!p256_private_key_ || !BN_rand_range_ex(p256_private_key_.get(), 1,
|
||
+ EC_GROUP_get0_order(group.get()))) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ // Compute the corresponding P-256 public key and serialize it.
|
||
+ UniquePtr<EC_POINT> p256_public_key(EC_POINT_new(group.get()));
|
||
+ if (!p256_public_key ||
|
||
+ !EC_POINT_mul(group.get(), p256_public_key.get(), p256_private_key_.get(),
|
||
+ NULL, NULL, bn_ctx.get()) ||
|
||
+ !EC_POINT_point2cbb(out_public_key, group.get(), p256_public_key.get(),
|
||
+ POINT_CONVERSION_UNCOMPRESSED, bn_ctx.get())) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ // Compute the x-coordinate of |peer_key| * |p256_private_key_|.
|
||
+ if (!EC_POINT_mul(group.get(), result.get(), NULL, peer_point.get(),
|
||
+ p256_private_key_.get(), bn_ctx.get()) ||
|
||
+ !EC_POINT_get_affine_coordinates_GFp(group.get(), result.get(), x, NULL,
|
||
+ bn_ctx.get())) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ // Encode the x-coordinate left-padded with zeros.
|
||
+ Array<uint8_t> secret;
|
||
+ if (!secret.Init(32 + KYBER_KEY_BYTES) ||
|
||
+ !BN_bn2bin_padded(secret.data(), 32, x)) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+
|
||
+ KYBER768_public_key peer_public_key;
|
||
+ KYBER768_parse_public_key(&peer_public_key, peer_key.data() + 65);
|
||
+
|
||
+ uint8_t ciphertext[KYBER768_CIPHERTEXT_BYTES];
|
||
+ uint8_t entropy[KYBER_ENCAP_BYTES];
|
||
+ RAND_bytes(entropy, sizeof(entropy));
|
||
+
|
||
+ 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<uint8_t> *out_secret, uint8_t *out_alert,
|
||
+ Span<const uint8_t> peer_key) override {
|
||
+ assert(p256_private_key_);
|
||
+ *out_alert = SSL_AD_INTERNAL_ERROR;
|
||
+
|
||
+ Array<uint8_t> secret;
|
||
+ if (!secret.Init(32 + KYBER_KEY_BYTES)) {
|
||
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ if (peer_key.size() != 65 + KYBER768_CIPHERTEXT_BYTES) {
|
||
+ *out_alert = SSL_AD_DECODE_ERROR;
|
||
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ // Set up a shared |BN_CTX| for P-256 operations.
|
||
+ UniquePtr<BN_CTX> bn_ctx(BN_CTX_new());
|
||
+ if (!bn_ctx) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ BN_CTXScope scope(bn_ctx.get());
|
||
+
|
||
+ UniquePtr<EC_GROUP> group;
|
||
+ group.reset(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
|
||
+ if (!group) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ // Parse peer point
|
||
+ UniquePtr<EC_POINT> peer_point(EC_POINT_new(group.get()));
|
||
+ UniquePtr<EC_POINT> result(EC_POINT_new(group.get()));
|
||
+ BIGNUM *x = BN_CTX_get(bn_ctx.get());
|
||
+ if (!peer_point || !result || !x) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ if (peer_key.empty() || peer_key[0] != POINT_CONVERSION_UNCOMPRESSED ||
|
||
+ !EC_POINT_oct2point(group.get(), peer_point.get(), peer_key.data(),
|
||
+ 65, bn_ctx.get())) {
|
||
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
|
||
+ *out_alert = SSL_AD_DECODE_ERROR;
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ // Compute the x-coordinate of |peer_key| * |p256_private_key_|.
|
||
+ if (!EC_POINT_mul(group.get(), result.get(), NULL, peer_point.get(),
|
||
+ p256_private_key_.get(), bn_ctx.get()) ||
|
||
+ !EC_POINT_get_affine_coordinates_GFp(group.get(), result.get(), x, NULL,
|
||
+ bn_ctx.get())) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ // Encode the x-coordinate left-padded with zeros.
|
||
+ if (!secret.Init(32 + KYBER_KEY_BYTES) ||
|
||
+ !BN_bn2bin_padded(secret.data(), 32, x)) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ KYBER768_decap(secret.data() + 32, &kyber_private_key_,
|
||
+ peer_key.data() + 65, peer_key.size() - 65, 0);
|
||
+
|
||
+ *out_secret = std::move(secret);
|
||
+ return true;
|
||
+ }
|
||
+
|
||
+ private:
|
||
+ UniquePtr<BIGNUM> p256_private_key_;
|
||
+ KYBER768_private_key kyber_private_key_;
|
||
+};
|
||
+
|
||
+class X25519Kyber768Draft00KeyShare : public SSLKeyShare {
|
||
+ public:
|
||
+ X25519Kyber768Draft00KeyShare(uint16_t 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<uint8_t> *out_secret,
|
||
- uint8_t *out_alert, Span<const uint8_t> peer_key) override {
|
||
+ bool Encap(CBB *out_public_key, Array<uint8_t> *out_secret,
|
||
+ uint8_t *out_alert, Span<const uint8_t> peer_key) override {
|
||
Array<uint8_t> secret;
|
||
- if (!secret.Init(32 + 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<uint8_t> *out_secret, uint8_t *out_alert,
|
||
- Span<const uint8_t> ciphertext) override {
|
||
+ Span<const uint8_t> peer_key) override {
|
||
+ *out_alert = SSL_AD_INTERNAL_ERROR;
|
||
+
|
||
+ Array<uint8_t> secret;
|
||
+ if (!secret.Init(32 + KYBER_KEY_BYTES)) {
|
||
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ if (peer_key.size() != 32 + KYBER768_CIPHERTEXT_BYTES ||
|
||
+ !X25519(secret.data(), x25519_private_key_, peer_key.data())) {
|
||
+ *out_alert = SSL_AD_DECODE_ERROR;
|
||
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ KYBER768_decap(secret.data() + 32, &kyber_private_key_,
|
||
+ peer_key.data() + 32, peer_key.size() - 32, 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<uint8_t> *out_secret,
|
||
+ uint8_t *out_alert, Span<const uint8_t> peer_key) override {
|
||
+ Array<uint8_t> secret;
|
||
+ if (!secret.Init(32 + KYBER_KEY_BYTES)) {
|
||
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ uint8_t x25519_public_key[32];
|
||
+ X25519_keypair(x25519_public_key, x25519_private_key_);
|
||
+
|
||
+ KYBER768_public_key peer_public_key;
|
||
+ if (peer_key.size() != 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<uint8_t> *out_secret, uint8_t *out_alert,
|
||
+ Span<const uint8_t> peer_key) override {
|
||
*out_alert = SSL_AD_INTERNAL_ERROR;
|
||
|
||
Array<uint8_t> secret;
|
||
- if (!secret.Init(32 + 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<uint8_t> *out_secret,
|
||
+ uint8_t *out_alert, Span<const uint8_t> peer_key) override {
|
||
+ Array<uint8_t> secret;
|
||
+ if (!secret.Init(32 + KYBER_KEY_BYTES)) {
|
||
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ uint8_t x25519_public_key[32];
|
||
+ X25519_keypair(x25519_public_key, x25519_private_key_);
|
||
+
|
||
+ KYBER512_public_key peer_public_key;
|
||
+ if (peer_key.size() != 32 + KYBER512_PUBLIC_KEY_BYTES) {
|
||
+ *out_alert = SSL_AD_DECODE_ERROR;
|
||
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ KYBER512_parse_public_key(&peer_public_key, peer_key.data() + 32);
|
||
+
|
||
+ if (!X25519(secret.data(), x25519_private_key_, peer_key.data())) {
|
||
+ *out_alert = SSL_AD_DECODE_ERROR;
|
||
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ uint8_t ciphertext[KYBER512_CIPHERTEXT_BYTES];
|
||
+ uint8_t entropy[KYBER_ENCAP_BYTES];
|
||
+ RAND_bytes(entropy, sizeof(entropy));
|
||
+
|
||
+ 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<uint8_t> *out_secret, uint8_t *out_alert,
|
||
+ Span<const uint8_t> peer_key) override {
|
||
+ *out_alert = SSL_AD_INTERNAL_ERROR;
|
||
+
|
||
+ Array<uint8_t> secret;
|
||
+ if (!secret.Init(32 + KYBER_KEY_BYTES)) {
|
||
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ if (peer_key.size() != 32 + KYBER512_CIPHERTEXT_BYTES ||
|
||
+ !X25519(secret.data(), x25519_private_key_, peer_key.data())) {
|
||
+ *out_alert = SSL_AD_DECODE_ERROR;
|
||
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ KYBER512_decap(secret.data() + 32, &kyber_private_key_,
|
||
+ peer_key.data() + 32, peer_key.size() - 32, 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> SSLKeyShare::Create(uint16_t group_id) {
|
||
return MakeUnique<ECKeyShare>(NID_secp521r1, SSL_CURVE_SECP521R1);
|
||
case SSL_CURVE_X25519:
|
||
return MakeUnique<X25519KeyShare>();
|
||
+ case SSL_CURVE_X25519_KYBER512_DRAFT00:
|
||
+ return UniquePtr<SSLKeyShare>(New<X25519Kyber512Draft00KeyShare>());
|
||
case SSL_CURVE_X25519_KYBER768_DRAFT00:
|
||
- return MakeUnique<X25519Kyber768KeyShare>();
|
||
+ return UniquePtr<SSLKeyShare>(New<X25519Kyber768Draft00KeyShare>(
|
||
+ group_id));
|
||
+ case SSL_CURVE_X25519_KYBER768_DRAFT00_OLD:
|
||
+ return UniquePtr<SSLKeyShare>(New<X25519Kyber768Draft00KeyShare>(
|
||
+ group_id));
|
||
+ case SSL_CURVE_P256_KYBER768_DRAFT00:
|
||
+ return UniquePtr<SSLKeyShare>(New<P256Kyber768Draft00KeyShare>());
|
||
+ case SSL_CURVE_X25519_MLKEM768:
|
||
+ return UniquePtr<SSLKeyShare>(New<X25519MLKEM768KeyShare>());
|
||
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<const uint16_t> in_sigalgs) {
|
||
- if (in_sigalgs.size() < 2) {
|
||
- return true;
|
||
- }
|
||
-
|
||
- Array<uint16_t> 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<const uint16_t> in_sigalgs) {
|
||
+// if (in_sigalgs.size() < 2) {
|
||
+// return true;
|
||
+// }
|
||
+//
|
||
+// Array<uint16_t> 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<uint16_t> *out, Span<const uint16_t> 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<uint16_t> 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<uint16_t> 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<std::string> &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) ||
|