From 7f8646abdc467a860b216c304a56b050b606716c Mon Sep 17 00:00:00 2001 From: Spencer Wilson Date: Mon, 23 Oct 2023 11:04:12 -0400 Subject: [PATCH] Run HQC KATs with custom PRNG --- README.md | 1 + src/common/CMakeLists.txt | 3 ++- src/common/rand/rand.c | 4 ++++ src/common/rand/rand.h | 17 +++++++++++++++++ src/common/rand/rand_hqc.c | 35 +++++++++++++++++++++++++++++++++++ tests/kat_kem.c | 32 +++++++++++++++++++++++++++++--- 6 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 src/common/rand/rand_hqc.c diff --git a/README.md b/README.md index fab0097353..540a14f247 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,7 @@ liboqs includes some third party libraries or modules that are licensed differen - `src/common/crypto/sha3/xkcp_low/.../brg_endian.h` : BSD 3-Clause License - `src/common/crypto/sha3/xkcp_low/.../KeccakP-1600-AVX2.s` : BSD-like [CRYPTOGAMS license](http://www.openssl.org/~appro/cryptogams/) - `src/common/rand/rand_nist.c`: See file +- TODO: update for hqc - `src/kem/bike/additional`: Apache License v2.0 - `src/kem/classic_mceliece/pqclean_*`: public domain - `src/kem/kyber/pqcrystals-*`: public domain (CC0) or Apache License v2.0 diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index c077b489eb..4beadbb2c8 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -79,7 +79,8 @@ add_library(common OBJECT ${AES_IMPL} pqclean_shims/fips202.c pqclean_shims/fips202x4.c rand/rand.c - rand/rand_nist.c) + rand/rand_nist.c + rand/rand_hqc.c) if(${OQS_USE_OPENSSL}) target_include_directories(common PRIVATE ${OPENSSL_INCLUDE_DIR}) diff --git a/src/common/rand/rand.c b/src/common/rand/rand.c index 92ea271d19..d437846cc6 100644 --- a/src/common/rand/rand.c +++ b/src/common/rand/rand.c @@ -19,6 +19,7 @@ void OQS_randombytes_system(uint8_t *random_array, size_t bytes_to_read); void OQS_randombytes_nist_kat(uint8_t *random_array, size_t bytes_to_read); +void OQS_randombytes_hqc_kat(uint8_t *random_array, size_t bytes_to_read); #ifdef OQS_USE_OPENSSL void OQS_randombytes_openssl(uint8_t *random_array, size_t bytes_to_read); #endif @@ -37,6 +38,9 @@ OQS_API OQS_STATUS OQS_randombytes_switch_algorithm(const char *algorithm) { } else if (0 == strcasecmp(OQS_RAND_alg_nist_kat, algorithm)) { oqs_randombytes_algorithm = &OQS_randombytes_nist_kat; return OQS_SUCCESS; + } else if (0 == strcasecmp(OQS_RAND_alg_hqc_kat, algorithm)) { + oqs_randombytes_algorithm = &OQS_randombytes_hqc_kat; + return OQS_SUCCESS; } else if (0 == strcasecmp(OQS_RAND_alg_openssl, algorithm)) { #ifdef OQS_USE_OPENSSL oqs_randombytes_algorithm = &OQS_randombytes_openssl; diff --git a/src/common/rand/rand.h b/src/common/rand/rand.h index 3499c2593d..50bcf0dcc4 100644 --- a/src/common/rand/rand.h +++ b/src/common/rand/rand.h @@ -22,6 +22,8 @@ extern "C" { #define OQS_RAND_alg_system "system" /** Algorithm identifier for NIST deterministic RNG for KATs. */ #define OQS_RAND_alg_nist_kat "NIST-KAT" +/** Algorithm identifier for HQC deterministic RNG for KATs. */ +#define OQS_RAND_alg_hqc_kat "HQC-KAT" /** Algorithm identifier for using OpenSSL's PRNG. */ #define OQS_RAND_alg_openssl "OpenSSL" @@ -66,6 +68,21 @@ OQS_API void OQS_randombytes(uint8_t *random_array, size_t bytes_to_read); */ OQS_API void OQS_randombytes_nist_kat_init_256bit(const uint8_t *entropy_input, const uint8_t *personalization_string); +/** + * Initializes the HQC PRNG with a given seed. + * OQS_randombytes_hqc_kat_release should be called afterwards to clean up memory. + * + * @param[in] entropy_input The seed; must be exactly 48 bytes + * @param[in] personalization_string An optional personalization string; + * may be NULL; if not NULL, must be at least 48 bytes long + */ +OQS_API void OQS_randombytes_hqc_kat_init(const uint8_t *entropy_input, const uint8_t *personalization_string); + +/** + * Releases the HQC PRNG context. Can be called safely even if OQS_randombytes_hqc_kat_init was not called. + */ +OQS_API void OQS_randombytes_hqc_kat_free(void); + #if defined(__cplusplus) } // extern "C" #endif diff --git a/src/common/rand/rand_hqc.c b/src/common/rand/rand_hqc.c new file mode 100644 index 0000000000..269f9f9599 --- /dev/null +++ b/src/common/rand/rand_hqc.c @@ -0,0 +1,35 @@ +#include "oqs/sha3.h" + +#define PRNG_DOMAIN 1 + +// TODO document licence etc + +OQS_SHA3_shake256_inc_ctx shake_prng_state = { NULL }; + +void OQS_randombytes_hqc_kat(uint8_t *buf, size_t n); + +void OQS_randombytes_hqc_kat_init(const uint8_t *entropy_input, const uint8_t *personalization_string) { + uint8_t domain = PRNG_DOMAIN; + if (shake_prng_state.ctx != NULL) { + OQS_SHA3_shake256_inc_ctx_reset(&shake_prng_state); + } else { + OQS_SHA3_shake256_inc_init(&shake_prng_state); + } + OQS_SHA3_shake256_inc_absorb(&shake_prng_state, entropy_input, 48); + if (personalization_string) { + OQS_SHA3_shake256_inc_absorb(&shake_prng_state, personalization_string, 48); + } + OQS_SHA3_shake256_inc_absorb(&shake_prng_state, &domain, 1); + OQS_SHA3_shake256_inc_finalize(&shake_prng_state); +} + +void OQS_randombytes_hqc_kat(uint8_t *buf, size_t n) { + OQS_SHA3_shake256_inc_squeeze(buf, n, &shake_prng_state); +} + +void OQS_randombytes_hqc_kat_free() { + if (shake_prng_state.ctx != NULL) { + OQS_SHA3_shake256_inc_ctx_release(&shake_prng_state); + shake_prng_state.ctx = NULL; + } +} diff --git a/tests/kat_kem.c b/tests/kat_kem.c index 273607248e..0036901791 100644 --- a/tests/kat_kem.c +++ b/tests/kat_kem.c @@ -1,4 +1,6 @@ // SPDX-License-Identifier: MIT +// +// TODO fix leak // This KAT test only generates a subset of the NIST KAT files. // To extract the subset from a submission file, use the command: @@ -37,6 +39,12 @@ static void fprintBstr(FILE *fp, const char *S, const uint8_t *A, size_t L) { fprintf(fp, "\n"); } +static inline bool is_hqc(const char *method_name) { + return (0 == strcmp(method_name, OQS_KEM_alg_hqc_128)) + || (0 == strcmp(method_name, OQS_KEM_alg_hqc_192)) + || (0 == strcmp(method_name, OQS_KEM_alg_hqc_256)); +} + static OQS_STATUS kem_kat(const char *method_name) { uint8_t entropy_input[48]; @@ -50,6 +58,7 @@ static OQS_STATUS kem_kat(const char *method_name) { uint8_t *shared_secret_d = NULL; OQS_STATUS rc, ret = OQS_ERROR; int rv; + char *rand_alg; kem = OQS_KEM_new(method_name); if (kem == NULL) { @@ -61,11 +70,20 @@ static OQS_STATUS kem_kat(const char *method_name) { entropy_input[i] = i; } - rc = OQS_randombytes_switch_algorithm(OQS_RAND_alg_nist_kat); + rand_alg = OQS_RAND_alg_nist_kat; + if (is_hqc(method_name)) { + rand_alg = OQS_RAND_alg_hqc_kat; + } + rc = OQS_randombytes_switch_algorithm(rand_alg); if (rc != OQS_SUCCESS) { goto err; } - OQS_randombytes_nist_kat_init_256bit(entropy_input, NULL); + + if (is_hqc(method_name)) { + OQS_randombytes_hqc_kat_init(entropy_input, NULL); + } else { + OQS_randombytes_nist_kat_init_256bit(entropy_input, NULL); + } fh = stdout; @@ -73,7 +91,12 @@ static OQS_STATUS kem_kat(const char *method_name) { OQS_randombytes(seed, 48); fprintBstr(fh, "seed = ", seed, 48); - OQS_randombytes_nist_kat_init_256bit(seed, NULL); + if (is_hqc(method_name)) { + OQS_randombytes_hqc_kat_init(seed, NULL); + } else { + OQS_randombytes_nist_kat_init_256bit(seed, NULL); + } + public_key = malloc(kem->length_public_key); secret_key = malloc(kem->length_secret_key); @@ -131,6 +154,9 @@ static OQS_STATUS kem_kat(const char *method_name) { OQS_MEM_secure_free(shared_secret_e, kem->length_shared_secret); OQS_MEM_secure_free(shared_secret_d, kem->length_shared_secret); } + if (is_hqc(method_name)) { + OQS_randombytes_hqc_kat_free(); + } OQS_MEM_insecure_free(public_key); OQS_MEM_insecure_free(ciphertext); OQS_KEM_free(kem);