From aea72d3eb2c2ff40acc3d26764ab5a61d0907599 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Wed, 10 Apr 2019 11:50:23 -0400 Subject: [PATCH 01/10] Add macros for AES block size --- common/aes.h | 1 + test/common/aes.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/common/aes.h b/common/aes.h index dd5a1019..ef1eca28 100644 --- a/common/aes.h +++ b/common/aes.h @@ -8,6 +8,7 @@ #define AES192_KEYBYTES 24 #define AES256_KEYBYTES 32 #define AESCTR_NONCEBYTES 12 +#define AES_BLOCKBYTES 16 typedef struct { uint64_t sk_exp[88]; diff --git a/test/common/aes.c b/test/common/aes.c index dcfe8029..d7759168 100644 --- a/test/common/aes.c +++ b/test/common/aes.c @@ -78,19 +78,19 @@ int main(void) } - aes128_ecb(ct, msg, 3, &ctx128); + aes128_ecb(ct, msg, sizeof(msg) / AES_BLOCKBYTES, &ctx128); if(memcmp(ct, ct128, 48)) { printf("ERROR AES128ECB output does not match test vector.\n"); r = 1; } - aes192_ecb(ct, msg, 3, &ctx192); + aes192_ecb(ct, msg, sizeof(msg) / AES_BLOCKBYTES, &ctx192); if(memcmp(ct, ct192, 48)) { printf("ERROR AES192ECB output does not match test vector.\n"); r = 1; } - aes256_ecb(ct, msg, 3, &ctx256); + aes256_ecb(ct, msg, sizeof(msg) / AES_BLOCKBYTES, &ctx256); if(memcmp(ct, ct256, 48)) { printf("ERROR AES256ECB output does not match test vector.\n"); r = 1; From e9427afeb28d8263a1c3964f4c0a93fc81adda38 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Wed, 10 Apr 2019 11:51:09 -0400 Subject: [PATCH 02/10] Add FrodoKEM-640-AES --- crypto_kem/frodokem640aes/META.yml | 24 ++ crypto_kem/frodokem640aes/clean/LICENSE | 21 ++ crypto_kem/frodokem640aes/clean/Makefile | 19 ++ .../clean/Makefile.Microsoft_nmake | 19 ++ crypto_kem/frodokem640aes/clean/api.h | 20 ++ crypto_kem/frodokem640aes/clean/common.h | 19 ++ crypto_kem/frodokem640aes/clean/kem.c | 238 ++++++++++++++++++ crypto_kem/frodokem640aes/clean/matrix_aes.c | 85 +++++++ crypto_kem/frodokem640aes/clean/noise.c | 33 +++ crypto_kem/frodokem640aes/clean/params.h | 27 ++ crypto_kem/frodokem640aes/clean/util.c | 234 +++++++++++++++++ .../frodokem640aes_clean.yml | 9 + 12 files changed, 748 insertions(+) create mode 100644 crypto_kem/frodokem640aes/META.yml create mode 100644 crypto_kem/frodokem640aes/clean/LICENSE create mode 100644 crypto_kem/frodokem640aes/clean/Makefile create mode 100644 crypto_kem/frodokem640aes/clean/Makefile.Microsoft_nmake create mode 100644 crypto_kem/frodokem640aes/clean/api.h create mode 100644 crypto_kem/frodokem640aes/clean/common.h create mode 100644 crypto_kem/frodokem640aes/clean/kem.c create mode 100644 crypto_kem/frodokem640aes/clean/matrix_aes.c create mode 100644 crypto_kem/frodokem640aes/clean/noise.c create mode 100644 crypto_kem/frodokem640aes/clean/params.h create mode 100644 crypto_kem/frodokem640aes/clean/util.c create mode 100644 test/duplicate_consistency/frodokem640aes_clean.yml diff --git a/crypto_kem/frodokem640aes/META.yml b/crypto_kem/frodokem640aes/META.yml new file mode 100644 index 00000000..31c39aa3 --- /dev/null +++ b/crypto_kem/frodokem640aes/META.yml @@ -0,0 +1,24 @@ +name: FrodoKEM-640-AES +type: kem +claimed-nist-level: 1 +length-public-key: 9616 +length-ciphertext: 9720 +length-shared-secret: 16 +testvectors-sha256: d4c7d30254a8cac8ad73b742b31813e47dcae6532a4dcbe13c04d72a2920a086 +principal-submitter: Douglas Stebila, University of Waterloo +auxiliary-submitters: +- Erdem Alkim +- Joppe W. Bos, NXP Semiconductors +- Léo Ducas, CWI +- Patrick Longa, Microsoft Research +- Ilya Mironov, Google +- Michael Naehrig, Microsoft Research +- Valeria Nikolaenko +- Chris Peikert, University of Michigan +- Ananth Raghunathan, Google +- Karen Easterbrook, Microsoft Research +- Brian LaMacchia, Microsoft Research +implementations: +- name: clean + version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/437e228fca580a82435cab09f30ae14b03183119 + length-secret-key: 19888 diff --git a/crypto_kem/frodokem640aes/clean/LICENSE b/crypto_kem/frodokem640aes/clean/LICENSE new file mode 100644 index 00000000..5cf7c8db --- /dev/null +++ b/crypto_kem/frodokem640aes/clean/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE diff --git a/crypto_kem/frodokem640aes/clean/Makefile b/crypto_kem/frodokem640aes/clean/Makefile new file mode 100644 index 00000000..f174ec77 --- /dev/null +++ b/crypto_kem/frodokem640aes/clean/Makefile @@ -0,0 +1,19 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libfrodokem640aes_clean.a +HEADERS=api.h params.h common.h +OBJECTS=kem.o matrix_aes.o noise.o util.o + +CFLAGS=-Wall -Wextra -Wpedantic -Werror -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_kem/frodokem640aes/clean/Makefile.Microsoft_nmake b/crypto_kem/frodokem640aes/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..87db9fdf --- /dev/null +++ b/crypto_kem/frodokem640aes/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libfrodokem640aes_clean.lib +OBJECTS=kem.obj matrix_aes.obj noise.obj util.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_kem/frodokem640aes/clean/api.h b/crypto_kem/frodokem640aes/clean/api.h new file mode 100644 index 00000000..6b688108 --- /dev/null +++ b/crypto_kem/frodokem640aes/clean/api.h @@ -0,0 +1,20 @@ +#ifndef PQCLEAN_FRODOKEM640AES_CLEAN_API_H +#define PQCLEAN_FRODOKEM640AES_CLEAN_API_H + +#include +#include + +#define PQCLEAN_FRODOKEM640AES_CLEAN_CRYPTO_SECRETKEYBYTES 19888 // sizeof(s) + CRYPTO_PUBLICKEYBYTES + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH +#define PQCLEAN_FRODOKEM640AES_CLEAN_CRYPTO_PUBLICKEYBYTES 9616 // sizeof(seed_A) + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 +#define PQCLEAN_FRODOKEM640AES_CLEAN_CRYPTO_BYTES 16 +#define PQCLEAN_FRODOKEM640AES_CLEAN_CRYPTO_CIPHERTEXTBYTES 9720 // (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + (PARAMS_LOGQ*PARAMS_NBAR*PARAMS_NBAR)/8 + +#define PQCLEAN_FRODOKEM640AES_CLEAN_CRYPTO_ALGNAME "FrodoKEM-640-AES" + +int PQCLEAN_FRODOKEM640AES_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); + +int PQCLEAN_FRODOKEM640AES_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); + +int PQCLEAN_FRODOKEM640AES_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); + +#endif diff --git a/crypto_kem/frodokem640aes/clean/common.h b/crypto_kem/frodokem640aes/clean/common.h new file mode 100644 index 00000000..76309497 --- /dev/null +++ b/crypto_kem/frodokem640aes/clean/common.h @@ -0,0 +1,19 @@ +#ifndef COMMON_H +#define COMMON_H + +int PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); +int PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); +void PQCLEAN_FRODOKEM640AES_CLEAN_sample_n(uint16_t *s, size_t n); +void PQCLEAN_FRODOKEM640AES_CLEAN_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s); +void PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e); +void PQCLEAN_FRODOKEM640AES_CLEAN_add(uint16_t *out, const uint16_t *a, const uint16_t *b); +void PQCLEAN_FRODOKEM640AES_CLEAN_sub(uint16_t *out, const uint16_t *a, const uint16_t *b); +void PQCLEAN_FRODOKEM640AES_CLEAN_key_encode(uint16_t *out, const uint16_t *in); +void PQCLEAN_FRODOKEM640AES_CLEAN_key_decode(uint16_t *out, const uint16_t *in); +void PQCLEAN_FRODOKEM640AES_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); +void PQCLEAN_FRODOKEM640AES_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); +void PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes(uint8_t *mem, size_t n); +uint16_t PQCLEAN_FRODOKEM640AES_CLEAN_LE_TO_UINT16(uint16_t n); +uint16_t PQCLEAN_FRODOKEM640AES_CLEAN_UINT16_TO_LE(uint16_t n); + +#endif diff --git a/crypto_kem/frodokem640aes/clean/kem.c b/crypto_kem/frodokem640aes/clean/kem.c new file mode 100644 index 00000000..7049e252 --- /dev/null +++ b/crypto_kem/frodokem640aes/clean/kem.c @@ -0,0 +1,238 @@ +/******************************************************************************************** +* FrodoKEM: Learning with Errors Key Encapsulation +* +* Abstract: Key Encapsulation Mechanism (KEM) based on Frodo +*********************************************************************************************/ + +#include +#include + +#include "fips202.h" +#include "randombytes.h" + +#include "api.h" +#include "common.h" +#include "params.h" + +int PQCLEAN_FRODOKEM640AES_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + // FrodoKEM's key generation + // Outputs: public key pk ( BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 bytes) + // secret key sk (CRYPTO_BYTES + BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH bytes) + uint8_t *pk_seedA = &pk[0]; + uint8_t *pk_b = &pk[BYTES_SEED_A]; + uint8_t *sk_s = &sk[0]; + uint8_t *sk_pk = &sk[CRYPTO_BYTES]; + uint8_t *sk_S = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; + uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; + uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t S[2 * PARAMS_N * PARAMS_NBAR] = {0}; // contains secret data + uint16_t *E = &S[PARAMS_N * PARAMS_NBAR]; // contains secret data + uint8_t randomness[2 * CRYPTO_BYTES + BYTES_SEED_A]; // contains secret data via randomness_s and randomness_seedSE + uint8_t *randomness_s = &randomness[0]; // contains secret data + uint8_t *randomness_seedSE = &randomness[CRYPTO_BYTES]; // contains secret data + uint8_t *randomness_z = &randomness[2 * CRYPTO_BYTES]; + uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data + + // Generate the secret value s, the seed for S and E, and the seed for the seed for A. Add seed_A to the public key + randombytes(randomness, CRYPTO_BYTES + CRYPTO_BYTES + BYTES_SEED_A); + shake(pk_seedA, BYTES_SEED_A, randomness_z, BYTES_SEED_A); + + // Generate S and E, and compute B = A*S + E. Generate A on-the-fly + shake_input_seedSE[0] = 0x5F; + memcpy(&shake_input_seedSE[1], randomness_seedSE, CRYPTO_BYTES); + shake((uint8_t *)S, 2 * PARAMS_N * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); + for (size_t i = 0; i < 2 * PARAMS_N * PARAMS_NBAR; i++) { + S[i] = PQCLEAN_FRODOKEM640AES_CLEAN_LE_TO_UINT16(S[i]); + } + PQCLEAN_FRODOKEM640AES_CLEAN_sample_n(S, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM640AES_CLEAN_sample_n(E, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_as_plus_e(B, S, E, pk); + + // Encode the second part of the public key + PQCLEAN_FRODOKEM640AES_CLEAN_pack(pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, B, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); + + // Add s, pk and S to the secret key + memcpy(sk_s, randomness_s, CRYPTO_BYTES); + memcpy(sk_pk, pk, CRYPTO_PUBLICKEYBYTES); + for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { + S[i] = PQCLEAN_FRODOKEM640AES_CLEAN_UINT16_TO_LE(S[i]); + } + memcpy(sk_S, S, 2 * PARAMS_N * PARAMS_NBAR); + + // Add H(pk) to the secret key + shake(sk_pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); + + // Cleanup: + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes((uint8_t *)E, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes(randomness, 2 * CRYPTO_BYTES); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); + return 0; +} + + +int PQCLEAN_FRODOKEM640AES_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { + // FrodoKEM's key encapsulation + const uint8_t *pk_seedA = &pk[0]; + const uint8_t *pk_b = &pk[BYTES_SEED_A]; + uint8_t *ct_c1 = &ct[0]; + uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; + uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t V[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data + uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; + uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data + uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data + uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data + uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via mu + uint8_t *pkh = &G2in[0]; + uint8_t *mu = &G2in[BYTES_PKHASH]; // contains secret data + uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data + uint8_t *seedSE = &G2out[0]; // contains secret data + uint8_t *k = &G2out[CRYPTO_BYTES]; // contains secret data + uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k + uint8_t *Fin_ct = &Fin[0]; + uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data + uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data + + // pkh <- G_1(pk), generate random mu, compute (seedSE || k) = G_2(pkh || mu) + shake(pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); + randombytes(mu, BYTES_MU); + shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); + + // Generate Sp and Ep, and compute Bp = Sp*A + Ep. Generate A on-the-fly + shake_input_seedSE[0] = 0x96; + memcpy(&shake_input_seedSE[1], seedSE, CRYPTO_BYTES); + shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); + for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { + Sp[i] = PQCLEAN_FRODOKEM640AES_CLEAN_LE_TO_UINT16(Sp[i]); + } + PQCLEAN_FRODOKEM640AES_CLEAN_sample_n(Sp, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM640AES_CLEAN_sample_n(Ep, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_sa_plus_e(Bp, Sp, Ep, pk_seedA); + PQCLEAN_FRODOKEM640AES_CLEAN_pack(ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, Bp, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); + + // Generate Epp, and compute V = Sp*B + Epp + PQCLEAN_FRODOKEM640AES_CLEAN_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); + PQCLEAN_FRODOKEM640AES_CLEAN_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); + PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_sb_plus_e(V, B, Sp, Epp); + + // Encode mu, and compute C = V + enc(mu) (mod q) + PQCLEAN_FRODOKEM640AES_CLEAN_key_encode(C, (uint16_t *)mu); + PQCLEAN_FRODOKEM640AES_CLEAN_add(C, V, C); + PQCLEAN_FRODOKEM640AES_CLEAN_pack(ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, C, PARAMS_NBAR * PARAMS_NBAR, PARAMS_LOGQ); + + // Compute ss = F(ct||KK) + memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); + memcpy(Fin_k, k, CRYPTO_BYTES); + shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); + + // Cleanup: + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes((uint8_t *)V, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes(mu, BYTES_MU); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes(G2out, 2 * CRYPTO_BYTES); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes(Fin_k, CRYPTO_BYTES); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); + return 0; +} + + +int PQCLEAN_FRODOKEM640AES_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { + // FrodoKEM's key decapsulation + uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t W[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data + uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; + uint16_t CC[PARAMS_NBAR * PARAMS_NBAR] = {0}; + uint16_t BBp[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data + uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data + uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data + const uint8_t *ct_c1 = &ct[0]; + const uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; + const uint8_t *sk_s = &sk[0]; + const uint8_t *sk_pk = &sk[CRYPTO_BYTES]; + const uint16_t *sk_S = (uint16_t *) &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; + uint16_t S[PARAMS_N * PARAMS_NBAR]; // contains secret data + const uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; + const uint8_t *pk_seedA = &sk_pk[0]; + const uint8_t *pk_b = &sk_pk[BYTES_SEED_A]; + uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via muprime + uint8_t *pkh = &G2in[0]; + uint8_t *muprime = &G2in[BYTES_PKHASH]; // contains secret data + uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data + uint8_t *seedSEprime = &G2out[0]; // contains secret data + uint8_t *kprime = &G2out[CRYPTO_BYTES]; // contains secret data + uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k + uint8_t *Fin_ct = &Fin[0]; + uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data + uint8_t shake_input_seedSEprime[1 + CRYPTO_BYTES]; // contains secret data + + for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { + S[i] = PQCLEAN_FRODOKEM640AES_CLEAN_LE_TO_UINT16(sk_S[i]); + } + + // Compute W = C - Bp*S (mod q), and decode the randomness mu + PQCLEAN_FRODOKEM640AES_CLEAN_unpack(Bp, PARAMS_N * PARAMS_NBAR, ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, PARAMS_LOGQ); + PQCLEAN_FRODOKEM640AES_CLEAN_unpack(C, PARAMS_NBAR * PARAMS_NBAR, ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, PARAMS_LOGQ); + PQCLEAN_FRODOKEM640AES_CLEAN_mul_bs(W, Bp, S); + PQCLEAN_FRODOKEM640AES_CLEAN_sub(W, C, W); + PQCLEAN_FRODOKEM640AES_CLEAN_key_decode((uint16_t *)muprime, W); + + // Generate (seedSE' || k') = G_2(pkh || mu') + memcpy(pkh, sk_pkh, BYTES_PKHASH); + shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); + + // Generate Sp and Ep, and compute BBp = Sp*A + Ep. Generate A on-the-fly + shake_input_seedSEprime[0] = 0x96; + memcpy(&shake_input_seedSEprime[1], seedSEprime, CRYPTO_BYTES); + shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSEprime, 1 + CRYPTO_BYTES); + for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { + Sp[i] = PQCLEAN_FRODOKEM640AES_CLEAN_LE_TO_UINT16(Sp[i]); + } + PQCLEAN_FRODOKEM640AES_CLEAN_sample_n(Sp, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM640AES_CLEAN_sample_n(Ep, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_sa_plus_e(BBp, Sp, Ep, pk_seedA); + + // Generate Epp, and compute W = Sp*B + Epp + PQCLEAN_FRODOKEM640AES_CLEAN_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); + PQCLEAN_FRODOKEM640AES_CLEAN_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); + PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_sb_plus_e(W, B, Sp, Epp); + + // Encode mu, and compute CC = W + enc(mu') (mod q) + PQCLEAN_FRODOKEM640AES_CLEAN_key_encode(CC, (uint16_t *)muprime); + PQCLEAN_FRODOKEM640AES_CLEAN_add(CC, W, CC); + + // Prepare input to F + memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); + + // Reducing BBp modulo q + for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { + BBp[i] = BBp[i] & ((1 << PARAMS_LOGQ) - 1); + } + + // Is (Bp == BBp & C == CC) = true + if (memcmp(Bp, BBp, 2 * PARAMS_N * PARAMS_NBAR) == 0 && memcmp(C, CC, 2 * PARAMS_NBAR * PARAMS_NBAR) == 0) { + // Load k' to do ss = F(ct || k') + memcpy(Fin_k, kprime, CRYPTO_BYTES); + } else { + // Load s to do ss = F(ct || s) + memcpy(Fin_k, sk_s, CRYPTO_BYTES); + } + shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); + + // Cleanup: + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes((uint8_t *)W, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes(muprime, BYTES_MU); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes(G2out, 2 * CRYPTO_BYTES); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes(Fin_k, CRYPTO_BYTES); + PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes(shake_input_seedSEprime, 1 + CRYPTO_BYTES); + return 0; +} diff --git a/crypto_kem/frodokem640aes/clean/matrix_aes.c b/crypto_kem/frodokem640aes/clean/matrix_aes.c new file mode 100644 index 00000000..2a1aef2d --- /dev/null +++ b/crypto_kem/frodokem640aes/clean/matrix_aes.c @@ -0,0 +1,85 @@ +/******************************************************************************************** +* FrodoKEM: Learning with Errors Key Encapsulation +* +* Abstract: matrix arithmetic functions used by the KEM +*********************************************************************************************/ + +#include +#include + +#include "aes.h" + +#include "api.h" +#include "common.h" +#include "params.h" + +int PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { + // Generate-and-multiply: generate matrix A (N x N) row-wise, multiply by s on the right. + // Inputs: s, e (N x N_BAR) + // Output: out = A*s + e (N x N_BAR) + int i, j, k; + int16_t A[PARAMS_N * PARAMS_N] = {0}; + aes128ctx ctx128; + + aes128_keyexp(&ctx128, seed_A); + for (i = 0; i < PARAMS_N; i++) { + for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { + A[i*PARAMS_N + j] = i; // Loading values in the little-endian order + A[i*PARAMS_N + j + 1] = j; + } + } + aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); + + for (i = 0; i < PARAMS_N * PARAMS_N; i++) { + A[i] = PQCLEAN_FRODOKEM640AES_CLEAN_LE_TO_UINT16(A[i]); + } + memcpy(out, e, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t)); + + for (i = 0; i < PARAMS_N; i++) { // Matrix multiplication-addition A*s + e + for (k = 0; k < PARAMS_NBAR; k++) { + uint16_t sum = 0; + for (j = 0; j < PARAMS_N; j++) { + sum += A[i * PARAMS_N + j] * s[k * PARAMS_N + j]; + } + out[i * PARAMS_NBAR + k] += sum; // Adding e. No need to reduce modulo 2^15, extra bits are taken care of during packing later on. + } + } + + return 1; +} + + +int PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { + // Generate-and-multiply: generate matrix A (N x N) column-wise, multiply by s' on the left. + // Inputs: s', e' (N_BAR x N) + // Output: out = s'*A + e' (N_BAR x N) + int i, j, k; + int16_t A[PARAMS_N * PARAMS_N] = {0}; + aes128ctx ctx128; + + aes128_keyexp(&ctx128, seed_A); + for (i = 0; i < PARAMS_N; i++) { + for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { + A[i*PARAMS_N + j] = i; // Loading values in the little-endian order + A[i*PARAMS_N + j + 1] = j; + } + } + aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); + + for (i = 0; i < PARAMS_N * PARAMS_N; i++) { + A[i] = PQCLEAN_FRODOKEM640AES_CLEAN_LE_TO_UINT16(A[i]); + } + memcpy(out, e, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t)); + + for (i = 0; i < PARAMS_N; i++) { // Matrix multiplication-addition A*s + e + for (k = 0; k < PARAMS_NBAR; k++) { + uint16_t sum = 0; + for (j = 0; j < PARAMS_N; j++) { + sum += A[j * PARAMS_N + i] * s[k * PARAMS_N + j]; + } + out[k * PARAMS_N + i] += sum; // Adding e. No need to reduce modulo 2^15, extra bits are taken care of during packing later on. + } + } + + return 1; +} diff --git a/crypto_kem/frodokem640aes/clean/noise.c b/crypto_kem/frodokem640aes/clean/noise.c new file mode 100644 index 00000000..154e7b7c --- /dev/null +++ b/crypto_kem/frodokem640aes/clean/noise.c @@ -0,0 +1,33 @@ +/******************************************************************************************** +* FrodoKEM: Learning with Errors Key Encapsulation +* +* Abstract: noise sampling functions +*********************************************************************************************/ + +#include + +#include "api.h" +#include "params.h" + +static uint16_t CDF_TABLE[CDF_TABLE_LEN] = CDF_TABLE_DATA; + +void PQCLEAN_FRODOKEM640AES_CLEAN_sample_n(uint16_t *s, const size_t n) { + // Fills vector s with n samples from the noise distribution which requires 16 bits to sample. + // The distribution is specified by its CDF. + // Input: pseudo-random values (2*n bytes) passed in s. The input is overwritten by the output. + unsigned int i, j; + + for (i = 0; i < n; ++i) { + uint8_t sample = 0; + uint16_t prnd = s[i] >> 1; // Drop the least significant bit + uint8_t sign = s[i] & 0x1; // Pick the least significant bit + + // No need to compare with the last value. + for (j = 0; j < (unsigned int)(CDF_TABLE_LEN - 1); j++) { + // Constant time comparison: 1 if CDF_TABLE[j] < s, 0 otherwise. Uses the fact that CDF_TABLE[j] and s fit in 15 bits. + sample += (uint16_t)(CDF_TABLE[j] - prnd) >> 15; + } + // Assuming that sign is either 0 or 1, flips sample iff sign = 1 + s[i] = ((-sign) ^ sample) + sign; + } +} diff --git a/crypto_kem/frodokem640aes/clean/params.h b/crypto_kem/frodokem640aes/clean/params.h new file mode 100644 index 00000000..08d6df39 --- /dev/null +++ b/crypto_kem/frodokem640aes/clean/params.h @@ -0,0 +1,27 @@ +#ifndef PARAMS_H +#define PARAMS_H + +#define CRYPTO_SECRETKEYBYTES PQCLEAN_FRODOKEM640AES_CLEAN_CRYPTO_SECRETKEYBYTES +#define CRYPTO_PUBLICKEYBYTES PQCLEAN_FRODOKEM640AES_CLEAN_CRYPTO_PUBLICKEYBYTES +#define CRYPTO_BYTES PQCLEAN_FRODOKEM640AES_CLEAN_CRYPTO_BYTES +#define CRYPTO_CIPHERTEXTBYTES PQCLEAN_FRODOKEM640AES_CLEAN_CRYPTO_CIPHERTEXTBYTES + +#define PARAMS_N 640 +#define PARAMS_NBAR 8 +#define PARAMS_LOGQ 15 +#define PARAMS_Q (1 << PARAMS_LOGQ) +#define PARAMS_EXTRACTED_BITS 2 +#define PARAMS_STRIPE_STEP 8 +#define PARAMS_PARALLEL 4 +#define BYTES_SEED_A 16 +#define BYTES_MU ((PARAMS_EXTRACTED_BITS * PARAMS_NBAR * PARAMS_NBAR) / 8) +#define BYTES_PKHASH CRYPTO_BYTES + +// Selecting SHAKE XOF function for the KEM and noise sampling +#define shake shake128 + +// CDF table +#define CDF_TABLE_DATA {4643, 13363, 20579, 25843, 29227, 31145, 32103, 32525, 32689, 32745, 32762, 32766, 32767} +#define CDF_TABLE_LEN 13 + +#endif diff --git a/crypto_kem/frodokem640aes/clean/util.c b/crypto_kem/frodokem640aes/clean/util.c new file mode 100644 index 00000000..4fec5624 --- /dev/null +++ b/crypto_kem/frodokem640aes/clean/util.c @@ -0,0 +1,234 @@ +/******************************************************************************************** +* FrodoKEM: Learning with Errors Key Encapsulation +* +* Abstract: additional functions for FrodoKEM +*********************************************************************************************/ + +#include +#include + +#include "api.h" +#include "params.h" + +#define min(x, y) (((x) < (y)) ? (x) : (y)) + +uint16_t PQCLEAN_FRODOKEM640AES_CLEAN_LE_TO_UINT16(const uint16_t n) { + return (((uint8_t *) &(n))[0] | (((uint8_t *) &(n))[1] << 8)); +} + +uint16_t PQCLEAN_FRODOKEM640AES_CLEAN_UINT16_TO_LE(const uint16_t n) { + uint16_t y; + uint8_t *z = (uint8_t *) &y; + z[0] = n & 0xFF; + z[1] = (n & 0xFF00) >> 8; + return y; +} + +void PQCLEAN_FRODOKEM640AES_CLEAN_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s) { + // Multiply by s on the right + // Inputs: b (N_BAR x N), s (N x N_BAR) + // Output: out = b*s (N_BAR x N_BAR) + int i, j, k; + + for (i = 0; i < PARAMS_NBAR; i++) { + for (j = 0; j < PARAMS_NBAR; j++) { + out[i * PARAMS_NBAR + j] = 0; + for (k = 0; k < PARAMS_N; k++) { + out[i * PARAMS_NBAR + j] += b[i * PARAMS_N + k] * s[j * PARAMS_N + k]; + } + out[i * PARAMS_NBAR + j] = (uint32_t)(out[i * PARAMS_NBAR + j]) & ((1 << PARAMS_LOGQ) - 1); + } + } +} + + +void PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e) { + // Multiply by s on the left + // Inputs: b (N x N_BAR), s (N_BAR x N), e (N_BAR x N_BAR) + // Output: out = s*b + e (N_BAR x N_BAR) + int i, j, k; + + for (k = 0; k < PARAMS_NBAR; k++) { + for (i = 0; i < PARAMS_NBAR; i++) { + out[k * PARAMS_NBAR + i] = e[k * PARAMS_NBAR + i]; + for (j = 0; j < PARAMS_N; j++) { + out[k * PARAMS_NBAR + i] += s[k * PARAMS_N + j] * b[j * PARAMS_NBAR + i]; + } + out[k * PARAMS_NBAR + i] = (uint32_t)(out[k * PARAMS_NBAR + i]) & ((1 << PARAMS_LOGQ) - 1); + } + } +} + + +void PQCLEAN_FRODOKEM640AES_CLEAN_add(uint16_t *out, const uint16_t *a, const uint16_t *b) { + // Add a and b + // Inputs: a, b (N_BAR x N_BAR) + // Output: c = a + b + + for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { + out[i] = (a[i] + b[i]) & ((1 << PARAMS_LOGQ) - 1); + } +} + + +void PQCLEAN_FRODOKEM640AES_CLEAN_sub(uint16_t *out, const uint16_t *a, const uint16_t *b) { + // Subtract a and b + // Inputs: a, b (N_BAR x N_BAR) + // Output: c = a - b + + for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { + out[i] = (a[i] - b[i]) & ((1 << PARAMS_LOGQ) - 1); + } +} + + +void PQCLEAN_FRODOKEM640AES_CLEAN_key_encode(uint16_t *out, const uint16_t *in) { + // Encoding + unsigned int i, j, npieces_word = 8; + unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; + uint64_t temp, mask = ((uint64_t)1 << PARAMS_EXTRACTED_BITS) - 1; + uint16_t *pos = out; + + for (i = 0; i < nwords; i++) { + temp = 0; + for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { + temp |= ((uint64_t)((uint8_t *)in)[i * PARAMS_EXTRACTED_BITS + j]) << (8 * j); + } + for (j = 0; j < npieces_word; j++) { + *pos = (uint16_t)((temp & mask) << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS)); + temp >>= PARAMS_EXTRACTED_BITS; + pos++; + } + } +} + + +void PQCLEAN_FRODOKEM640AES_CLEAN_key_decode(uint16_t *out, const uint16_t *in) { + // Decoding + unsigned int i, j, index = 0, npieces_word = 8; + unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; + uint16_t temp, maskex = ((uint16_t)1 << PARAMS_EXTRACTED_BITS) - 1, maskq = ((uint16_t)1 << PARAMS_LOGQ) - 1; + uint8_t *pos = (uint8_t *)out; + uint64_t templong; + + for (i = 0; i < nwords; i++) { + templong = 0; + for (j = 0; j < npieces_word; j++) { // temp = floor(in*2^{-11}+0.5) + temp = ((in[index] & maskq) + (1 << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS - 1))) >> (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS); + templong |= ((uint64_t)(temp & maskex)) << (PARAMS_EXTRACTED_BITS * j); + index++; + } + for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { + pos[i * PARAMS_EXTRACTED_BITS + j] = (templong >> (8 * j)) & 0xFF; + } + } +} + + +void PQCLEAN_FRODOKEM640AES_CLEAN_pack(uint8_t *out, const size_t outlen, const uint16_t *in, const size_t inlen, const uint8_t lsb) { + // Pack the input uint16 vector into a char output vector, copying lsb bits from each input element. + // If inlen * lsb / 8 > outlen, only outlen * 8 bits are copied. + memset(out, 0, outlen); + + size_t i = 0; // whole bytes already filled in + size_t j = 0; // whole uint16_t already copied + uint16_t w = 0; // the leftover, not yet copied + uint8_t bits = 0; // the number of lsb in w + + while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { + /* + in: | | |********|********| + ^ + j + w : | ****| + ^ + bits + out:|**|**|**|**|**|**|**|**|* | + ^^ + ib + */ + uint8_t b = 0; // bits in out[i] already filled in + while (b < 8) { + int nbits = min(8 - b, bits); + uint16_t mask = (1 << nbits) - 1; + uint8_t t = (uint8_t) ((w >> (bits - nbits)) & mask); // the bits to copy from w to out + out[i] = out[i] + (t << (8 - b - nbits)); + b += (uint8_t) nbits; + bits -= (uint8_t) nbits; + w &= ~(mask << bits); // not strictly necessary; mostly for debugging + + if (bits == 0) { + if (j < inlen) { + w = in[j]; + bits = lsb; + j++; + } else { + break; // the input vector is exhausted + } + } + } + if (b == 8) { // out[i] is filled in + i++; + } + } +} + + +void PQCLEAN_FRODOKEM640AES_CLEAN_unpack(uint16_t *out, const size_t outlen, const uint8_t *in, const size_t inlen, const uint8_t lsb) { + // Unpack the input char vector into a uint16_t output vector, copying lsb bits + // for each output element from input. outlen must be at least ceil(inlen * 8 / lsb). + memset(out, 0, outlen * sizeof(uint16_t)); + + size_t i = 0; // whole uint16_t already filled in + size_t j = 0; // whole bytes already copied + uint8_t w = 0; // the leftover, not yet copied + uint8_t bits = 0; // the number of lsb bits of w + + while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { + /* + in: | | | | | | |**|**|... + ^ + j + w : | *| + ^ + bits + out:| *****| *****| *** | |... + ^ ^ + i b + */ + uint8_t b = 0; // bits in out[i] already filled in + while (b < lsb) { + int nbits = min(lsb - b, bits); + uint16_t mask = (1 << nbits) - 1; + uint8_t t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out + out[i] = out[i] + (t << (lsb - b - nbits)); + b += (uint8_t) nbits; + bits -= (uint8_t) nbits; + w &= ~(mask << bits); // not strictly necessary; mostly for debugging + + if (bits == 0) { + if (j < inlen) { + w = in[j]; + bits = 8; + j++; + } else { + break; // the input vector is exhausted + } + } + } + if (b == lsb) { // out[i] is filled in + i++; + } + } +} + + +void PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes(uint8_t *mem, size_t n) { + // Clear 8-bit bytes from memory. "n" indicates the number of bytes to be zeroed. + // This function uses the volatile type qualifier to inform the compiler not to optimize out the memory clearing. + volatile uint8_t *v = mem; + + for (size_t i = 0; i < n; i++) { + v[i] = 0; + } +} diff --git a/test/duplicate_consistency/frodokem640aes_clean.yml b/test/duplicate_consistency/frodokem640aes_clean.yml new file mode 100644 index 00000000..dd50d9ca --- /dev/null +++ b/test/duplicate_consistency/frodokem640aes_clean.yml @@ -0,0 +1,9 @@ +source: + scheme: frodokem640shake + implementation: clean +files: + - common.h + - params.h + - kem.c + - noise.c + - util.c From 395f14a2f6ecbaf1869d90b4e623c99eaa28e939 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Wed, 10 Apr 2019 12:07:49 -0400 Subject: [PATCH 03/10] Allow multiple sources for duplicate consistency checks; break into multiple unit test to improve output --- .../frodokem1344shake_clean.yml | 19 ++++++------ .../frodokem640aes_clean.yml | 19 ++++++------ .../frodokem976shake_clean.yml | 19 ++++++------ test/test_duplicate_consistency.py | 31 ++++++++++--------- 4 files changed, 46 insertions(+), 42 deletions(-) diff --git a/test/duplicate_consistency/frodokem1344shake_clean.yml b/test/duplicate_consistency/frodokem1344shake_clean.yml index fb1fb3de..ee44e1ab 100644 --- a/test/duplicate_consistency/frodokem1344shake_clean.yml +++ b/test/duplicate_consistency/frodokem1344shake_clean.yml @@ -1,9 +1,10 @@ -source: - scheme: frodokem640shake - implementation: clean -files: - - common.h - - kem.c - - matrix_shake.c - - noise.c - - util.c +consistency_checks: +- source: + scheme: frodokem640shake + implementation: clean + files: + - common.h + - kem.c + - matrix_shake.c + - noise.c + - util.c diff --git a/test/duplicate_consistency/frodokem640aes_clean.yml b/test/duplicate_consistency/frodokem640aes_clean.yml index dd50d9ca..19c945d0 100644 --- a/test/duplicate_consistency/frodokem640aes_clean.yml +++ b/test/duplicate_consistency/frodokem640aes_clean.yml @@ -1,9 +1,10 @@ -source: - scheme: frodokem640shake - implementation: clean -files: - - common.h - - params.h - - kem.c - - noise.c - - util.c +consistency_checks: +- source: + scheme: frodokem640shake + implementation: clean + files: + - common.h + - params.h + - kem.c + - noise.c + - util.c diff --git a/test/duplicate_consistency/frodokem976shake_clean.yml b/test/duplicate_consistency/frodokem976shake_clean.yml index fb1fb3de..ee44e1ab 100644 --- a/test/duplicate_consistency/frodokem976shake_clean.yml +++ b/test/duplicate_consistency/frodokem976shake_clean.yml @@ -1,9 +1,10 @@ -source: - scheme: frodokem640shake - implementation: clean -files: - - common.h - - kem.c - - matrix_shake.c - - noise.c - - util.c +consistency_checks: +- source: + scheme: frodokem640shake + implementation: clean + files: + - common.h + - kem.c + - matrix_shake.c + - noise.c + - util.c diff --git a/test/test_duplicate_consistency.py b/test/test_duplicate_consistency.py index 52755a3b..6ee7c7e0 100644 --- a/test/test_duplicate_consistency.py +++ b/test/test_duplicate_consistency.py @@ -9,29 +9,30 @@ import unittest import yaml def test_duplicate_consistency(): + helpers.skip_windows() for scheme in pqclean.Scheme.all_schemes(): for implementation in scheme.implementations: if os.path.isfile(os.path.join('duplicate_consistency', '{}_{}.yml'.format(scheme.name, implementation.name))): - yield check_duplicate_consistency, implementation + metafile = os.path.join('duplicate_consistency', '{}_{}.yml'.format(implementation.scheme.name, implementation.name)) + with open(metafile, encoding='utf-8') as f: + metadata = yaml.load(f.read()) + for group in metadata['consistency_checks']: + source = pqclean.Implementation.by_name(group['source']['scheme'], group['source']['implementation']) + for file in group['files']: + yield check_duplicate_consistency, implementation, source, file def file_get_contents(filename): with open(filename) as f: return f.read() -def check_duplicate_consistency(implementation): - helpers.skip_windows() - metafile = os.path.join('duplicate_consistency', '{}_{}.yml'.format(implementation.scheme.name, implementation.name)) - with open(metafile, encoding='utf-8') as f: - metadata = yaml.load(f.read()) - source = pqclean.Implementation.by_name(metadata['source']['scheme'], metadata['source']['implementation']) - for file in metadata['files']: - transformed_src = helpers.run_subprocess( - ['sed', '-e', 's/{}/{}/g'.format(source.namespace_prefix(), implementation.namespace_prefix()), os.path.join(source.path(), file)] - ) - this_src = file_get_contents(os.path.join(implementation.path(), file)) - print(os.path.join(implementation.path(), file)) - print(this_src) - assert(transformed_src == this_src) +def check_duplicate_consistency(implementation, source, file): + transformed_src = helpers.run_subprocess( + ['sed', '-e', 's/{}/{}/g'.format(source.namespace_prefix(), implementation.namespace_prefix()), os.path.join(source.path(), file)] + ) + this_src = file_get_contents(os.path.join(implementation.path(), file)) + print(os.path.join(implementation.path(), file)) + print(this_src) + assert(transformed_src == this_src) if __name__ == '__main__': try: From 999ea3afedc5e1afa88ccc8a57cf47721ef11e82 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Wed, 10 Apr 2019 12:11:27 -0400 Subject: [PATCH 04/10] Add FrodoKEM-976-AES --- crypto_kem/frodokem976aes/META.yml | 24 ++ crypto_kem/frodokem976aes/clean/LICENSE | 21 ++ crypto_kem/frodokem976aes/clean/Makefile | 19 ++ .../clean/Makefile.Microsoft_nmake | 19 ++ crypto_kem/frodokem976aes/clean/api.h | 20 ++ crypto_kem/frodokem976aes/clean/common.h | 19 ++ crypto_kem/frodokem976aes/clean/kem.c | 238 ++++++++++++++++++ crypto_kem/frodokem976aes/clean/matrix_aes.c | 85 +++++++ crypto_kem/frodokem976aes/clean/noise.c | 33 +++ crypto_kem/frodokem976aes/clean/params.h | 27 ++ crypto_kem/frodokem976aes/clean/util.c | 234 +++++++++++++++++ .../frodokem976aes_clean.yml | 15 ++ 12 files changed, 754 insertions(+) create mode 100644 crypto_kem/frodokem976aes/META.yml create mode 100644 crypto_kem/frodokem976aes/clean/LICENSE create mode 100644 crypto_kem/frodokem976aes/clean/Makefile create mode 100644 crypto_kem/frodokem976aes/clean/Makefile.Microsoft_nmake create mode 100644 crypto_kem/frodokem976aes/clean/api.h create mode 100644 crypto_kem/frodokem976aes/clean/common.h create mode 100644 crypto_kem/frodokem976aes/clean/kem.c create mode 100644 crypto_kem/frodokem976aes/clean/matrix_aes.c create mode 100644 crypto_kem/frodokem976aes/clean/noise.c create mode 100644 crypto_kem/frodokem976aes/clean/params.h create mode 100644 crypto_kem/frodokem976aes/clean/util.c create mode 100644 test/duplicate_consistency/frodokem976aes_clean.yml diff --git a/crypto_kem/frodokem976aes/META.yml b/crypto_kem/frodokem976aes/META.yml new file mode 100644 index 00000000..7c915c4c --- /dev/null +++ b/crypto_kem/frodokem976aes/META.yml @@ -0,0 +1,24 @@ +name: FrodoKEM-976-AES +type: kem +claimed-nist-level: 1 +length-public-key: 15632 +length-ciphertext: 15744 +length-shared-secret: 24 +testvectors-sha256: 30a2a3f2d834b5d90cd10241f53c4a4379abeea0dbd4eb65b260749b2ba81391 +principal-submitter: Douglas Stebila, University of Waterloo +auxiliary-submitters: +- Erdem Alkim +- Joppe W. Bos, NXP Semiconductors +- Léo Ducas, CWI +- Patrick Longa, Microsoft Research +- Ilya Mironov, Google +- Michael Naehrig, Microsoft Research +- Valeria Nikolaenko +- Chris Peikert, University of Michigan +- Ananth Raghunathan, Google +- Karen Easterbrook, Microsoft Research +- Brian LaMacchia, Microsoft Research +implementations: +- name: clean + version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/437e228fca580a82435cab09f30ae14b03183119 + length-secret-key: 31296 diff --git a/crypto_kem/frodokem976aes/clean/LICENSE b/crypto_kem/frodokem976aes/clean/LICENSE new file mode 100644 index 00000000..5cf7c8db --- /dev/null +++ b/crypto_kem/frodokem976aes/clean/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE diff --git a/crypto_kem/frodokem976aes/clean/Makefile b/crypto_kem/frodokem976aes/clean/Makefile new file mode 100644 index 00000000..08f167b0 --- /dev/null +++ b/crypto_kem/frodokem976aes/clean/Makefile @@ -0,0 +1,19 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libfrodokem976aes_clean.a +HEADERS=api.h params.h common.h +OBJECTS=kem.o matrix_aes.o noise.o util.o + +CFLAGS=-Wall -Wextra -Wpedantic -Werror -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_kem/frodokem976aes/clean/Makefile.Microsoft_nmake b/crypto_kem/frodokem976aes/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..2f622409 --- /dev/null +++ b/crypto_kem/frodokem976aes/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libfrodokem976aes_clean.lib +OBJECTS=kem.obj matrix_aes.obj noise.obj util.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_kem/frodokem976aes/clean/api.h b/crypto_kem/frodokem976aes/clean/api.h new file mode 100644 index 00000000..4317bc69 --- /dev/null +++ b/crypto_kem/frodokem976aes/clean/api.h @@ -0,0 +1,20 @@ +#ifndef PQCLEAN_FRODOKEM976AES_CLEAN_API_H +#define PQCLEAN_FRODOKEM976AES_CLEAN_API_H + +#include +#include + +#define PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_SECRETKEYBYTES 31296 // sizeof(s) + CRYPTO_PUBLICKEYBYTES + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH +#define PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_PUBLICKEYBYTES 15632 // sizeof(seed_A) + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 +#define PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_BYTES 24 +#define PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_CIPHERTEXTBYTES 15744 // (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + (PARAMS_LOGQ*PARAMS_NBAR*PARAMS_NBAR)/8 + +#define PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_ALGNAME "FrodoKEM-976-AES" + +int PQCLEAN_FRODOKEM976AES_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); + +int PQCLEAN_FRODOKEM976AES_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); + +int PQCLEAN_FRODOKEM976AES_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); + +#endif diff --git a/crypto_kem/frodokem976aes/clean/common.h b/crypto_kem/frodokem976aes/clean/common.h new file mode 100644 index 00000000..c6d420bb --- /dev/null +++ b/crypto_kem/frodokem976aes/clean/common.h @@ -0,0 +1,19 @@ +#ifndef COMMON_H +#define COMMON_H + +int PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); +int PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); +void PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(uint16_t *s, size_t n); +void PQCLEAN_FRODOKEM976AES_CLEAN_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s); +void PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e); +void PQCLEAN_FRODOKEM976AES_CLEAN_add(uint16_t *out, const uint16_t *a, const uint16_t *b); +void PQCLEAN_FRODOKEM976AES_CLEAN_sub(uint16_t *out, const uint16_t *a, const uint16_t *b); +void PQCLEAN_FRODOKEM976AES_CLEAN_key_encode(uint16_t *out, const uint16_t *in); +void PQCLEAN_FRODOKEM976AES_CLEAN_key_decode(uint16_t *out, const uint16_t *in); +void PQCLEAN_FRODOKEM976AES_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); +void PQCLEAN_FRODOKEM976AES_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); +void PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(uint8_t *mem, size_t n); +uint16_t PQCLEAN_FRODOKEM976AES_CLEAN_LE_TO_UINT16(uint16_t n); +uint16_t PQCLEAN_FRODOKEM976AES_CLEAN_UINT16_TO_LE(uint16_t n); + +#endif diff --git a/crypto_kem/frodokem976aes/clean/kem.c b/crypto_kem/frodokem976aes/clean/kem.c new file mode 100644 index 00000000..0c46065e --- /dev/null +++ b/crypto_kem/frodokem976aes/clean/kem.c @@ -0,0 +1,238 @@ +/******************************************************************************************** +* FrodoKEM: Learning with Errors Key Encapsulation +* +* Abstract: Key Encapsulation Mechanism (KEM) based on Frodo +*********************************************************************************************/ + +#include +#include + +#include "fips202.h" +#include "randombytes.h" + +#include "api.h" +#include "common.h" +#include "params.h" + +int PQCLEAN_FRODOKEM976AES_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + // FrodoKEM's key generation + // Outputs: public key pk ( BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 bytes) + // secret key sk (CRYPTO_BYTES + BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH bytes) + uint8_t *pk_seedA = &pk[0]; + uint8_t *pk_b = &pk[BYTES_SEED_A]; + uint8_t *sk_s = &sk[0]; + uint8_t *sk_pk = &sk[CRYPTO_BYTES]; + uint8_t *sk_S = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; + uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; + uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t S[2 * PARAMS_N * PARAMS_NBAR] = {0}; // contains secret data + uint16_t *E = &S[PARAMS_N * PARAMS_NBAR]; // contains secret data + uint8_t randomness[2 * CRYPTO_BYTES + BYTES_SEED_A]; // contains secret data via randomness_s and randomness_seedSE + uint8_t *randomness_s = &randomness[0]; // contains secret data + uint8_t *randomness_seedSE = &randomness[CRYPTO_BYTES]; // contains secret data + uint8_t *randomness_z = &randomness[2 * CRYPTO_BYTES]; + uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data + + // Generate the secret value s, the seed for S and E, and the seed for the seed for A. Add seed_A to the public key + randombytes(randomness, CRYPTO_BYTES + CRYPTO_BYTES + BYTES_SEED_A); + shake(pk_seedA, BYTES_SEED_A, randomness_z, BYTES_SEED_A); + + // Generate S and E, and compute B = A*S + E. Generate A on-the-fly + shake_input_seedSE[0] = 0x5F; + memcpy(&shake_input_seedSE[1], randomness_seedSE, CRYPTO_BYTES); + shake((uint8_t *)S, 2 * PARAMS_N * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); + for (size_t i = 0; i < 2 * PARAMS_N * PARAMS_NBAR; i++) { + S[i] = PQCLEAN_FRODOKEM976AES_CLEAN_LE_TO_UINT16(S[i]); + } + PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(S, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(E, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_as_plus_e(B, S, E, pk); + + // Encode the second part of the public key + PQCLEAN_FRODOKEM976AES_CLEAN_pack(pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, B, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); + + // Add s, pk and S to the secret key + memcpy(sk_s, randomness_s, CRYPTO_BYTES); + memcpy(sk_pk, pk, CRYPTO_PUBLICKEYBYTES); + for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { + S[i] = PQCLEAN_FRODOKEM976AES_CLEAN_UINT16_TO_LE(S[i]); + } + memcpy(sk_S, S, 2 * PARAMS_N * PARAMS_NBAR); + + // Add H(pk) to the secret key + shake(sk_pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); + + // Cleanup: + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)E, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(randomness, 2 * CRYPTO_BYTES); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); + return 0; +} + + +int PQCLEAN_FRODOKEM976AES_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { + // FrodoKEM's key encapsulation + const uint8_t *pk_seedA = &pk[0]; + const uint8_t *pk_b = &pk[BYTES_SEED_A]; + uint8_t *ct_c1 = &ct[0]; + uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; + uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t V[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data + uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; + uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data + uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data + uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data + uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via mu + uint8_t *pkh = &G2in[0]; + uint8_t *mu = &G2in[BYTES_PKHASH]; // contains secret data + uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data + uint8_t *seedSE = &G2out[0]; // contains secret data + uint8_t *k = &G2out[CRYPTO_BYTES]; // contains secret data + uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k + uint8_t *Fin_ct = &Fin[0]; + uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data + uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data + + // pkh <- G_1(pk), generate random mu, compute (seedSE || k) = G_2(pkh || mu) + shake(pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); + randombytes(mu, BYTES_MU); + shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); + + // Generate Sp and Ep, and compute Bp = Sp*A + Ep. Generate A on-the-fly + shake_input_seedSE[0] = 0x96; + memcpy(&shake_input_seedSE[1], seedSE, CRYPTO_BYTES); + shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); + for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { + Sp[i] = PQCLEAN_FRODOKEM976AES_CLEAN_LE_TO_UINT16(Sp[i]); + } + PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(Sp, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(Ep, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sa_plus_e(Bp, Sp, Ep, pk_seedA); + PQCLEAN_FRODOKEM976AES_CLEAN_pack(ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, Bp, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); + + // Generate Epp, and compute V = Sp*B + Epp + PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); + PQCLEAN_FRODOKEM976AES_CLEAN_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); + PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sb_plus_e(V, B, Sp, Epp); + + // Encode mu, and compute C = V + enc(mu) (mod q) + PQCLEAN_FRODOKEM976AES_CLEAN_key_encode(C, (uint16_t *)mu); + PQCLEAN_FRODOKEM976AES_CLEAN_add(C, V, C); + PQCLEAN_FRODOKEM976AES_CLEAN_pack(ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, C, PARAMS_NBAR * PARAMS_NBAR, PARAMS_LOGQ); + + // Compute ss = F(ct||KK) + memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); + memcpy(Fin_k, k, CRYPTO_BYTES); + shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); + + // Cleanup: + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)V, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(mu, BYTES_MU); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(G2out, 2 * CRYPTO_BYTES); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(Fin_k, CRYPTO_BYTES); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); + return 0; +} + + +int PQCLEAN_FRODOKEM976AES_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { + // FrodoKEM's key decapsulation + uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t W[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data + uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; + uint16_t CC[PARAMS_NBAR * PARAMS_NBAR] = {0}; + uint16_t BBp[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data + uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data + uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data + const uint8_t *ct_c1 = &ct[0]; + const uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; + const uint8_t *sk_s = &sk[0]; + const uint8_t *sk_pk = &sk[CRYPTO_BYTES]; + const uint16_t *sk_S = (uint16_t *) &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; + uint16_t S[PARAMS_N * PARAMS_NBAR]; // contains secret data + const uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; + const uint8_t *pk_seedA = &sk_pk[0]; + const uint8_t *pk_b = &sk_pk[BYTES_SEED_A]; + uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via muprime + uint8_t *pkh = &G2in[0]; + uint8_t *muprime = &G2in[BYTES_PKHASH]; // contains secret data + uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data + uint8_t *seedSEprime = &G2out[0]; // contains secret data + uint8_t *kprime = &G2out[CRYPTO_BYTES]; // contains secret data + uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k + uint8_t *Fin_ct = &Fin[0]; + uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data + uint8_t shake_input_seedSEprime[1 + CRYPTO_BYTES]; // contains secret data + + for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { + S[i] = PQCLEAN_FRODOKEM976AES_CLEAN_LE_TO_UINT16(sk_S[i]); + } + + // Compute W = C - Bp*S (mod q), and decode the randomness mu + PQCLEAN_FRODOKEM976AES_CLEAN_unpack(Bp, PARAMS_N * PARAMS_NBAR, ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, PARAMS_LOGQ); + PQCLEAN_FRODOKEM976AES_CLEAN_unpack(C, PARAMS_NBAR * PARAMS_NBAR, ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, PARAMS_LOGQ); + PQCLEAN_FRODOKEM976AES_CLEAN_mul_bs(W, Bp, S); + PQCLEAN_FRODOKEM976AES_CLEAN_sub(W, C, W); + PQCLEAN_FRODOKEM976AES_CLEAN_key_decode((uint16_t *)muprime, W); + + // Generate (seedSE' || k') = G_2(pkh || mu') + memcpy(pkh, sk_pkh, BYTES_PKHASH); + shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); + + // Generate Sp and Ep, and compute BBp = Sp*A + Ep. Generate A on-the-fly + shake_input_seedSEprime[0] = 0x96; + memcpy(&shake_input_seedSEprime[1], seedSEprime, CRYPTO_BYTES); + shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSEprime, 1 + CRYPTO_BYTES); + for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { + Sp[i] = PQCLEAN_FRODOKEM976AES_CLEAN_LE_TO_UINT16(Sp[i]); + } + PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(Sp, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(Ep, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sa_plus_e(BBp, Sp, Ep, pk_seedA); + + // Generate Epp, and compute W = Sp*B + Epp + PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); + PQCLEAN_FRODOKEM976AES_CLEAN_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); + PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sb_plus_e(W, B, Sp, Epp); + + // Encode mu, and compute CC = W + enc(mu') (mod q) + PQCLEAN_FRODOKEM976AES_CLEAN_key_encode(CC, (uint16_t *)muprime); + PQCLEAN_FRODOKEM976AES_CLEAN_add(CC, W, CC); + + // Prepare input to F + memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); + + // Reducing BBp modulo q + for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { + BBp[i] = BBp[i] & ((1 << PARAMS_LOGQ) - 1); + } + + // Is (Bp == BBp & C == CC) = true + if (memcmp(Bp, BBp, 2 * PARAMS_N * PARAMS_NBAR) == 0 && memcmp(C, CC, 2 * PARAMS_NBAR * PARAMS_NBAR) == 0) { + // Load k' to do ss = F(ct || k') + memcpy(Fin_k, kprime, CRYPTO_BYTES); + } else { + // Load s to do ss = F(ct || s) + memcpy(Fin_k, sk_s, CRYPTO_BYTES); + } + shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); + + // Cleanup: + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)W, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(muprime, BYTES_MU); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(G2out, 2 * CRYPTO_BYTES); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(Fin_k, CRYPTO_BYTES); + PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(shake_input_seedSEprime, 1 + CRYPTO_BYTES); + return 0; +} diff --git a/crypto_kem/frodokem976aes/clean/matrix_aes.c b/crypto_kem/frodokem976aes/clean/matrix_aes.c new file mode 100644 index 00000000..1d660437 --- /dev/null +++ b/crypto_kem/frodokem976aes/clean/matrix_aes.c @@ -0,0 +1,85 @@ +/******************************************************************************************** +* FrodoKEM: Learning with Errors Key Encapsulation +* +* Abstract: matrix arithmetic functions used by the KEM +*********************************************************************************************/ + +#include +#include + +#include "aes.h" + +#include "api.h" +#include "common.h" +#include "params.h" + +int PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { + // Generate-and-multiply: generate matrix A (N x N) row-wise, multiply by s on the right. + // Inputs: s, e (N x N_BAR) + // Output: out = A*s + e (N x N_BAR) + int i, j, k; + int16_t A[PARAMS_N * PARAMS_N] = {0}; + aes128ctx ctx128; + + aes128_keyexp(&ctx128, seed_A); + for (i = 0; i < PARAMS_N; i++) { + for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { + A[i*PARAMS_N + j] = i; // Loading values in the little-endian order + A[i*PARAMS_N + j + 1] = j; + } + } + aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); + + for (i = 0; i < PARAMS_N * PARAMS_N; i++) { + A[i] = PQCLEAN_FRODOKEM976AES_CLEAN_LE_TO_UINT16(A[i]); + } + memcpy(out, e, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t)); + + for (i = 0; i < PARAMS_N; i++) { // Matrix multiplication-addition A*s + e + for (k = 0; k < PARAMS_NBAR; k++) { + uint16_t sum = 0; + for (j = 0; j < PARAMS_N; j++) { + sum += A[i * PARAMS_N + j] * s[k * PARAMS_N + j]; + } + out[i * PARAMS_NBAR + k] += sum; // Adding e. No need to reduce modulo 2^15, extra bits are taken care of during packing later on. + } + } + + return 1; +} + + +int PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { + // Generate-and-multiply: generate matrix A (N x N) column-wise, multiply by s' on the left. + // Inputs: s', e' (N_BAR x N) + // Output: out = s'*A + e' (N_BAR x N) + int i, j, k; + int16_t A[PARAMS_N * PARAMS_N] = {0}; + aes128ctx ctx128; + + aes128_keyexp(&ctx128, seed_A); + for (i = 0; i < PARAMS_N; i++) { + for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { + A[i*PARAMS_N + j] = i; // Loading values in the little-endian order + A[i*PARAMS_N + j + 1] = j; + } + } + aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); + + for (i = 0; i < PARAMS_N * PARAMS_N; i++) { + A[i] = PQCLEAN_FRODOKEM976AES_CLEAN_LE_TO_UINT16(A[i]); + } + memcpy(out, e, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t)); + + for (i = 0; i < PARAMS_N; i++) { // Matrix multiplication-addition A*s + e + for (k = 0; k < PARAMS_NBAR; k++) { + uint16_t sum = 0; + for (j = 0; j < PARAMS_N; j++) { + sum += A[j * PARAMS_N + i] * s[k * PARAMS_N + j]; + } + out[k * PARAMS_N + i] += sum; // Adding e. No need to reduce modulo 2^15, extra bits are taken care of during packing later on. + } + } + + return 1; +} diff --git a/crypto_kem/frodokem976aes/clean/noise.c b/crypto_kem/frodokem976aes/clean/noise.c new file mode 100644 index 00000000..9617138b --- /dev/null +++ b/crypto_kem/frodokem976aes/clean/noise.c @@ -0,0 +1,33 @@ +/******************************************************************************************** +* FrodoKEM: Learning with Errors Key Encapsulation +* +* Abstract: noise sampling functions +*********************************************************************************************/ + +#include + +#include "api.h" +#include "params.h" + +static uint16_t CDF_TABLE[CDF_TABLE_LEN] = CDF_TABLE_DATA; + +void PQCLEAN_FRODOKEM976AES_CLEAN_sample_n(uint16_t *s, const size_t n) { + // Fills vector s with n samples from the noise distribution which requires 16 bits to sample. + // The distribution is specified by its CDF. + // Input: pseudo-random values (2*n bytes) passed in s. The input is overwritten by the output. + unsigned int i, j; + + for (i = 0; i < n; ++i) { + uint8_t sample = 0; + uint16_t prnd = s[i] >> 1; // Drop the least significant bit + uint8_t sign = s[i] & 0x1; // Pick the least significant bit + + // No need to compare with the last value. + for (j = 0; j < (unsigned int)(CDF_TABLE_LEN - 1); j++) { + // Constant time comparison: 1 if CDF_TABLE[j] < s, 0 otherwise. Uses the fact that CDF_TABLE[j] and s fit in 15 bits. + sample += (uint16_t)(CDF_TABLE[j] - prnd) >> 15; + } + // Assuming that sign is either 0 or 1, flips sample iff sign = 1 + s[i] = ((-sign) ^ sample) + sign; + } +} diff --git a/crypto_kem/frodokem976aes/clean/params.h b/crypto_kem/frodokem976aes/clean/params.h new file mode 100644 index 00000000..1e4147e8 --- /dev/null +++ b/crypto_kem/frodokem976aes/clean/params.h @@ -0,0 +1,27 @@ +#ifndef PARAMS_H +#define PARAMS_H + +#define CRYPTO_SECRETKEYBYTES PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_SECRETKEYBYTES +#define CRYPTO_PUBLICKEYBYTES PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_PUBLICKEYBYTES +#define CRYPTO_BYTES PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_BYTES +#define CRYPTO_CIPHERTEXTBYTES PQCLEAN_FRODOKEM976AES_CLEAN_CRYPTO_CIPHERTEXTBYTES + +#define PARAMS_N 976 +#define PARAMS_NBAR 8 +#define PARAMS_LOGQ 16 +#define PARAMS_Q (1 << PARAMS_LOGQ) +#define PARAMS_EXTRACTED_BITS 3 +#define PARAMS_STRIPE_STEP 8 +#define PARAMS_PARALLEL 4 +#define BYTES_SEED_A 16 +#define BYTES_MU ((PARAMS_EXTRACTED_BITS * PARAMS_NBAR * PARAMS_NBAR) / 8) +#define BYTES_PKHASH CRYPTO_BYTES + +// Selecting SHAKE XOF function for the KEM and noise sampling +#define shake shake256 + +// CDF table +#define CDF_TABLE_DATA {5638, 15915, 23689, 28571, 31116, 32217, 32613, 32731, 32760, 32766, 32767} +#define CDF_TABLE_LEN 11 + +#endif diff --git a/crypto_kem/frodokem976aes/clean/util.c b/crypto_kem/frodokem976aes/clean/util.c new file mode 100644 index 00000000..39516957 --- /dev/null +++ b/crypto_kem/frodokem976aes/clean/util.c @@ -0,0 +1,234 @@ +/******************************************************************************************** +* FrodoKEM: Learning with Errors Key Encapsulation +* +* Abstract: additional functions for FrodoKEM +*********************************************************************************************/ + +#include +#include + +#include "api.h" +#include "params.h" + +#define min(x, y) (((x) < (y)) ? (x) : (y)) + +uint16_t PQCLEAN_FRODOKEM976AES_CLEAN_LE_TO_UINT16(const uint16_t n) { + return (((uint8_t *) &(n))[0] | (((uint8_t *) &(n))[1] << 8)); +} + +uint16_t PQCLEAN_FRODOKEM976AES_CLEAN_UINT16_TO_LE(const uint16_t n) { + uint16_t y; + uint8_t *z = (uint8_t *) &y; + z[0] = n & 0xFF; + z[1] = (n & 0xFF00) >> 8; + return y; +} + +void PQCLEAN_FRODOKEM976AES_CLEAN_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s) { + // Multiply by s on the right + // Inputs: b (N_BAR x N), s (N x N_BAR) + // Output: out = b*s (N_BAR x N_BAR) + int i, j, k; + + for (i = 0; i < PARAMS_NBAR; i++) { + for (j = 0; j < PARAMS_NBAR; j++) { + out[i * PARAMS_NBAR + j] = 0; + for (k = 0; k < PARAMS_N; k++) { + out[i * PARAMS_NBAR + j] += b[i * PARAMS_N + k] * s[j * PARAMS_N + k]; + } + out[i * PARAMS_NBAR + j] = (uint32_t)(out[i * PARAMS_NBAR + j]) & ((1 << PARAMS_LOGQ) - 1); + } + } +} + + +void PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e) { + // Multiply by s on the left + // Inputs: b (N x N_BAR), s (N_BAR x N), e (N_BAR x N_BAR) + // Output: out = s*b + e (N_BAR x N_BAR) + int i, j, k; + + for (k = 0; k < PARAMS_NBAR; k++) { + for (i = 0; i < PARAMS_NBAR; i++) { + out[k * PARAMS_NBAR + i] = e[k * PARAMS_NBAR + i]; + for (j = 0; j < PARAMS_N; j++) { + out[k * PARAMS_NBAR + i] += s[k * PARAMS_N + j] * b[j * PARAMS_NBAR + i]; + } + out[k * PARAMS_NBAR + i] = (uint32_t)(out[k * PARAMS_NBAR + i]) & ((1 << PARAMS_LOGQ) - 1); + } + } +} + + +void PQCLEAN_FRODOKEM976AES_CLEAN_add(uint16_t *out, const uint16_t *a, const uint16_t *b) { + // Add a and b + // Inputs: a, b (N_BAR x N_BAR) + // Output: c = a + b + + for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { + out[i] = (a[i] + b[i]) & ((1 << PARAMS_LOGQ) - 1); + } +} + + +void PQCLEAN_FRODOKEM976AES_CLEAN_sub(uint16_t *out, const uint16_t *a, const uint16_t *b) { + // Subtract a and b + // Inputs: a, b (N_BAR x N_BAR) + // Output: c = a - b + + for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { + out[i] = (a[i] - b[i]) & ((1 << PARAMS_LOGQ) - 1); + } +} + + +void PQCLEAN_FRODOKEM976AES_CLEAN_key_encode(uint16_t *out, const uint16_t *in) { + // Encoding + unsigned int i, j, npieces_word = 8; + unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; + uint64_t temp, mask = ((uint64_t)1 << PARAMS_EXTRACTED_BITS) - 1; + uint16_t *pos = out; + + for (i = 0; i < nwords; i++) { + temp = 0; + for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { + temp |= ((uint64_t)((uint8_t *)in)[i * PARAMS_EXTRACTED_BITS + j]) << (8 * j); + } + for (j = 0; j < npieces_word; j++) { + *pos = (uint16_t)((temp & mask) << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS)); + temp >>= PARAMS_EXTRACTED_BITS; + pos++; + } + } +} + + +void PQCLEAN_FRODOKEM976AES_CLEAN_key_decode(uint16_t *out, const uint16_t *in) { + // Decoding + unsigned int i, j, index = 0, npieces_word = 8; + unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; + uint16_t temp, maskex = ((uint16_t)1 << PARAMS_EXTRACTED_BITS) - 1, maskq = ((uint16_t)1 << PARAMS_LOGQ) - 1; + uint8_t *pos = (uint8_t *)out; + uint64_t templong; + + for (i = 0; i < nwords; i++) { + templong = 0; + for (j = 0; j < npieces_word; j++) { // temp = floor(in*2^{-11}+0.5) + temp = ((in[index] & maskq) + (1 << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS - 1))) >> (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS); + templong |= ((uint64_t)(temp & maskex)) << (PARAMS_EXTRACTED_BITS * j); + index++; + } + for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { + pos[i * PARAMS_EXTRACTED_BITS + j] = (templong >> (8 * j)) & 0xFF; + } + } +} + + +void PQCLEAN_FRODOKEM976AES_CLEAN_pack(uint8_t *out, const size_t outlen, const uint16_t *in, const size_t inlen, const uint8_t lsb) { + // Pack the input uint16 vector into a char output vector, copying lsb bits from each input element. + // If inlen * lsb / 8 > outlen, only outlen * 8 bits are copied. + memset(out, 0, outlen); + + size_t i = 0; // whole bytes already filled in + size_t j = 0; // whole uint16_t already copied + uint16_t w = 0; // the leftover, not yet copied + uint8_t bits = 0; // the number of lsb in w + + while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { + /* + in: | | |********|********| + ^ + j + w : | ****| + ^ + bits + out:|**|**|**|**|**|**|**|**|* | + ^^ + ib + */ + uint8_t b = 0; // bits in out[i] already filled in + while (b < 8) { + int nbits = min(8 - b, bits); + uint16_t mask = (1 << nbits) - 1; + uint8_t t = (uint8_t) ((w >> (bits - nbits)) & mask); // the bits to copy from w to out + out[i] = out[i] + (t << (8 - b - nbits)); + b += (uint8_t) nbits; + bits -= (uint8_t) nbits; + w &= ~(mask << bits); // not strictly necessary; mostly for debugging + + if (bits == 0) { + if (j < inlen) { + w = in[j]; + bits = lsb; + j++; + } else { + break; // the input vector is exhausted + } + } + } + if (b == 8) { // out[i] is filled in + i++; + } + } +} + + +void PQCLEAN_FRODOKEM976AES_CLEAN_unpack(uint16_t *out, const size_t outlen, const uint8_t *in, const size_t inlen, const uint8_t lsb) { + // Unpack the input char vector into a uint16_t output vector, copying lsb bits + // for each output element from input. outlen must be at least ceil(inlen * 8 / lsb). + memset(out, 0, outlen * sizeof(uint16_t)); + + size_t i = 0; // whole uint16_t already filled in + size_t j = 0; // whole bytes already copied + uint8_t w = 0; // the leftover, not yet copied + uint8_t bits = 0; // the number of lsb bits of w + + while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { + /* + in: | | | | | | |**|**|... + ^ + j + w : | *| + ^ + bits + out:| *****| *****| *** | |... + ^ ^ + i b + */ + uint8_t b = 0; // bits in out[i] already filled in + while (b < lsb) { + int nbits = min(lsb - b, bits); + uint16_t mask = (1 << nbits) - 1; + uint8_t t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out + out[i] = out[i] + (t << (lsb - b - nbits)); + b += (uint8_t) nbits; + bits -= (uint8_t) nbits; + w &= ~(mask << bits); // not strictly necessary; mostly for debugging + + if (bits == 0) { + if (j < inlen) { + w = in[j]; + bits = 8; + j++; + } else { + break; // the input vector is exhausted + } + } + } + if (b == lsb) { // out[i] is filled in + i++; + } + } +} + + +void PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(uint8_t *mem, size_t n) { + // Clear 8-bit bytes from memory. "n" indicates the number of bytes to be zeroed. + // This function uses the volatile type qualifier to inform the compiler not to optimize out the memory clearing. + volatile uint8_t *v = mem; + + for (size_t i = 0; i < n; i++) { + v[i] = 0; + } +} diff --git a/test/duplicate_consistency/frodokem976aes_clean.yml b/test/duplicate_consistency/frodokem976aes_clean.yml new file mode 100644 index 00000000..a6ef9256 --- /dev/null +++ b/test/duplicate_consistency/frodokem976aes_clean.yml @@ -0,0 +1,15 @@ +consistency_checks: +- source: + scheme: frodokem640aes + implementation: clean + files: + - common.h + - kem.c + - matrix_aes.c + - noise.c + - util.c +- source: + scheme: frodokem976shake + implementation: clean + files: + - params.h From 30c8661d823abbddb28512a126a368d0e7d84e6b Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Wed, 10 Apr 2019 12:16:24 -0400 Subject: [PATCH 05/10] Add FrodoKEM-1344-AES --- crypto_kem/frodokem1344aes/META.yml | 24 ++ crypto_kem/frodokem1344aes/clean/LICENSE | 21 ++ crypto_kem/frodokem1344aes/clean/Makefile | 19 ++ .../clean/Makefile.Microsoft_nmake | 19 ++ crypto_kem/frodokem1344aes/clean/api.h | 20 ++ crypto_kem/frodokem1344aes/clean/common.h | 19 ++ crypto_kem/frodokem1344aes/clean/kem.c | 238 ++++++++++++++++++ crypto_kem/frodokem1344aes/clean/matrix_aes.c | 85 +++++++ crypto_kem/frodokem1344aes/clean/noise.c | 33 +++ crypto_kem/frodokem1344aes/clean/params.h | 27 ++ crypto_kem/frodokem1344aes/clean/util.c | 234 +++++++++++++++++ .../frodokem1344aes_clean.yml | 15 ++ 12 files changed, 754 insertions(+) create mode 100644 crypto_kem/frodokem1344aes/META.yml create mode 100644 crypto_kem/frodokem1344aes/clean/LICENSE create mode 100644 crypto_kem/frodokem1344aes/clean/Makefile create mode 100644 crypto_kem/frodokem1344aes/clean/Makefile.Microsoft_nmake create mode 100644 crypto_kem/frodokem1344aes/clean/api.h create mode 100644 crypto_kem/frodokem1344aes/clean/common.h create mode 100644 crypto_kem/frodokem1344aes/clean/kem.c create mode 100644 crypto_kem/frodokem1344aes/clean/matrix_aes.c create mode 100644 crypto_kem/frodokem1344aes/clean/noise.c create mode 100644 crypto_kem/frodokem1344aes/clean/params.h create mode 100644 crypto_kem/frodokem1344aes/clean/util.c create mode 100644 test/duplicate_consistency/frodokem1344aes_clean.yml diff --git a/crypto_kem/frodokem1344aes/META.yml b/crypto_kem/frodokem1344aes/META.yml new file mode 100644 index 00000000..6c7c470c --- /dev/null +++ b/crypto_kem/frodokem1344aes/META.yml @@ -0,0 +1,24 @@ +name: FrodoKEM-1344-AES +type: kem +claimed-nist-level: 5 +length-public-key: 21520 +length-ciphertext: 21632 +length-shared-secret: 32 +testvectors-sha256: 91dce2e12200afc88f951aff9349b72d1dda6e53e305135a891aa1a67ef88352 +principal-submitter: Douglas Stebila, University of Waterloo +auxiliary-submitters: +- Erdem Alkim +- Joppe W. Bos, NXP Semiconductors +- Léo Ducas, CWI +- Patrick Longa, Microsoft Research +- Ilya Mironov, Google +- Michael Naehrig, Microsoft Research +- Valeria Nikolaenko +- Chris Peikert, University of Michigan +- Ananth Raghunathan, Google +- Karen Easterbrook, Microsoft Research +- Brian LaMacchia, Microsoft Research +implementations: +- name: clean + version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/437e228fca580a82435cab09f30ae14b03183119 + length-secret-key: 43088 diff --git a/crypto_kem/frodokem1344aes/clean/LICENSE b/crypto_kem/frodokem1344aes/clean/LICENSE new file mode 100644 index 00000000..5cf7c8db --- /dev/null +++ b/crypto_kem/frodokem1344aes/clean/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE diff --git a/crypto_kem/frodokem1344aes/clean/Makefile b/crypto_kem/frodokem1344aes/clean/Makefile new file mode 100644 index 00000000..48102628 --- /dev/null +++ b/crypto_kem/frodokem1344aes/clean/Makefile @@ -0,0 +1,19 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libfrodokem1344aes_clean.a +HEADERS=api.h params.h common.h +OBJECTS=kem.o matrix_aes.o noise.o util.o + +CFLAGS=-Wall -Wextra -Wpedantic -Werror -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_kem/frodokem1344aes/clean/Makefile.Microsoft_nmake b/crypto_kem/frodokem1344aes/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..b6ee48e2 --- /dev/null +++ b/crypto_kem/frodokem1344aes/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libfrodokem1344aes_clean.lib +OBJECTS=kem.obj matrix_aes.obj noise.obj util.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_kem/frodokem1344aes/clean/api.h b/crypto_kem/frodokem1344aes/clean/api.h new file mode 100644 index 00000000..d7b783c2 --- /dev/null +++ b/crypto_kem/frodokem1344aes/clean/api.h @@ -0,0 +1,20 @@ +#ifndef PQCLEAN_FRODOKEM1344AES_CLEAN_API_H +#define PQCLEAN_FRODOKEM1344AES_CLEAN_API_H + +#include +#include + +#define PQCLEAN_FRODOKEM1344AES_CLEAN_CRYPTO_SECRETKEYBYTES 43088 // sizeof(s) + CRYPTO_PUBLICKEYBYTES + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH +#define PQCLEAN_FRODOKEM1344AES_CLEAN_CRYPTO_PUBLICKEYBYTES 21520 // sizeof(seed_A) + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 +#define PQCLEAN_FRODOKEM1344AES_CLEAN_CRYPTO_BYTES 32 +#define PQCLEAN_FRODOKEM1344AES_CLEAN_CRYPTO_CIPHERTEXTBYTES 21632 // (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + (PARAMS_LOGQ*PARAMS_NBAR*PARAMS_NBAR)/8 + +#define PQCLEAN_FRODOKEM1344AES_CLEAN_CRYPTO_ALGNAME "FrodoKEM-1344-AES" + +int PQCLEAN_FRODOKEM1344AES_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); + +int PQCLEAN_FRODOKEM1344AES_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); + +int PQCLEAN_FRODOKEM1344AES_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); + +#endif diff --git a/crypto_kem/frodokem1344aes/clean/common.h b/crypto_kem/frodokem1344aes/clean/common.h new file mode 100644 index 00000000..f9fa3779 --- /dev/null +++ b/crypto_kem/frodokem1344aes/clean/common.h @@ -0,0 +1,19 @@ +#ifndef COMMON_H +#define COMMON_H + +int PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); +int PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); +void PQCLEAN_FRODOKEM1344AES_CLEAN_sample_n(uint16_t *s, size_t n); +void PQCLEAN_FRODOKEM1344AES_CLEAN_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s); +void PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e); +void PQCLEAN_FRODOKEM1344AES_CLEAN_add(uint16_t *out, const uint16_t *a, const uint16_t *b); +void PQCLEAN_FRODOKEM1344AES_CLEAN_sub(uint16_t *out, const uint16_t *a, const uint16_t *b); +void PQCLEAN_FRODOKEM1344AES_CLEAN_key_encode(uint16_t *out, const uint16_t *in); +void PQCLEAN_FRODOKEM1344AES_CLEAN_key_decode(uint16_t *out, const uint16_t *in); +void PQCLEAN_FRODOKEM1344AES_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); +void PQCLEAN_FRODOKEM1344AES_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); +void PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes(uint8_t *mem, size_t n); +uint16_t PQCLEAN_FRODOKEM1344AES_CLEAN_LE_TO_UINT16(uint16_t n); +uint16_t PQCLEAN_FRODOKEM1344AES_CLEAN_UINT16_TO_LE(uint16_t n); + +#endif diff --git a/crypto_kem/frodokem1344aes/clean/kem.c b/crypto_kem/frodokem1344aes/clean/kem.c new file mode 100644 index 00000000..611ca711 --- /dev/null +++ b/crypto_kem/frodokem1344aes/clean/kem.c @@ -0,0 +1,238 @@ +/******************************************************************************************** +* FrodoKEM: Learning with Errors Key Encapsulation +* +* Abstract: Key Encapsulation Mechanism (KEM) based on Frodo +*********************************************************************************************/ + +#include +#include + +#include "fips202.h" +#include "randombytes.h" + +#include "api.h" +#include "common.h" +#include "params.h" + +int PQCLEAN_FRODOKEM1344AES_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + // FrodoKEM's key generation + // Outputs: public key pk ( BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 bytes) + // secret key sk (CRYPTO_BYTES + BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH bytes) + uint8_t *pk_seedA = &pk[0]; + uint8_t *pk_b = &pk[BYTES_SEED_A]; + uint8_t *sk_s = &sk[0]; + uint8_t *sk_pk = &sk[CRYPTO_BYTES]; + uint8_t *sk_S = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; + uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; + uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t S[2 * PARAMS_N * PARAMS_NBAR] = {0}; // contains secret data + uint16_t *E = &S[PARAMS_N * PARAMS_NBAR]; // contains secret data + uint8_t randomness[2 * CRYPTO_BYTES + BYTES_SEED_A]; // contains secret data via randomness_s and randomness_seedSE + uint8_t *randomness_s = &randomness[0]; // contains secret data + uint8_t *randomness_seedSE = &randomness[CRYPTO_BYTES]; // contains secret data + uint8_t *randomness_z = &randomness[2 * CRYPTO_BYTES]; + uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data + + // Generate the secret value s, the seed for S and E, and the seed for the seed for A. Add seed_A to the public key + randombytes(randomness, CRYPTO_BYTES + CRYPTO_BYTES + BYTES_SEED_A); + shake(pk_seedA, BYTES_SEED_A, randomness_z, BYTES_SEED_A); + + // Generate S and E, and compute B = A*S + E. Generate A on-the-fly + shake_input_seedSE[0] = 0x5F; + memcpy(&shake_input_seedSE[1], randomness_seedSE, CRYPTO_BYTES); + shake((uint8_t *)S, 2 * PARAMS_N * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); + for (size_t i = 0; i < 2 * PARAMS_N * PARAMS_NBAR; i++) { + S[i] = PQCLEAN_FRODOKEM1344AES_CLEAN_LE_TO_UINT16(S[i]); + } + PQCLEAN_FRODOKEM1344AES_CLEAN_sample_n(S, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM1344AES_CLEAN_sample_n(E, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_as_plus_e(B, S, E, pk); + + // Encode the second part of the public key + PQCLEAN_FRODOKEM1344AES_CLEAN_pack(pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, B, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); + + // Add s, pk and S to the secret key + memcpy(sk_s, randomness_s, CRYPTO_BYTES); + memcpy(sk_pk, pk, CRYPTO_PUBLICKEYBYTES); + for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { + S[i] = PQCLEAN_FRODOKEM1344AES_CLEAN_UINT16_TO_LE(S[i]); + } + memcpy(sk_S, S, 2 * PARAMS_N * PARAMS_NBAR); + + // Add H(pk) to the secret key + shake(sk_pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); + + // Cleanup: + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes((uint8_t *)E, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes(randomness, 2 * CRYPTO_BYTES); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); + return 0; +} + + +int PQCLEAN_FRODOKEM1344AES_CLEAN_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { + // FrodoKEM's key encapsulation + const uint8_t *pk_seedA = &pk[0]; + const uint8_t *pk_b = &pk[BYTES_SEED_A]; + uint8_t *ct_c1 = &ct[0]; + uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; + uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t V[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data + uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; + uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data + uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data + uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data + uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via mu + uint8_t *pkh = &G2in[0]; + uint8_t *mu = &G2in[BYTES_PKHASH]; // contains secret data + uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data + uint8_t *seedSE = &G2out[0]; // contains secret data + uint8_t *k = &G2out[CRYPTO_BYTES]; // contains secret data + uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k + uint8_t *Fin_ct = &Fin[0]; + uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data + uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data + + // pkh <- G_1(pk), generate random mu, compute (seedSE || k) = G_2(pkh || mu) + shake(pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); + randombytes(mu, BYTES_MU); + shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); + + // Generate Sp and Ep, and compute Bp = Sp*A + Ep. Generate A on-the-fly + shake_input_seedSE[0] = 0x96; + memcpy(&shake_input_seedSE[1], seedSE, CRYPTO_BYTES); + shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); + for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { + Sp[i] = PQCLEAN_FRODOKEM1344AES_CLEAN_LE_TO_UINT16(Sp[i]); + } + PQCLEAN_FRODOKEM1344AES_CLEAN_sample_n(Sp, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM1344AES_CLEAN_sample_n(Ep, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_sa_plus_e(Bp, Sp, Ep, pk_seedA); + PQCLEAN_FRODOKEM1344AES_CLEAN_pack(ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, Bp, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); + + // Generate Epp, and compute V = Sp*B + Epp + PQCLEAN_FRODOKEM1344AES_CLEAN_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); + PQCLEAN_FRODOKEM1344AES_CLEAN_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); + PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_sb_plus_e(V, B, Sp, Epp); + + // Encode mu, and compute C = V + enc(mu) (mod q) + PQCLEAN_FRODOKEM1344AES_CLEAN_key_encode(C, (uint16_t *)mu); + PQCLEAN_FRODOKEM1344AES_CLEAN_add(C, V, C); + PQCLEAN_FRODOKEM1344AES_CLEAN_pack(ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, C, PARAMS_NBAR * PARAMS_NBAR, PARAMS_LOGQ); + + // Compute ss = F(ct||KK) + memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); + memcpy(Fin_k, k, CRYPTO_BYTES); + shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); + + // Cleanup: + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes((uint8_t *)V, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes(mu, BYTES_MU); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes(G2out, 2 * CRYPTO_BYTES); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes(Fin_k, CRYPTO_BYTES); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); + return 0; +} + + +int PQCLEAN_FRODOKEM1344AES_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { + // FrodoKEM's key decapsulation + uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t W[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data + uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; + uint16_t CC[PARAMS_NBAR * PARAMS_NBAR] = {0}; + uint16_t BBp[PARAMS_N * PARAMS_NBAR] = {0}; + uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data + uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data + uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data + const uint8_t *ct_c1 = &ct[0]; + const uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; + const uint8_t *sk_s = &sk[0]; + const uint8_t *sk_pk = &sk[CRYPTO_BYTES]; + const uint16_t *sk_S = (uint16_t *) &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; + uint16_t S[PARAMS_N * PARAMS_NBAR]; // contains secret data + const uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; + const uint8_t *pk_seedA = &sk_pk[0]; + const uint8_t *pk_b = &sk_pk[BYTES_SEED_A]; + uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via muprime + uint8_t *pkh = &G2in[0]; + uint8_t *muprime = &G2in[BYTES_PKHASH]; // contains secret data + uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data + uint8_t *seedSEprime = &G2out[0]; // contains secret data + uint8_t *kprime = &G2out[CRYPTO_BYTES]; // contains secret data + uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k + uint8_t *Fin_ct = &Fin[0]; + uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data + uint8_t shake_input_seedSEprime[1 + CRYPTO_BYTES]; // contains secret data + + for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { + S[i] = PQCLEAN_FRODOKEM1344AES_CLEAN_LE_TO_UINT16(sk_S[i]); + } + + // Compute W = C - Bp*S (mod q), and decode the randomness mu + PQCLEAN_FRODOKEM1344AES_CLEAN_unpack(Bp, PARAMS_N * PARAMS_NBAR, ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, PARAMS_LOGQ); + PQCLEAN_FRODOKEM1344AES_CLEAN_unpack(C, PARAMS_NBAR * PARAMS_NBAR, ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, PARAMS_LOGQ); + PQCLEAN_FRODOKEM1344AES_CLEAN_mul_bs(W, Bp, S); + PQCLEAN_FRODOKEM1344AES_CLEAN_sub(W, C, W); + PQCLEAN_FRODOKEM1344AES_CLEAN_key_decode((uint16_t *)muprime, W); + + // Generate (seedSE' || k') = G_2(pkh || mu') + memcpy(pkh, sk_pkh, BYTES_PKHASH); + shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); + + // Generate Sp and Ep, and compute BBp = Sp*A + Ep. Generate A on-the-fly + shake_input_seedSEprime[0] = 0x96; + memcpy(&shake_input_seedSEprime[1], seedSEprime, CRYPTO_BYTES); + shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSEprime, 1 + CRYPTO_BYTES); + for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { + Sp[i] = PQCLEAN_FRODOKEM1344AES_CLEAN_LE_TO_UINT16(Sp[i]); + } + PQCLEAN_FRODOKEM1344AES_CLEAN_sample_n(Sp, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM1344AES_CLEAN_sample_n(Ep, PARAMS_N * PARAMS_NBAR); + PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_sa_plus_e(BBp, Sp, Ep, pk_seedA); + + // Generate Epp, and compute W = Sp*B + Epp + PQCLEAN_FRODOKEM1344AES_CLEAN_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); + PQCLEAN_FRODOKEM1344AES_CLEAN_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); + PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_sb_plus_e(W, B, Sp, Epp); + + // Encode mu, and compute CC = W + enc(mu') (mod q) + PQCLEAN_FRODOKEM1344AES_CLEAN_key_encode(CC, (uint16_t *)muprime); + PQCLEAN_FRODOKEM1344AES_CLEAN_add(CC, W, CC); + + // Prepare input to F + memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); + + // Reducing BBp modulo q + for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { + BBp[i] = BBp[i] & ((1 << PARAMS_LOGQ) - 1); + } + + // Is (Bp == BBp & C == CC) = true + if (memcmp(Bp, BBp, 2 * PARAMS_N * PARAMS_NBAR) == 0 && memcmp(C, CC, 2 * PARAMS_NBAR * PARAMS_NBAR) == 0) { + // Load k' to do ss = F(ct || k') + memcpy(Fin_k, kprime, CRYPTO_BYTES); + } else { + // Load s to do ss = F(ct || s) + memcpy(Fin_k, sk_s, CRYPTO_BYTES); + } + shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); + + // Cleanup: + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes((uint8_t *)W, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes(muprime, BYTES_MU); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes(G2out, 2 * CRYPTO_BYTES); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes(Fin_k, CRYPTO_BYTES); + PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes(shake_input_seedSEprime, 1 + CRYPTO_BYTES); + return 0; +} diff --git a/crypto_kem/frodokem1344aes/clean/matrix_aes.c b/crypto_kem/frodokem1344aes/clean/matrix_aes.c new file mode 100644 index 00000000..cec9a2a6 --- /dev/null +++ b/crypto_kem/frodokem1344aes/clean/matrix_aes.c @@ -0,0 +1,85 @@ +/******************************************************************************************** +* FrodoKEM: Learning with Errors Key Encapsulation +* +* Abstract: matrix arithmetic functions used by the KEM +*********************************************************************************************/ + +#include +#include + +#include "aes.h" + +#include "api.h" +#include "common.h" +#include "params.h" + +int PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { + // Generate-and-multiply: generate matrix A (N x N) row-wise, multiply by s on the right. + // Inputs: s, e (N x N_BAR) + // Output: out = A*s + e (N x N_BAR) + int i, j, k; + int16_t A[PARAMS_N * PARAMS_N] = {0}; + aes128ctx ctx128; + + aes128_keyexp(&ctx128, seed_A); + for (i = 0; i < PARAMS_N; i++) { + for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { + A[i*PARAMS_N + j] = i; // Loading values in the little-endian order + A[i*PARAMS_N + j + 1] = j; + } + } + aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); + + for (i = 0; i < PARAMS_N * PARAMS_N; i++) { + A[i] = PQCLEAN_FRODOKEM1344AES_CLEAN_LE_TO_UINT16(A[i]); + } + memcpy(out, e, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t)); + + for (i = 0; i < PARAMS_N; i++) { // Matrix multiplication-addition A*s + e + for (k = 0; k < PARAMS_NBAR; k++) { + uint16_t sum = 0; + for (j = 0; j < PARAMS_N; j++) { + sum += A[i * PARAMS_N + j] * s[k * PARAMS_N + j]; + } + out[i * PARAMS_NBAR + k] += sum; // Adding e. No need to reduce modulo 2^15, extra bits are taken care of during packing later on. + } + } + + return 1; +} + + +int PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { + // Generate-and-multiply: generate matrix A (N x N) column-wise, multiply by s' on the left. + // Inputs: s', e' (N_BAR x N) + // Output: out = s'*A + e' (N_BAR x N) + int i, j, k; + int16_t A[PARAMS_N * PARAMS_N] = {0}; + aes128ctx ctx128; + + aes128_keyexp(&ctx128, seed_A); + for (i = 0; i < PARAMS_N; i++) { + for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { + A[i*PARAMS_N + j] = i; // Loading values in the little-endian order + A[i*PARAMS_N + j + 1] = j; + } + } + aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); + + for (i = 0; i < PARAMS_N * PARAMS_N; i++) { + A[i] = PQCLEAN_FRODOKEM1344AES_CLEAN_LE_TO_UINT16(A[i]); + } + memcpy(out, e, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t)); + + for (i = 0; i < PARAMS_N; i++) { // Matrix multiplication-addition A*s + e + for (k = 0; k < PARAMS_NBAR; k++) { + uint16_t sum = 0; + for (j = 0; j < PARAMS_N; j++) { + sum += A[j * PARAMS_N + i] * s[k * PARAMS_N + j]; + } + out[k * PARAMS_N + i] += sum; // Adding e. No need to reduce modulo 2^15, extra bits are taken care of during packing later on. + } + } + + return 1; +} diff --git a/crypto_kem/frodokem1344aes/clean/noise.c b/crypto_kem/frodokem1344aes/clean/noise.c new file mode 100644 index 00000000..3150caef --- /dev/null +++ b/crypto_kem/frodokem1344aes/clean/noise.c @@ -0,0 +1,33 @@ +/******************************************************************************************** +* FrodoKEM: Learning with Errors Key Encapsulation +* +* Abstract: noise sampling functions +*********************************************************************************************/ + +#include + +#include "api.h" +#include "params.h" + +static uint16_t CDF_TABLE[CDF_TABLE_LEN] = CDF_TABLE_DATA; + +void PQCLEAN_FRODOKEM1344AES_CLEAN_sample_n(uint16_t *s, const size_t n) { + // Fills vector s with n samples from the noise distribution which requires 16 bits to sample. + // The distribution is specified by its CDF. + // Input: pseudo-random values (2*n bytes) passed in s. The input is overwritten by the output. + unsigned int i, j; + + for (i = 0; i < n; ++i) { + uint8_t sample = 0; + uint16_t prnd = s[i] >> 1; // Drop the least significant bit + uint8_t sign = s[i] & 0x1; // Pick the least significant bit + + // No need to compare with the last value. + for (j = 0; j < (unsigned int)(CDF_TABLE_LEN - 1); j++) { + // Constant time comparison: 1 if CDF_TABLE[j] < s, 0 otherwise. Uses the fact that CDF_TABLE[j] and s fit in 15 bits. + sample += (uint16_t)(CDF_TABLE[j] - prnd) >> 15; + } + // Assuming that sign is either 0 or 1, flips sample iff sign = 1 + s[i] = ((-sign) ^ sample) + sign; + } +} diff --git a/crypto_kem/frodokem1344aes/clean/params.h b/crypto_kem/frodokem1344aes/clean/params.h new file mode 100644 index 00000000..1bda8a84 --- /dev/null +++ b/crypto_kem/frodokem1344aes/clean/params.h @@ -0,0 +1,27 @@ +#ifndef PARAMS_H +#define PARAMS_H + +#define CRYPTO_SECRETKEYBYTES PQCLEAN_FRODOKEM1344AES_CLEAN_CRYPTO_SECRETKEYBYTES +#define CRYPTO_PUBLICKEYBYTES PQCLEAN_FRODOKEM1344AES_CLEAN_CRYPTO_PUBLICKEYBYTES +#define CRYPTO_BYTES PQCLEAN_FRODOKEM1344AES_CLEAN_CRYPTO_BYTES +#define CRYPTO_CIPHERTEXTBYTES PQCLEAN_FRODOKEM1344AES_CLEAN_CRYPTO_CIPHERTEXTBYTES + +#define PARAMS_N 1344 +#define PARAMS_NBAR 8 +#define PARAMS_LOGQ 16 +#define PARAMS_Q (1 << PARAMS_LOGQ) +#define PARAMS_EXTRACTED_BITS 4 +#define PARAMS_STRIPE_STEP 8 +#define PARAMS_PARALLEL 4 +#define BYTES_SEED_A 16 +#define BYTES_MU ((PARAMS_EXTRACTED_BITS * PARAMS_NBAR * PARAMS_NBAR) / 8) +#define BYTES_PKHASH CRYPTO_BYTES + +// Selecting SHAKE XOF function for the KEM and noise sampling +#define shake shake256 + +// CDF table +#define CDF_TABLE_DATA {9142, 23462, 30338, 32361, 32725, 32765, 32767} +#define CDF_TABLE_LEN 7 + +#endif diff --git a/crypto_kem/frodokem1344aes/clean/util.c b/crypto_kem/frodokem1344aes/clean/util.c new file mode 100644 index 00000000..e900a858 --- /dev/null +++ b/crypto_kem/frodokem1344aes/clean/util.c @@ -0,0 +1,234 @@ +/******************************************************************************************** +* FrodoKEM: Learning with Errors Key Encapsulation +* +* Abstract: additional functions for FrodoKEM +*********************************************************************************************/ + +#include +#include + +#include "api.h" +#include "params.h" + +#define min(x, y) (((x) < (y)) ? (x) : (y)) + +uint16_t PQCLEAN_FRODOKEM1344AES_CLEAN_LE_TO_UINT16(const uint16_t n) { + return (((uint8_t *) &(n))[0] | (((uint8_t *) &(n))[1] << 8)); +} + +uint16_t PQCLEAN_FRODOKEM1344AES_CLEAN_UINT16_TO_LE(const uint16_t n) { + uint16_t y; + uint8_t *z = (uint8_t *) &y; + z[0] = n & 0xFF; + z[1] = (n & 0xFF00) >> 8; + return y; +} + +void PQCLEAN_FRODOKEM1344AES_CLEAN_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s) { + // Multiply by s on the right + // Inputs: b (N_BAR x N), s (N x N_BAR) + // Output: out = b*s (N_BAR x N_BAR) + int i, j, k; + + for (i = 0; i < PARAMS_NBAR; i++) { + for (j = 0; j < PARAMS_NBAR; j++) { + out[i * PARAMS_NBAR + j] = 0; + for (k = 0; k < PARAMS_N; k++) { + out[i * PARAMS_NBAR + j] += b[i * PARAMS_N + k] * s[j * PARAMS_N + k]; + } + out[i * PARAMS_NBAR + j] = (uint32_t)(out[i * PARAMS_NBAR + j]) & ((1 << PARAMS_LOGQ) - 1); + } + } +} + + +void PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e) { + // Multiply by s on the left + // Inputs: b (N x N_BAR), s (N_BAR x N), e (N_BAR x N_BAR) + // Output: out = s*b + e (N_BAR x N_BAR) + int i, j, k; + + for (k = 0; k < PARAMS_NBAR; k++) { + for (i = 0; i < PARAMS_NBAR; i++) { + out[k * PARAMS_NBAR + i] = e[k * PARAMS_NBAR + i]; + for (j = 0; j < PARAMS_N; j++) { + out[k * PARAMS_NBAR + i] += s[k * PARAMS_N + j] * b[j * PARAMS_NBAR + i]; + } + out[k * PARAMS_NBAR + i] = (uint32_t)(out[k * PARAMS_NBAR + i]) & ((1 << PARAMS_LOGQ) - 1); + } + } +} + + +void PQCLEAN_FRODOKEM1344AES_CLEAN_add(uint16_t *out, const uint16_t *a, const uint16_t *b) { + // Add a and b + // Inputs: a, b (N_BAR x N_BAR) + // Output: c = a + b + + for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { + out[i] = (a[i] + b[i]) & ((1 << PARAMS_LOGQ) - 1); + } +} + + +void PQCLEAN_FRODOKEM1344AES_CLEAN_sub(uint16_t *out, const uint16_t *a, const uint16_t *b) { + // Subtract a and b + // Inputs: a, b (N_BAR x N_BAR) + // Output: c = a - b + + for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { + out[i] = (a[i] - b[i]) & ((1 << PARAMS_LOGQ) - 1); + } +} + + +void PQCLEAN_FRODOKEM1344AES_CLEAN_key_encode(uint16_t *out, const uint16_t *in) { + // Encoding + unsigned int i, j, npieces_word = 8; + unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; + uint64_t temp, mask = ((uint64_t)1 << PARAMS_EXTRACTED_BITS) - 1; + uint16_t *pos = out; + + for (i = 0; i < nwords; i++) { + temp = 0; + for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { + temp |= ((uint64_t)((uint8_t *)in)[i * PARAMS_EXTRACTED_BITS + j]) << (8 * j); + } + for (j = 0; j < npieces_word; j++) { + *pos = (uint16_t)((temp & mask) << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS)); + temp >>= PARAMS_EXTRACTED_BITS; + pos++; + } + } +} + + +void PQCLEAN_FRODOKEM1344AES_CLEAN_key_decode(uint16_t *out, const uint16_t *in) { + // Decoding + unsigned int i, j, index = 0, npieces_word = 8; + unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; + uint16_t temp, maskex = ((uint16_t)1 << PARAMS_EXTRACTED_BITS) - 1, maskq = ((uint16_t)1 << PARAMS_LOGQ) - 1; + uint8_t *pos = (uint8_t *)out; + uint64_t templong; + + for (i = 0; i < nwords; i++) { + templong = 0; + for (j = 0; j < npieces_word; j++) { // temp = floor(in*2^{-11}+0.5) + temp = ((in[index] & maskq) + (1 << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS - 1))) >> (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS); + templong |= ((uint64_t)(temp & maskex)) << (PARAMS_EXTRACTED_BITS * j); + index++; + } + for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { + pos[i * PARAMS_EXTRACTED_BITS + j] = (templong >> (8 * j)) & 0xFF; + } + } +} + + +void PQCLEAN_FRODOKEM1344AES_CLEAN_pack(uint8_t *out, const size_t outlen, const uint16_t *in, const size_t inlen, const uint8_t lsb) { + // Pack the input uint16 vector into a char output vector, copying lsb bits from each input element. + // If inlen * lsb / 8 > outlen, only outlen * 8 bits are copied. + memset(out, 0, outlen); + + size_t i = 0; // whole bytes already filled in + size_t j = 0; // whole uint16_t already copied + uint16_t w = 0; // the leftover, not yet copied + uint8_t bits = 0; // the number of lsb in w + + while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { + /* + in: | | |********|********| + ^ + j + w : | ****| + ^ + bits + out:|**|**|**|**|**|**|**|**|* | + ^^ + ib + */ + uint8_t b = 0; // bits in out[i] already filled in + while (b < 8) { + int nbits = min(8 - b, bits); + uint16_t mask = (1 << nbits) - 1; + uint8_t t = (uint8_t) ((w >> (bits - nbits)) & mask); // the bits to copy from w to out + out[i] = out[i] + (t << (8 - b - nbits)); + b += (uint8_t) nbits; + bits -= (uint8_t) nbits; + w &= ~(mask << bits); // not strictly necessary; mostly for debugging + + if (bits == 0) { + if (j < inlen) { + w = in[j]; + bits = lsb; + j++; + } else { + break; // the input vector is exhausted + } + } + } + if (b == 8) { // out[i] is filled in + i++; + } + } +} + + +void PQCLEAN_FRODOKEM1344AES_CLEAN_unpack(uint16_t *out, const size_t outlen, const uint8_t *in, const size_t inlen, const uint8_t lsb) { + // Unpack the input char vector into a uint16_t output vector, copying lsb bits + // for each output element from input. outlen must be at least ceil(inlen * 8 / lsb). + memset(out, 0, outlen * sizeof(uint16_t)); + + size_t i = 0; // whole uint16_t already filled in + size_t j = 0; // whole bytes already copied + uint8_t w = 0; // the leftover, not yet copied + uint8_t bits = 0; // the number of lsb bits of w + + while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { + /* + in: | | | | | | |**|**|... + ^ + j + w : | *| + ^ + bits + out:| *****| *****| *** | |... + ^ ^ + i b + */ + uint8_t b = 0; // bits in out[i] already filled in + while (b < lsb) { + int nbits = min(lsb - b, bits); + uint16_t mask = (1 << nbits) - 1; + uint8_t t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out + out[i] = out[i] + (t << (lsb - b - nbits)); + b += (uint8_t) nbits; + bits -= (uint8_t) nbits; + w &= ~(mask << bits); // not strictly necessary; mostly for debugging + + if (bits == 0) { + if (j < inlen) { + w = in[j]; + bits = 8; + j++; + } else { + break; // the input vector is exhausted + } + } + } + if (b == lsb) { // out[i] is filled in + i++; + } + } +} + + +void PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes(uint8_t *mem, size_t n) { + // Clear 8-bit bytes from memory. "n" indicates the number of bytes to be zeroed. + // This function uses the volatile type qualifier to inform the compiler not to optimize out the memory clearing. + volatile uint8_t *v = mem; + + for (size_t i = 0; i < n; i++) { + v[i] = 0; + } +} diff --git a/test/duplicate_consistency/frodokem1344aes_clean.yml b/test/duplicate_consistency/frodokem1344aes_clean.yml new file mode 100644 index 00000000..6ca6a58a --- /dev/null +++ b/test/duplicate_consistency/frodokem1344aes_clean.yml @@ -0,0 +1,15 @@ +consistency_checks: +- source: + scheme: frodokem640aes + implementation: clean + files: + - common.h + - kem.c + - matrix_aes.c + - noise.c + - util.c +- source: + scheme: frodokem1344shake + implementation: clean + files: + - params.h From 7e8bd90d8cc2edc990121d2066eda3ffc6dd8f79 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Wed, 10 Apr 2019 12:21:53 -0400 Subject: [PATCH 06/10] Fix Windows compiler warnings and endianness --- crypto_kem/frodokem1344aes/clean/matrix_aes.c | 16 ++++++++++++---- crypto_kem/frodokem640aes/clean/matrix_aes.c | 16 ++++++++++++---- crypto_kem/frodokem976aes/clean/matrix_aes.c | 16 ++++++++++++---- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/crypto_kem/frodokem1344aes/clean/matrix_aes.c b/crypto_kem/frodokem1344aes/clean/matrix_aes.c index cec9a2a6..5647f3c1 100644 --- a/crypto_kem/frodokem1344aes/clean/matrix_aes.c +++ b/crypto_kem/frodokem1344aes/clean/matrix_aes.c @@ -24,10 +24,14 @@ int PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_ aes128_keyexp(&ctx128, seed_A); for (i = 0; i < PARAMS_N; i++) { for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { - A[i*PARAMS_N + j] = i; // Loading values in the little-endian order - A[i*PARAMS_N + j + 1] = j; + A[i*PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order + A[i*PARAMS_N + j + 1] = (int16_t) j; } } + for (i = 0; i < PARAMS_N * PARAMS_N; i++) { + A[i] = PQCLEAN_FRODOKEM1344AES_CLEAN_UINT16_TO_LE(A[i]); + } + aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); for (i = 0; i < PARAMS_N * PARAMS_N; i++) { @@ -60,10 +64,14 @@ int PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_ aes128_keyexp(&ctx128, seed_A); for (i = 0; i < PARAMS_N; i++) { for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { - A[i*PARAMS_N + j] = i; // Loading values in the little-endian order - A[i*PARAMS_N + j + 1] = j; + A[i*PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order + A[i*PARAMS_N + j + 1] = (int16_t) j; } } + for (i = 0; i < PARAMS_N * PARAMS_N; i++) { + A[i] = PQCLEAN_FRODOKEM1344AES_CLEAN_UINT16_TO_LE(A[i]); + } + aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); for (i = 0; i < PARAMS_N * PARAMS_N; i++) { diff --git a/crypto_kem/frodokem640aes/clean/matrix_aes.c b/crypto_kem/frodokem640aes/clean/matrix_aes.c index 2a1aef2d..f3331e11 100644 --- a/crypto_kem/frodokem640aes/clean/matrix_aes.c +++ b/crypto_kem/frodokem640aes/clean/matrix_aes.c @@ -24,10 +24,14 @@ int PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t aes128_keyexp(&ctx128, seed_A); for (i = 0; i < PARAMS_N; i++) { for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { - A[i*PARAMS_N + j] = i; // Loading values in the little-endian order - A[i*PARAMS_N + j + 1] = j; + A[i*PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order + A[i*PARAMS_N + j + 1] = (int16_t) j; } } + for (i = 0; i < PARAMS_N * PARAMS_N; i++) { + A[i] = PQCLEAN_FRODOKEM640AES_CLEAN_UINT16_TO_LE(A[i]); + } + aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); for (i = 0; i < PARAMS_N * PARAMS_N; i++) { @@ -60,10 +64,14 @@ int PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t aes128_keyexp(&ctx128, seed_A); for (i = 0; i < PARAMS_N; i++) { for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { - A[i*PARAMS_N + j] = i; // Loading values in the little-endian order - A[i*PARAMS_N + j + 1] = j; + A[i*PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order + A[i*PARAMS_N + j + 1] = (int16_t) j; } } + for (i = 0; i < PARAMS_N * PARAMS_N; i++) { + A[i] = PQCLEAN_FRODOKEM640AES_CLEAN_UINT16_TO_LE(A[i]); + } + aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); for (i = 0; i < PARAMS_N * PARAMS_N; i++) { diff --git a/crypto_kem/frodokem976aes/clean/matrix_aes.c b/crypto_kem/frodokem976aes/clean/matrix_aes.c index 1d660437..19236b46 100644 --- a/crypto_kem/frodokem976aes/clean/matrix_aes.c +++ b/crypto_kem/frodokem976aes/clean/matrix_aes.c @@ -24,10 +24,14 @@ int PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t aes128_keyexp(&ctx128, seed_A); for (i = 0; i < PARAMS_N; i++) { for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { - A[i*PARAMS_N + j] = i; // Loading values in the little-endian order - A[i*PARAMS_N + j + 1] = j; + A[i*PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order + A[i*PARAMS_N + j + 1] = (int16_t) j; } } + for (i = 0; i < PARAMS_N * PARAMS_N; i++) { + A[i] = PQCLEAN_FRODOKEM976AES_CLEAN_UINT16_TO_LE(A[i]); + } + aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); for (i = 0; i < PARAMS_N * PARAMS_N; i++) { @@ -60,10 +64,14 @@ int PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t aes128_keyexp(&ctx128, seed_A); for (i = 0; i < PARAMS_N; i++) { for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { - A[i*PARAMS_N + j] = i; // Loading values in the little-endian order - A[i*PARAMS_N + j + 1] = j; + A[i*PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order + A[i*PARAMS_N + j + 1] = (int16_t) j; } } + for (i = 0; i < PARAMS_N * PARAMS_N; i++) { + A[i] = PQCLEAN_FRODOKEM976AES_CLEAN_UINT16_TO_LE(A[i]); + } + aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); for (i = 0; i < PARAMS_N * PARAMS_N; i++) { From c40276dd4a528141f3cecfe585d20042ed370f5c Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Wed, 10 Apr 2019 12:30:02 -0400 Subject: [PATCH 07/10] Link against AES object on Windows --- test/Makefile.Microsoft_nmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Makefile.Microsoft_nmake b/test/Makefile.Microsoft_nmake index f6f6dd46..85378202 100644 --- a/test/Makefile.Microsoft_nmake +++ b/test/Makefile.Microsoft_nmake @@ -12,8 +12,8 @@ IMPLEMENTATION_UPPERCASE=CLEAN SCHEME_DIR=..\crypto_$(TYPE)\$(SCHEME)\$(IMPLEMENTATION) COMMON_DIR=..\common -COMMON_OBJECTS=$(COMMON_DIR)\fips202.obj $(COMMON_DIR)\sha2.obj -COMMON_OBJECTS_NOPATH=fips202.obj sha2.obj +COMMON_OBJECTS=$(COMMON_DIR)\aes.obj $(COMMON_DIR)\fips202.obj $(COMMON_DIR)\sha2.obj +COMMON_OBJECTS_NOPATH=aes.obj fips202.obj sha2.obj DEST_DIR=..\bin From 8b4f656376d38d0c30bb4f6aeee5742454aa144b Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Wed, 10 Apr 2019 14:14:49 -0400 Subject: [PATCH 08/10] Skip too-slow valgrind tests on i386 CircleCI builds --- test/test_valgrind.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_valgrind.py b/test/test_valgrind.py index 219791cd..1fa8b601 100644 --- a/test/test_valgrind.py +++ b/test/test_valgrind.py @@ -20,6 +20,8 @@ def check_valgrind(implementation: pqclean.Implementation): if (platform.machine() not in ('i386', 'x86_64') or platform.system() != 'Linux'): raise unittest.SkipTest() + if os.environ.get('CIRCLECI') == 'true' and os.environ.get('ARCH') == 'i386' and (implementation.scheme.name == 'frodokem1344aes' or implementation.scheme.name == 'frodokem1344shake'): + raise unittest.SkipTest('Skipping too-slow valgrind test') helpers.make(TYPE=implementation.scheme.type, SCHEME=implementation.scheme.name, From 6ca367e2e629d6b58550c13c60b33d5f511cc85e Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Thu, 11 Apr 2019 08:18:49 -0400 Subject: [PATCH 09/10] Apply astyle --- crypto_kem/frodokem1344aes/clean/matrix_aes.c | 8 ++++---- crypto_kem/frodokem1344aes/clean/util.c | 2 +- crypto_kem/frodokem640aes/clean/matrix_aes.c | 8 ++++---- crypto_kem/frodokem640aes/clean/util.c | 2 +- crypto_kem/frodokem976aes/clean/matrix_aes.c | 8 ++++---- crypto_kem/frodokem976aes/clean/util.c | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/crypto_kem/frodokem1344aes/clean/matrix_aes.c b/crypto_kem/frodokem1344aes/clean/matrix_aes.c index 5647f3c1..645901b0 100644 --- a/crypto_kem/frodokem1344aes/clean/matrix_aes.c +++ b/crypto_kem/frodokem1344aes/clean/matrix_aes.c @@ -24,8 +24,8 @@ int PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_ aes128_keyexp(&ctx128, seed_A); for (i = 0; i < PARAMS_N; i++) { for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { - A[i*PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order - A[i*PARAMS_N + j + 1] = (int16_t) j; + A[i * PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order + A[i * PARAMS_N + j + 1] = (int16_t) j; } } for (i = 0; i < PARAMS_N * PARAMS_N; i++) { @@ -64,8 +64,8 @@ int PQCLEAN_FRODOKEM1344AES_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_ aes128_keyexp(&ctx128, seed_A); for (i = 0; i < PARAMS_N; i++) { for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { - A[i*PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order - A[i*PARAMS_N + j + 1] = (int16_t) j; + A[i * PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order + A[i * PARAMS_N + j + 1] = (int16_t) j; } } for (i = 0; i < PARAMS_N * PARAMS_N; i++) { diff --git a/crypto_kem/frodokem1344aes/clean/util.c b/crypto_kem/frodokem1344aes/clean/util.c index e900a858..e66409c7 100644 --- a/crypto_kem/frodokem1344aes/clean/util.c +++ b/crypto_kem/frodokem1344aes/clean/util.c @@ -13,7 +13,7 @@ #define min(x, y) (((x) < (y)) ? (x) : (y)) uint16_t PQCLEAN_FRODOKEM1344AES_CLEAN_LE_TO_UINT16(const uint16_t n) { - return (((uint8_t *) &(n))[0] | (((uint8_t *) &(n))[1] << 8)); + return (((uint8_t *) &n)[0] | (((uint8_t *) &n)[1] << 8)); } uint16_t PQCLEAN_FRODOKEM1344AES_CLEAN_UINT16_TO_LE(const uint16_t n) { diff --git a/crypto_kem/frodokem640aes/clean/matrix_aes.c b/crypto_kem/frodokem640aes/clean/matrix_aes.c index f3331e11..65344e3d 100644 --- a/crypto_kem/frodokem640aes/clean/matrix_aes.c +++ b/crypto_kem/frodokem640aes/clean/matrix_aes.c @@ -24,8 +24,8 @@ int PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t aes128_keyexp(&ctx128, seed_A); for (i = 0; i < PARAMS_N; i++) { for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { - A[i*PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order - A[i*PARAMS_N + j + 1] = (int16_t) j; + A[i * PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order + A[i * PARAMS_N + j + 1] = (int16_t) j; } } for (i = 0; i < PARAMS_N * PARAMS_N; i++) { @@ -64,8 +64,8 @@ int PQCLEAN_FRODOKEM640AES_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t aes128_keyexp(&ctx128, seed_A); for (i = 0; i < PARAMS_N; i++) { for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { - A[i*PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order - A[i*PARAMS_N + j + 1] = (int16_t) j; + A[i * PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order + A[i * PARAMS_N + j + 1] = (int16_t) j; } } for (i = 0; i < PARAMS_N * PARAMS_N; i++) { diff --git a/crypto_kem/frodokem640aes/clean/util.c b/crypto_kem/frodokem640aes/clean/util.c index 4fec5624..d7567777 100644 --- a/crypto_kem/frodokem640aes/clean/util.c +++ b/crypto_kem/frodokem640aes/clean/util.c @@ -13,7 +13,7 @@ #define min(x, y) (((x) < (y)) ? (x) : (y)) uint16_t PQCLEAN_FRODOKEM640AES_CLEAN_LE_TO_UINT16(const uint16_t n) { - return (((uint8_t *) &(n))[0] | (((uint8_t *) &(n))[1] << 8)); + return (((uint8_t *) &n)[0] | (((uint8_t *) &n)[1] << 8)); } uint16_t PQCLEAN_FRODOKEM640AES_CLEAN_UINT16_TO_LE(const uint16_t n) { diff --git a/crypto_kem/frodokem976aes/clean/matrix_aes.c b/crypto_kem/frodokem976aes/clean/matrix_aes.c index 19236b46..2596fc25 100644 --- a/crypto_kem/frodokem976aes/clean/matrix_aes.c +++ b/crypto_kem/frodokem976aes/clean/matrix_aes.c @@ -24,8 +24,8 @@ int PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_as_plus_e(uint16_t *out, const uint16_t aes128_keyexp(&ctx128, seed_A); for (i = 0; i < PARAMS_N; i++) { for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { - A[i*PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order - A[i*PARAMS_N + j + 1] = (int16_t) j; + A[i * PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order + A[i * PARAMS_N + j + 1] = (int16_t) j; } } for (i = 0; i < PARAMS_N * PARAMS_N; i++) { @@ -64,8 +64,8 @@ int PQCLEAN_FRODOKEM976AES_CLEAN_mul_add_sa_plus_e(uint16_t *out, const uint16_t aes128_keyexp(&ctx128, seed_A); for (i = 0; i < PARAMS_N; i++) { for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { - A[i*PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order - A[i*PARAMS_N + j + 1] = (int16_t) j; + A[i * PARAMS_N + j] = (int16_t) i; // Loading values in the little-endian order + A[i * PARAMS_N + j + 1] = (int16_t) j; } } for (i = 0; i < PARAMS_N * PARAMS_N; i++) { diff --git a/crypto_kem/frodokem976aes/clean/util.c b/crypto_kem/frodokem976aes/clean/util.c index 39516957..7c797e60 100644 --- a/crypto_kem/frodokem976aes/clean/util.c +++ b/crypto_kem/frodokem976aes/clean/util.c @@ -13,7 +13,7 @@ #define min(x, y) (((x) < (y)) ? (x) : (y)) uint16_t PQCLEAN_FRODOKEM976AES_CLEAN_LE_TO_UINT16(const uint16_t n) { - return (((uint8_t *) &(n))[0] | (((uint8_t *) &(n))[1] << 8)); + return (((uint8_t *) &n)[0] | (((uint8_t *) &n)[1] << 8)); } uint16_t PQCLEAN_FRODOKEM976AES_CLEAN_UINT16_TO_LE(const uint16_t n) { From 1e7ee7a4c22ad15e18d87ec5a2dc1bb48919d387 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Thu, 11 Apr 2019 09:23:35 -0400 Subject: [PATCH 10/10] skip_windows decorator --- test/test_duplicate_consistency.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_duplicate_consistency.py b/test/test_duplicate_consistency.py index a8f344d6..306d2bed 100644 --- a/test/test_duplicate_consistency.py +++ b/test/test_duplicate_consistency.py @@ -8,8 +8,8 @@ import helpers import unittest import yaml +helpers.skip_windows() def test_duplicate_consistency(): - helpers.skip_windows() for scheme in pqclean.Scheme.all_schemes(): for implementation in scheme.implementations: if helpers.permit_test('duplicate_consistency', implementation):