From 75416c93f0856f120942e39093d97bbf60e9932c Mon Sep 17 00:00:00 2001 From: Thom Wiggers Date: Fri, 29 May 2020 10:01:44 +0200 Subject: [PATCH 1/7] Kyber768 and Kyber1024 don't need -maes (see #296) --- crypto_kem/kyber1024/META.yml | 1 - crypto_kem/kyber768/META.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/crypto_kem/kyber1024/META.yml b/crypto_kem/kyber1024/META.yml index ac54719d..c50507eb 100644 --- a/crypto_kem/kyber1024/META.yml +++ b/crypto_kem/kyber1024/META.yml @@ -29,7 +29,6 @@ implementations: operating_systems: - Linux required_flags: - - aes - avx2 - bmi2 - popcnt diff --git a/crypto_kem/kyber768/META.yml b/crypto_kem/kyber768/META.yml index d98a80ed..73056cd7 100644 --- a/crypto_kem/kyber768/META.yml +++ b/crypto_kem/kyber768/META.yml @@ -29,7 +29,6 @@ implementations: operating_systems: - Linux required_flags: - - aes - avx2 - bmi2 - popcnt From 4883f2ce893128a7b2dbb0b6d546389271cd13b4 Mon Sep 17 00:00:00 2001 From: Ko- Date: Fri, 29 May 2020 16:41:41 +0200 Subject: [PATCH 2/7] Add domain separation to NewHope NewHope announced a new version of their specification that adds explicit domain separation. This is a port of https://github.com/newhopecrypto/newhope/commit/607a9d3 --- crypto_kem/newhope1024cca/clean/cpapke.c | 5 +++-- crypto_kem/newhope1024cca/clean/kem.c | 25 +++++++++++++----------- crypto_kem/newhope1024cpa/clean/cpapke.c | 5 +++-- crypto_kem/newhope1024cpa/clean/kem.c | 5 +++-- crypto_kem/newhope512cca/clean/cpapke.c | 5 +++-- crypto_kem/newhope512cca/clean/kem.c | 25 +++++++++++++----------- crypto_kem/newhope512cpa/clean/cpapke.c | 5 +++-- crypto_kem/newhope512cpa/clean/kem.c | 5 +++-- 8 files changed, 46 insertions(+), 34 deletions(-) diff --git a/crypto_kem/newhope1024cca/clean/cpapke.c b/crypto_kem/newhope1024cca/clean/cpapke.c index 09986223..11644c74 100644 --- a/crypto_kem/newhope1024cca/clean/cpapke.c +++ b/crypto_kem/newhope1024cca/clean/cpapke.c @@ -101,8 +101,9 @@ void PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_keypair(unsigned char *pk, unsigned char *publicseed = z; unsigned char *noiseseed = z + NEWHOPE_SYMBYTES; - randombytes(z, NEWHOPE_SYMBYTES); - shake256(z, 2 * NEWHOPE_SYMBYTES, z, NEWHOPE_SYMBYTES); + z[0] = 0x01; + randombytes(z + 1, NEWHOPE_SYMBYTES); + shake256(z, 2 * NEWHOPE_SYMBYTES, z, NEWHOPE_SYMBYTES + 1); gen_a(&ahat, publicseed); diff --git a/crypto_kem/newhope1024cca/clean/kem.c b/crypto_kem/newhope1024cca/clean/kem.c index 2ac276c6..163d56f0 100644 --- a/crypto_kem/newhope1024cca/clean/kem.c +++ b/crypto_kem/newhope1024cca/clean/kem.c @@ -52,16 +52,18 @@ int PQCLEAN_NEWHOPE1024CCA_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned **************************************************/ int PQCLEAN_NEWHOPE1024CCA_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk) { unsigned char k_coins_d[3 * NEWHOPE_SYMBYTES]; /* Will contain key, coins, qrom-hash */ - unsigned char buf[2 * NEWHOPE_SYMBYTES]; + unsigned char buf[2 * NEWHOPE_SYMBYTES + 1]; int i; - randombytes(buf, NEWHOPE_SYMBYTES); + buf[0] = 0x04; + randombytes(buf + 1, NEWHOPE_SYMBYTES); - shake256(buf, NEWHOPE_SYMBYTES, buf, NEWHOPE_SYMBYTES); /* Don't release system RNG output */ - shake256(buf + NEWHOPE_SYMBYTES, NEWHOPE_SYMBYTES, pk, NEWHOPE_CCAKEM_PUBLICKEYBYTES); /* Multitarget countermeasure for coins + contributory KEM */ - shake256(k_coins_d, 3 * NEWHOPE_SYMBYTES, buf, 2 * NEWHOPE_SYMBYTES); + shake256(buf + 1, NEWHOPE_SYMBYTES, buf, NEWHOPE_SYMBYTES + 1); /* Don't release system RNG output */ + shake256(buf + 1 + NEWHOPE_SYMBYTES, NEWHOPE_SYMBYTES, pk, NEWHOPE_CCAKEM_PUBLICKEYBYTES); /* Multitarget countermeasure for coins + contributory KEM */ + buf[0] = 0x08; + shake256(k_coins_d, 3 * NEWHOPE_SYMBYTES, buf, 2 * NEWHOPE_SYMBYTES + 1); - PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_enc(ct, buf, pk, k_coins_d + NEWHOPE_SYMBYTES); /* coins are in k_coins_d+NEWHOPE_SYMBYTES */ + PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_enc(ct, buf + 1, pk, k_coins_d + NEWHOPE_SYMBYTES); /* coins are in k_coins_d+NEWHOPE_SYMBYTES */ for (i = 0; i < NEWHOPE_SYMBYTES; i++) { ct[i + NEWHOPE_CPAPKE_CIPHERTEXTBYTES] = k_coins_d[i + 2 * NEWHOPE_SYMBYTES]; /* copy Targhi-Unruh hash into ct */ @@ -89,18 +91,19 @@ int PQCLEAN_NEWHOPE1024CCA_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char int PQCLEAN_NEWHOPE1024CCA_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk) { int i, fail; unsigned char ct_cmp[NEWHOPE_CCAKEM_CIPHERTEXTBYTES]; - unsigned char buf[2 * NEWHOPE_SYMBYTES]; + unsigned char buf[2 * NEWHOPE_SYMBYTES + 1]; unsigned char k_coins_d[3 * NEWHOPE_SYMBYTES]; /* Will contain key, coins, qrom-hash */ const unsigned char *pk = sk + NEWHOPE_CPAPKE_SECRETKEYBYTES; - PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_dec(buf, ct, sk); + buf[0] = 0x08; + PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_dec(buf + 1, ct, sk); for (i = 0; i < NEWHOPE_SYMBYTES; i++) { /* Use hash of pk stored in sk */ - buf[NEWHOPE_SYMBYTES + i] = sk[NEWHOPE_CCAKEM_SECRETKEYBYTES - 2 * NEWHOPE_SYMBYTES + i]; + buf[1 + NEWHOPE_SYMBYTES + i] = sk[NEWHOPE_CCAKEM_SECRETKEYBYTES - 2 * NEWHOPE_SYMBYTES + i]; } - shake256(k_coins_d, 3 * NEWHOPE_SYMBYTES, buf, 2 * NEWHOPE_SYMBYTES); + shake256(k_coins_d, 3 * NEWHOPE_SYMBYTES, buf, 2 * NEWHOPE_SYMBYTES + 1); - PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_enc(ct_cmp, buf, pk, k_coins_d + NEWHOPE_SYMBYTES); /* coins are in k_coins_d+NEWHOPE_SYMBYTES */ + PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_enc(ct_cmp, buf + 1, pk, k_coins_d + NEWHOPE_SYMBYTES); /* coins are in k_coins_d+NEWHOPE_SYMBYTES */ for (i = 0; i < NEWHOPE_SYMBYTES; i++) { ct_cmp[i + NEWHOPE_CPAPKE_CIPHERTEXTBYTES] = k_coins_d[i + 2 * NEWHOPE_SYMBYTES]; diff --git a/crypto_kem/newhope1024cpa/clean/cpapke.c b/crypto_kem/newhope1024cpa/clean/cpapke.c index ec5a5b07..fa11ebe7 100644 --- a/crypto_kem/newhope1024cpa/clean/cpapke.c +++ b/crypto_kem/newhope1024cpa/clean/cpapke.c @@ -101,8 +101,9 @@ void PQCLEAN_NEWHOPE1024CPA_CLEAN_cpapke_keypair(unsigned char *pk, unsigned char *publicseed = z; unsigned char *noiseseed = z + NEWHOPE_SYMBYTES; - randombytes(z, NEWHOPE_SYMBYTES); - shake256(z, 2 * NEWHOPE_SYMBYTES, z, NEWHOPE_SYMBYTES); + z[0] = 0x01; + randombytes(z + 1, NEWHOPE_SYMBYTES); + shake256(z, 2 * NEWHOPE_SYMBYTES, z, NEWHOPE_SYMBYTES + 1); gen_a(&ahat, publicseed); diff --git a/crypto_kem/newhope1024cpa/clean/kem.c b/crypto_kem/newhope1024cpa/clean/kem.c index 54f5a2f3..d1f41113 100644 --- a/crypto_kem/newhope1024cpa/clean/kem.c +++ b/crypto_kem/newhope1024cpa/clean/kem.c @@ -39,9 +39,10 @@ int PQCLEAN_NEWHOPE1024CPA_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned int PQCLEAN_NEWHOPE1024CPA_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk) { unsigned char buf[2 * NEWHOPE_SYMBYTES]; - randombytes(buf, NEWHOPE_SYMBYTES); + buf[0] = 0x02; + randombytes(buf + 1, NEWHOPE_SYMBYTES); - shake256(buf, 2 * NEWHOPE_SYMBYTES, buf, NEWHOPE_SYMBYTES); /* Don't release system RNG output */ + shake256(buf, 2 * NEWHOPE_SYMBYTES, buf, NEWHOPE_SYMBYTES + 1); /* Don't release system RNG output */ PQCLEAN_NEWHOPE1024CPA_CLEAN_cpapke_enc(ct, buf, pk, buf + NEWHOPE_SYMBYTES); /* coins are in buf+NEWHOPE_SYMBYTES */ diff --git a/crypto_kem/newhope512cca/clean/cpapke.c b/crypto_kem/newhope512cca/clean/cpapke.c index f965a213..4328e6de 100644 --- a/crypto_kem/newhope512cca/clean/cpapke.c +++ b/crypto_kem/newhope512cca/clean/cpapke.c @@ -101,8 +101,9 @@ void PQCLEAN_NEWHOPE512CCA_CLEAN_cpapke_keypair(unsigned char *pk, unsigned char *publicseed = z; unsigned char *noiseseed = z + NEWHOPE_SYMBYTES; - randombytes(z, NEWHOPE_SYMBYTES); - shake256(z, 2 * NEWHOPE_SYMBYTES, z, NEWHOPE_SYMBYTES); + z[0] = 0x01; + randombytes(z + 1, NEWHOPE_SYMBYTES); + shake256(z, 2 * NEWHOPE_SYMBYTES, z, NEWHOPE_SYMBYTES + 1); gen_a(&ahat, publicseed); diff --git a/crypto_kem/newhope512cca/clean/kem.c b/crypto_kem/newhope512cca/clean/kem.c index a9d5da8c..ac674d7b 100644 --- a/crypto_kem/newhope512cca/clean/kem.c +++ b/crypto_kem/newhope512cca/clean/kem.c @@ -52,16 +52,18 @@ int PQCLEAN_NEWHOPE512CCA_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned c **************************************************/ int PQCLEAN_NEWHOPE512CCA_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk) { unsigned char k_coins_d[3 * NEWHOPE_SYMBYTES]; /* Will contain key, coins, qrom-hash */ - unsigned char buf[2 * NEWHOPE_SYMBYTES]; + unsigned char buf[2 * NEWHOPE_SYMBYTES + 1]; int i; - randombytes(buf, NEWHOPE_SYMBYTES); + buf[0] = 0x04; + randombytes(buf + 1, NEWHOPE_SYMBYTES); - shake256(buf, NEWHOPE_SYMBYTES, buf, NEWHOPE_SYMBYTES); /* Don't release system RNG output */ - shake256(buf + NEWHOPE_SYMBYTES, NEWHOPE_SYMBYTES, pk, NEWHOPE_CCAKEM_PUBLICKEYBYTES); /* Multitarget countermeasure for coins + contributory KEM */ - shake256(k_coins_d, 3 * NEWHOPE_SYMBYTES, buf, 2 * NEWHOPE_SYMBYTES); + shake256(buf + 1, NEWHOPE_SYMBYTES, buf, NEWHOPE_SYMBYTES + 1); /* Don't release system RNG output */ + shake256(buf + 1 + NEWHOPE_SYMBYTES, NEWHOPE_SYMBYTES, pk, NEWHOPE_CCAKEM_PUBLICKEYBYTES); /* Multitarget countermeasure for coins + contributory KEM */ + buf[0] = 0x08; + shake256(k_coins_d, 3 * NEWHOPE_SYMBYTES, buf, 2 * NEWHOPE_SYMBYTES + 1); - PQCLEAN_NEWHOPE512CCA_CLEAN_cpapke_enc(ct, buf, pk, k_coins_d + NEWHOPE_SYMBYTES); /* coins are in k_coins_d+NEWHOPE_SYMBYTES */ + PQCLEAN_NEWHOPE512CCA_CLEAN_cpapke_enc(ct, buf + 1, pk, k_coins_d + NEWHOPE_SYMBYTES); /* coins are in k_coins_d+NEWHOPE_SYMBYTES */ for (i = 0; i < NEWHOPE_SYMBYTES; i++) { ct[i + NEWHOPE_CPAPKE_CIPHERTEXTBYTES] = k_coins_d[i + 2 * NEWHOPE_SYMBYTES]; /* copy Targhi-Unruh hash into ct */ @@ -89,18 +91,19 @@ int PQCLEAN_NEWHOPE512CCA_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char int PQCLEAN_NEWHOPE512CCA_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk) { int i, fail; unsigned char ct_cmp[NEWHOPE_CCAKEM_CIPHERTEXTBYTES]; - unsigned char buf[2 * NEWHOPE_SYMBYTES]; + unsigned char buf[2 * NEWHOPE_SYMBYTES + 1]; unsigned char k_coins_d[3 * NEWHOPE_SYMBYTES]; /* Will contain key, coins, qrom-hash */ const unsigned char *pk = sk + NEWHOPE_CPAPKE_SECRETKEYBYTES; - PQCLEAN_NEWHOPE512CCA_CLEAN_cpapke_dec(buf, ct, sk); + buf[0] = 0x08; + PQCLEAN_NEWHOPE512CCA_CLEAN_cpapke_dec(buf + 1, ct, sk); for (i = 0; i < NEWHOPE_SYMBYTES; i++) { /* Use hash of pk stored in sk */ - buf[NEWHOPE_SYMBYTES + i] = sk[NEWHOPE_CCAKEM_SECRETKEYBYTES - 2 * NEWHOPE_SYMBYTES + i]; + buf[1 + NEWHOPE_SYMBYTES + i] = sk[NEWHOPE_CCAKEM_SECRETKEYBYTES - 2 * NEWHOPE_SYMBYTES + i]; } - shake256(k_coins_d, 3 * NEWHOPE_SYMBYTES, buf, 2 * NEWHOPE_SYMBYTES); + shake256(k_coins_d, 3 * NEWHOPE_SYMBYTES, buf, 2 * NEWHOPE_SYMBYTES + 1); - PQCLEAN_NEWHOPE512CCA_CLEAN_cpapke_enc(ct_cmp, buf, pk, k_coins_d + NEWHOPE_SYMBYTES); /* coins are in k_coins_d+NEWHOPE_SYMBYTES */ + PQCLEAN_NEWHOPE512CCA_CLEAN_cpapke_enc(ct_cmp, buf + 1, pk, k_coins_d + NEWHOPE_SYMBYTES); /* coins are in k_coins_d+NEWHOPE_SYMBYTES */ for (i = 0; i < NEWHOPE_SYMBYTES; i++) { ct_cmp[i + NEWHOPE_CPAPKE_CIPHERTEXTBYTES] = k_coins_d[i + 2 * NEWHOPE_SYMBYTES]; diff --git a/crypto_kem/newhope512cpa/clean/cpapke.c b/crypto_kem/newhope512cpa/clean/cpapke.c index dbcc3434..c53b657a 100644 --- a/crypto_kem/newhope512cpa/clean/cpapke.c +++ b/crypto_kem/newhope512cpa/clean/cpapke.c @@ -101,8 +101,9 @@ void PQCLEAN_NEWHOPE512CPA_CLEAN_cpapke_keypair(unsigned char *pk, unsigned char *publicseed = z; unsigned char *noiseseed = z + NEWHOPE_SYMBYTES; - randombytes(z, NEWHOPE_SYMBYTES); - shake256(z, 2 * NEWHOPE_SYMBYTES, z, NEWHOPE_SYMBYTES); + z[0] = 0x01; + randombytes(z + 1, NEWHOPE_SYMBYTES); + shake256(z, 2 * NEWHOPE_SYMBYTES, z, NEWHOPE_SYMBYTES + 1); gen_a(&ahat, publicseed); diff --git a/crypto_kem/newhope512cpa/clean/kem.c b/crypto_kem/newhope512cpa/clean/kem.c index 95fbd4ff..ae3f9d4b 100644 --- a/crypto_kem/newhope512cpa/clean/kem.c +++ b/crypto_kem/newhope512cpa/clean/kem.c @@ -39,9 +39,10 @@ int PQCLEAN_NEWHOPE512CPA_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned c int PQCLEAN_NEWHOPE512CPA_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk) { unsigned char buf[2 * NEWHOPE_SYMBYTES]; - randombytes(buf, NEWHOPE_SYMBYTES); + buf[0] = 0x02; + randombytes(buf + 1, NEWHOPE_SYMBYTES); - shake256(buf, 2 * NEWHOPE_SYMBYTES, buf, NEWHOPE_SYMBYTES); /* Don't release system RNG output */ + shake256(buf, 2 * NEWHOPE_SYMBYTES, buf, NEWHOPE_SYMBYTES + 1); /* Don't release system RNG output */ PQCLEAN_NEWHOPE512CPA_CLEAN_cpapke_enc(ct, buf, pk, buf + NEWHOPE_SYMBYTES); /* coins are in buf+NEWHOPE_SYMBYTES */ From bca4250d1f22f1b3d0cc7dfa54e50b9b766a5845 Mon Sep 17 00:00:00 2001 From: Ko- Date: Fri, 29 May 2020 18:50:37 +0200 Subject: [PATCH 3/7] Update KAT values --- crypto_kem/newhope1024cca/META.yml | 2 +- crypto_kem/newhope1024cpa/META.yml | 2 +- crypto_kem/newhope512cca/META.yml | 2 +- crypto_kem/newhope512cpa/META.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto_kem/newhope1024cca/META.yml b/crypto_kem/newhope1024cca/META.yml index dc7be2cc..46af1287 100644 --- a/crypto_kem/newhope1024cca/META.yml +++ b/crypto_kem/newhope1024cca/META.yml @@ -6,7 +6,7 @@ length-public-key: 1824 length-secret-key: 3680 length-ciphertext: 2208 length-shared-secret: 32 -nistkat-sha256: 8500b88222b3a62e57a6ecaac57f79258f08af49211e0c3f2ca7eab8089c0ce0 +nistkat-sha256: 4a21f329bb5402a90d343af01ec1c8bc8ffffa8098cb0b89e1d2129f5157a073 principal-submitters: - Thomas Pöppelmann auxiliary-submitters: diff --git a/crypto_kem/newhope1024cpa/META.yml b/crypto_kem/newhope1024cpa/META.yml index 1fabd379..dcfa3746 100644 --- a/crypto_kem/newhope1024cpa/META.yml +++ b/crypto_kem/newhope1024cpa/META.yml @@ -6,7 +6,7 @@ length-public-key: 1824 length-secret-key: 1792 length-ciphertext: 2176 length-shared-secret: 32 -nistkat-sha256: f48b42b21a51d7f9325abc5fbda74872d62feaa8cbf818bee87f29bf96630a2f +nistkat-sha256: 440e2afb40d212a44d1bb1dc9963d7c942fa6ceb16fed2b1ccf015fa75ab115b principal-submitters: - Thomas Pöppelmann auxiliary-submitters: diff --git a/crypto_kem/newhope512cca/META.yml b/crypto_kem/newhope512cca/META.yml index da3bbec2..5cb83873 100644 --- a/crypto_kem/newhope512cca/META.yml +++ b/crypto_kem/newhope512cca/META.yml @@ -6,7 +6,7 @@ length-public-key: 928 length-secret-key: 1888 length-ciphertext: 1120 length-shared-secret: 32 -nistkat-sha256: 5b0389f8d9c30055ad0fb83da540ca36969dde041bebe6f1018c37768c5e1479 +nistkat-sha256: 4290da64305e70e65766be5d4e488dee2b4b238172876ceefc931934b6964a7d principal-submitters: - Thomas Pöppelmann auxiliary-submitters: diff --git a/crypto_kem/newhope512cpa/META.yml b/crypto_kem/newhope512cpa/META.yml index 8afe6275..277b9ceb 100644 --- a/crypto_kem/newhope512cpa/META.yml +++ b/crypto_kem/newhope512cpa/META.yml @@ -6,7 +6,7 @@ length-public-key: 928 length-secret-key: 896 length-ciphertext: 1088 length-shared-secret: 32 -nistkat-sha256: 42444446b96f45c9b7221c4fde8afd5dfc0b3c2ff05b9a88ff12ea3949fbb76c +nistkat-sha256: 7df3eae4740483a61d13610f6bc2221f27e32c7849cf371e9770f986ce6fdb54 principal-submitters: - Thomas Pöppelmann auxiliary-submitters: From 25b15e5791b3c052638587edf8a037eee5f450b3 Mon Sep 17 00:00:00 2001 From: Ko- Date: Fri, 29 May 2020 18:54:15 +0200 Subject: [PATCH 4/7] Fix whitespace to satisfy test_duplicate_consistency --- crypto_kem/newhope1024cca/clean/kem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto_kem/newhope1024cca/clean/kem.c b/crypto_kem/newhope1024cca/clean/kem.c index 163d56f0..2fbcff50 100644 --- a/crypto_kem/newhope1024cca/clean/kem.c +++ b/crypto_kem/newhope1024cca/clean/kem.c @@ -63,7 +63,7 @@ int PQCLEAN_NEWHOPE1024CCA_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char buf[0] = 0x08; shake256(k_coins_d, 3 * NEWHOPE_SYMBYTES, buf, 2 * NEWHOPE_SYMBYTES + 1); - PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_enc(ct, buf + 1, pk, k_coins_d + NEWHOPE_SYMBYTES); /* coins are in k_coins_d+NEWHOPE_SYMBYTES */ + PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_enc(ct, buf + 1, pk, k_coins_d + NEWHOPE_SYMBYTES); /* coins are in k_coins_d+NEWHOPE_SYMBYTES */ for (i = 0; i < NEWHOPE_SYMBYTES; i++) { ct[i + NEWHOPE_CPAPKE_CIPHERTEXTBYTES] = k_coins_d[i + 2 * NEWHOPE_SYMBYTES]; /* copy Targhi-Unruh hash into ct */ @@ -103,7 +103,7 @@ int PQCLEAN_NEWHOPE1024CCA_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigne } shake256(k_coins_d, 3 * NEWHOPE_SYMBYTES, buf, 2 * NEWHOPE_SYMBYTES + 1); - PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_enc(ct_cmp, buf + 1, pk, k_coins_d + NEWHOPE_SYMBYTES); /* coins are in k_coins_d+NEWHOPE_SYMBYTES */ + PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_enc(ct_cmp, buf + 1, pk, k_coins_d + NEWHOPE_SYMBYTES); /* coins are in k_coins_d+NEWHOPE_SYMBYTES */ for (i = 0; i < NEWHOPE_SYMBYTES; i++) { ct_cmp[i + NEWHOPE_CPAPKE_CIPHERTEXTBYTES] = k_coins_d[i + 2 * NEWHOPE_SYMBYTES]; From ae1530d192b47c28663a61395b9e6a67342c50e7 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Fri, 19 Jun 2020 13:15:13 -0400 Subject: [PATCH 5/7] Fix timing leak in decapsulation. As identified in: Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020. Based on https://github.com/microsoft/PQCrypto-LWEKE/commit/155c24c3df47be6d5d9845fea37be110945e963c --- crypto_kem/frodokem1344aes/clean/common.h | 2 ++ crypto_kem/frodokem1344aes/clean/kem.c | 15 ++++++------- crypto_kem/frodokem1344aes/clean/util.c | 24 +++++++++++++++++++++ crypto_kem/frodokem1344aes/opt/common.h | 2 ++ crypto_kem/frodokem1344aes/opt/kem.c | 15 ++++++------- crypto_kem/frodokem1344aes/opt/util.c | 24 +++++++++++++++++++++ crypto_kem/frodokem1344shake/clean/common.h | 2 ++ crypto_kem/frodokem1344shake/clean/kem.c | 15 ++++++------- crypto_kem/frodokem1344shake/clean/util.c | 24 +++++++++++++++++++++ crypto_kem/frodokem1344shake/opt/common.h | 2 ++ crypto_kem/frodokem1344shake/opt/kem.c | 15 ++++++------- crypto_kem/frodokem1344shake/opt/util.c | 24 +++++++++++++++++++++ crypto_kem/frodokem640aes/clean/common.h | 2 ++ crypto_kem/frodokem640aes/clean/kem.c | 15 ++++++------- crypto_kem/frodokem640aes/clean/util.c | 24 +++++++++++++++++++++ crypto_kem/frodokem640aes/opt/common.h | 2 ++ crypto_kem/frodokem640aes/opt/kem.c | 15 ++++++------- crypto_kem/frodokem640aes/opt/util.c | 24 +++++++++++++++++++++ crypto_kem/frodokem640shake/clean/common.h | 2 ++ crypto_kem/frodokem640shake/clean/kem.c | 15 ++++++------- crypto_kem/frodokem640shake/clean/util.c | 24 +++++++++++++++++++++ crypto_kem/frodokem640shake/opt/common.h | 2 ++ crypto_kem/frodokem640shake/opt/kem.c | 15 ++++++------- crypto_kem/frodokem640shake/opt/util.c | 24 +++++++++++++++++++++ crypto_kem/frodokem976aes/clean/common.h | 2 ++ crypto_kem/frodokem976aes/clean/kem.c | 15 ++++++------- crypto_kem/frodokem976aes/clean/util.c | 24 +++++++++++++++++++++ crypto_kem/frodokem976aes/opt/common.h | 2 ++ crypto_kem/frodokem976aes/opt/kem.c | 15 ++++++------- crypto_kem/frodokem976aes/opt/util.c | 24 +++++++++++++++++++++ crypto_kem/frodokem976shake/clean/common.h | 2 ++ crypto_kem/frodokem976shake/clean/kem.c | 15 ++++++------- crypto_kem/frodokem976shake/clean/util.c | 24 +++++++++++++++++++++ crypto_kem/frodokem976shake/opt/common.h | 2 ++ crypto_kem/frodokem976shake/opt/kem.c | 15 ++++++------- crypto_kem/frodokem976shake/opt/util.c | 24 +++++++++++++++++++++ 36 files changed, 396 insertions(+), 96 deletions(-) diff --git a/crypto_kem/frodokem1344aes/clean/common.h b/crypto_kem/frodokem1344aes/clean/common.h index f9fa3779..524370f0 100644 --- a/crypto_kem/frodokem1344aes/clean/common.h +++ b/crypto_kem/frodokem1344aes/clean/common.h @@ -12,6 +12,8 @@ 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); +int8_t PQCLEAN_FRODOKEM1344AES_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len); +void PQCLEAN_FRODOKEM1344AES_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector); 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); diff --git a/crypto_kem/frodokem1344aes/clean/kem.c b/crypto_kem/frodokem1344aes/clean/kem.c index 8faaf005..fcef3ee4 100644 --- a/crypto_kem/frodokem1344aes/clean/kem.c +++ b/crypto_kem/frodokem1344aes/clean/kem.c @@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM1344AES_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, 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); - } + // If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s) + // Needs to avoid branching on secret data as per: + // Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum + // primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020. + int8_t selector = PQCLEAN_FRODOKEM1344AES_CLEAN_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM1344AES_CLEAN_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR); + // If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s) + PQCLEAN_FRODOKEM1344AES_CLEAN_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector); shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); // Cleanup: diff --git a/crypto_kem/frodokem1344aes/clean/util.c b/crypto_kem/frodokem1344aes/clean/util.c index 138dbe23..be4e2d3e 100644 --- a/crypto_kem/frodokem1344aes/clean/util.c +++ b/crypto_kem/frodokem1344aes/clean/util.c @@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM1344AES_CLEAN_unpack(uint16_t *out, size_t outlen, const ui } +int8_t PQCLEAN_FRODOKEM1344AES_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) { + // Compare two arrays in constant time. + // Returns 0 if the byte arrays are equal, -1 otherwise. + uint16_t r = 0; + + for (size_t i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1); + return (int8_t)r; +} + + +void PQCLEAN_FRODOKEM1344AES_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) { + // Select one of the two input arrays to be moved to r + // If (selector == 0) then load r with a, else if (selector == -1) load r with b + + for (size_t i = 0; i < len; i++) { + r[i] = (~selector & a[i]) | (selector & b[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. diff --git a/crypto_kem/frodokem1344aes/opt/common.h b/crypto_kem/frodokem1344aes/opt/common.h index 1ea3ac89..e00eef0b 100644 --- a/crypto_kem/frodokem1344aes/opt/common.h +++ b/crypto_kem/frodokem1344aes/opt/common.h @@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM1344AES_OPT_key_encode(uint16_t *out, const uint16_t *in); void PQCLEAN_FRODOKEM1344AES_OPT_key_decode(uint16_t *out, const uint16_t *in); void PQCLEAN_FRODOKEM1344AES_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); void PQCLEAN_FRODOKEM1344AES_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); +int8_t PQCLEAN_FRODOKEM1344AES_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len); +void PQCLEAN_FRODOKEM1344AES_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector); void PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes(uint8_t *mem, size_t n); uint16_t PQCLEAN_FRODOKEM1344AES_OPT_LE_TO_UINT16(uint16_t n); uint16_t PQCLEAN_FRODOKEM1344AES_OPT_UINT16_TO_LE(uint16_t n); diff --git a/crypto_kem/frodokem1344aes/opt/kem.c b/crypto_kem/frodokem1344aes/opt/kem.c index f00af9c4..89c94adc 100644 --- a/crypto_kem/frodokem1344aes/opt/kem.c +++ b/crypto_kem/frodokem1344aes/opt/kem.c @@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM1344AES_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, c 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); - } + // If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s) + // Needs to avoid branching on secret data as per: + // Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum + // primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020. + int8_t selector = PQCLEAN_FRODOKEM1344AES_OPT_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM1344AES_OPT_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR); + // If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s) + PQCLEAN_FRODOKEM1344AES_OPT_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector); shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); // Cleanup: diff --git a/crypto_kem/frodokem1344aes/opt/util.c b/crypto_kem/frodokem1344aes/opt/util.c index 33b28109..d2dbb565 100644 --- a/crypto_kem/frodokem1344aes/opt/util.c +++ b/crypto_kem/frodokem1344aes/opt/util.c @@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM1344AES_OPT_unpack(uint16_t *out, size_t outlen, const uint } +int8_t PQCLEAN_FRODOKEM1344AES_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) { + // Compare two arrays in constant time. + // Returns 0 if the byte arrays are equal, -1 otherwise. + uint16_t r = 0; + + for (size_t i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1); + return (int8_t)r; +} + + +void PQCLEAN_FRODOKEM1344AES_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) { + // Select one of the two input arrays to be moved to r + // If (selector == 0) then load r with a, else if (selector == -1) load r with b + + for (size_t i = 0; i < len; i++) { + r[i] = (~selector & a[i]) | (selector & b[i]); + } +} + + void PQCLEAN_FRODOKEM1344AES_OPT_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. diff --git a/crypto_kem/frodokem1344shake/clean/common.h b/crypto_kem/frodokem1344shake/clean/common.h index 5e487aec..2daf0da3 100644 --- a/crypto_kem/frodokem1344shake/clean/common.h +++ b/crypto_kem/frodokem1344shake/clean/common.h @@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_key_encode(uint16_t *out, const uint16_t *i void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_key_decode(uint16_t *out, const uint16_t *in); void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); +int8_t PQCLEAN_FRODOKEM1344SHAKE_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len); +void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector); void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes(uint8_t *mem, size_t n); uint16_t PQCLEAN_FRODOKEM1344SHAKE_CLEAN_LE_TO_UINT16(uint16_t n); uint16_t PQCLEAN_FRODOKEM1344SHAKE_CLEAN_UINT16_TO_LE(uint16_t n); diff --git a/crypto_kem/frodokem1344shake/clean/kem.c b/crypto_kem/frodokem1344shake/clean/kem.c index 9e4bd8b5..8374b5c3 100644 --- a/crypto_kem/frodokem1344shake/clean/kem.c +++ b/crypto_kem/frodokem1344shake/clean/kem.c @@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM1344SHAKE_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *c 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); - } + // If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s) + // Needs to avoid branching on secret data as per: + // Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum + // primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020. + int8_t selector = PQCLEAN_FRODOKEM1344SHAKE_CLEAN_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM1344SHAKE_CLEAN_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR); + // If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s) + PQCLEAN_FRODOKEM1344SHAKE_CLEAN_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector); shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); // Cleanup: diff --git a/crypto_kem/frodokem1344shake/clean/util.c b/crypto_kem/frodokem1344shake/clean/util.c index 1a403a39..189ea752 100644 --- a/crypto_kem/frodokem1344shake/clean/util.c +++ b/crypto_kem/frodokem1344shake/clean/util.c @@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_unpack(uint16_t *out, size_t outlen, const } +int8_t PQCLEAN_FRODOKEM1344SHAKE_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) { + // Compare two arrays in constant time. + // Returns 0 if the byte arrays are equal, -1 otherwise. + uint16_t r = 0; + + for (size_t i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1); + return (int8_t)r; +} + + +void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) { + // Select one of the two input arrays to be moved to r + // If (selector == 0) then load r with a, else if (selector == -1) load r with b + + for (size_t i = 0; i < len; i++) { + r[i] = (~selector & a[i]) | (selector & b[i]); + } +} + + void PQCLEAN_FRODOKEM1344SHAKE_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. diff --git a/crypto_kem/frodokem1344shake/opt/common.h b/crypto_kem/frodokem1344shake/opt/common.h index 32af833b..7d907b6a 100644 --- a/crypto_kem/frodokem1344shake/opt/common.h +++ b/crypto_kem/frodokem1344shake/opt/common.h @@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM1344SHAKE_OPT_key_encode(uint16_t *out, const uint16_t *in) void PQCLEAN_FRODOKEM1344SHAKE_OPT_key_decode(uint16_t *out, const uint16_t *in); void PQCLEAN_FRODOKEM1344SHAKE_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); void PQCLEAN_FRODOKEM1344SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); +int8_t PQCLEAN_FRODOKEM1344SHAKE_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len); +void PQCLEAN_FRODOKEM1344SHAKE_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector); void PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes(uint8_t *mem, size_t n); uint16_t PQCLEAN_FRODOKEM1344SHAKE_OPT_LE_TO_UINT16(uint16_t n); uint16_t PQCLEAN_FRODOKEM1344SHAKE_OPT_UINT16_TO_LE(uint16_t n); diff --git a/crypto_kem/frodokem1344shake/opt/kem.c b/crypto_kem/frodokem1344shake/opt/kem.c index d4022d58..57f38048 100644 --- a/crypto_kem/frodokem1344shake/opt/kem.c +++ b/crypto_kem/frodokem1344shake/opt/kem.c @@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM1344SHAKE_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, 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); - } + // If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s) + // Needs to avoid branching on secret data as per: + // Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum + // primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020. + int8_t selector = PQCLEAN_FRODOKEM1344SHAKE_OPT_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM1344SHAKE_OPT_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR); + // If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s) + PQCLEAN_FRODOKEM1344SHAKE_OPT_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector); shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); // Cleanup: diff --git a/crypto_kem/frodokem1344shake/opt/util.c b/crypto_kem/frodokem1344shake/opt/util.c index 22606c95..220bfae5 100644 --- a/crypto_kem/frodokem1344shake/opt/util.c +++ b/crypto_kem/frodokem1344shake/opt/util.c @@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM1344SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const ui } +int8_t PQCLEAN_FRODOKEM1344SHAKE_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) { + // Compare two arrays in constant time. + // Returns 0 if the byte arrays are equal, -1 otherwise. + uint16_t r = 0; + + for (size_t i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1); + return (int8_t)r; +} + + +void PQCLEAN_FRODOKEM1344SHAKE_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) { + // Select one of the two input arrays to be moved to r + // If (selector == 0) then load r with a, else if (selector == -1) load r with b + + for (size_t i = 0; i < len; i++) { + r[i] = (~selector & a[i]) | (selector & b[i]); + } +} + + void PQCLEAN_FRODOKEM1344SHAKE_OPT_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. diff --git a/crypto_kem/frodokem640aes/clean/common.h b/crypto_kem/frodokem640aes/clean/common.h index 76309497..f30d5835 100644 --- a/crypto_kem/frodokem640aes/clean/common.h +++ b/crypto_kem/frodokem640aes/clean/common.h @@ -12,6 +12,8 @@ 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); +int8_t PQCLEAN_FRODOKEM640AES_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len); +void PQCLEAN_FRODOKEM640AES_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector); 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); diff --git a/crypto_kem/frodokem640aes/clean/kem.c b/crypto_kem/frodokem640aes/clean/kem.c index 75b38eb0..3572ee59 100644 --- a/crypto_kem/frodokem640aes/clean/kem.c +++ b/crypto_kem/frodokem640aes/clean/kem.c @@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM640AES_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, 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); - } + // If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s) + // Needs to avoid branching on secret data as per: + // Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum + // primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020. + int8_t selector = PQCLEAN_FRODOKEM640AES_CLEAN_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM640AES_CLEAN_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR); + // If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s) + PQCLEAN_FRODOKEM640AES_CLEAN_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector); shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); // Cleanup: diff --git a/crypto_kem/frodokem640aes/clean/util.c b/crypto_kem/frodokem640aes/clean/util.c index 96bec752..d0218113 100644 --- a/crypto_kem/frodokem640aes/clean/util.c +++ b/crypto_kem/frodokem640aes/clean/util.c @@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM640AES_CLEAN_unpack(uint16_t *out, size_t outlen, const uin } +int8_t PQCLEAN_FRODOKEM640AES_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) { + // Compare two arrays in constant time. + // Returns 0 if the byte arrays are equal, -1 otherwise. + uint16_t r = 0; + + for (size_t i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1); + return (int8_t)r; +} + + +void PQCLEAN_FRODOKEM640AES_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) { + // Select one of the two input arrays to be moved to r + // If (selector == 0) then load r with a, else if (selector == -1) load r with b + + for (size_t i = 0; i < len; i++) { + r[i] = (~selector & a[i]) | (selector & b[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. diff --git a/crypto_kem/frodokem640aes/opt/common.h b/crypto_kem/frodokem640aes/opt/common.h index ea2f4d6d..aead8777 100644 --- a/crypto_kem/frodokem640aes/opt/common.h +++ b/crypto_kem/frodokem640aes/opt/common.h @@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM640AES_OPT_key_encode(uint16_t *out, const uint16_t *in); void PQCLEAN_FRODOKEM640AES_OPT_key_decode(uint16_t *out, const uint16_t *in); void PQCLEAN_FRODOKEM640AES_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); void PQCLEAN_FRODOKEM640AES_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); +int8_t PQCLEAN_FRODOKEM640AES_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len); +void PQCLEAN_FRODOKEM640AES_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector); void PQCLEAN_FRODOKEM640AES_OPT_clear_bytes(uint8_t *mem, size_t n); uint16_t PQCLEAN_FRODOKEM640AES_OPT_LE_TO_UINT16(uint16_t n); uint16_t PQCLEAN_FRODOKEM640AES_OPT_UINT16_TO_LE(uint16_t n); diff --git a/crypto_kem/frodokem640aes/opt/kem.c b/crypto_kem/frodokem640aes/opt/kem.c index 587fd86b..d65a08ac 100644 --- a/crypto_kem/frodokem640aes/opt/kem.c +++ b/crypto_kem/frodokem640aes/opt/kem.c @@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM640AES_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, co 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); - } + // If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s) + // Needs to avoid branching on secret data as per: + // Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum + // primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020. + int8_t selector = PQCLEAN_FRODOKEM640AES_OPT_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM640AES_OPT_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR); + // If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s) + PQCLEAN_FRODOKEM640AES_OPT_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector); shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); // Cleanup: diff --git a/crypto_kem/frodokem640aes/opt/util.c b/crypto_kem/frodokem640aes/opt/util.c index 716c36dc..b43d9f84 100644 --- a/crypto_kem/frodokem640aes/opt/util.c +++ b/crypto_kem/frodokem640aes/opt/util.c @@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM640AES_OPT_unpack(uint16_t *out, size_t outlen, const uint8 } +int8_t PQCLEAN_FRODOKEM640AES_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) { + // Compare two arrays in constant time. + // Returns 0 if the byte arrays are equal, -1 otherwise. + uint16_t r = 0; + + for (size_t i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1); + return (int8_t)r; +} + + +void PQCLEAN_FRODOKEM640AES_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) { + // Select one of the two input arrays to be moved to r + // If (selector == 0) then load r with a, else if (selector == -1) load r with b + + for (size_t i = 0; i < len; i++) { + r[i] = (~selector & a[i]) | (selector & b[i]); + } +} + + void PQCLEAN_FRODOKEM640AES_OPT_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. diff --git a/crypto_kem/frodokem640shake/clean/common.h b/crypto_kem/frodokem640shake/clean/common.h index aa19ea07..ddba6428 100644 --- a/crypto_kem/frodokem640shake/clean/common.h +++ b/crypto_kem/frodokem640shake/clean/common.h @@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM640SHAKE_CLEAN_key_encode(uint16_t *out, const uint16_t *in void PQCLEAN_FRODOKEM640SHAKE_CLEAN_key_decode(uint16_t *out, const uint16_t *in); void PQCLEAN_FRODOKEM640SHAKE_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); void PQCLEAN_FRODOKEM640SHAKE_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); +int8_t PQCLEAN_FRODOKEM640SHAKE_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len); +void PQCLEAN_FRODOKEM640SHAKE_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector); void PQCLEAN_FRODOKEM640SHAKE_CLEAN_clear_bytes(uint8_t *mem, size_t n); uint16_t PQCLEAN_FRODOKEM640SHAKE_CLEAN_LE_TO_UINT16(uint16_t n); uint16_t PQCLEAN_FRODOKEM640SHAKE_CLEAN_UINT16_TO_LE(uint16_t n); diff --git a/crypto_kem/frodokem640shake/clean/kem.c b/crypto_kem/frodokem640shake/clean/kem.c index b9c42351..40d5dd76 100644 --- a/crypto_kem/frodokem640shake/clean/kem.c +++ b/crypto_kem/frodokem640shake/clean/kem.c @@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM640SHAKE_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct 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); - } + // If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s) + // Needs to avoid branching on secret data as per: + // Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum + // primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020. + int8_t selector = PQCLEAN_FRODOKEM640SHAKE_CLEAN_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM640SHAKE_CLEAN_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR); + // If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s) + PQCLEAN_FRODOKEM640SHAKE_CLEAN_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector); shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); // Cleanup: diff --git a/crypto_kem/frodokem640shake/clean/util.c b/crypto_kem/frodokem640shake/clean/util.c index 6c12743f..31e1b155 100644 --- a/crypto_kem/frodokem640shake/clean/util.c +++ b/crypto_kem/frodokem640shake/clean/util.c @@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM640SHAKE_CLEAN_unpack(uint16_t *out, size_t outlen, const u } +int8_t PQCLEAN_FRODOKEM640SHAKE_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) { + // Compare two arrays in constant time. + // Returns 0 if the byte arrays are equal, -1 otherwise. + uint16_t r = 0; + + for (size_t i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1); + return (int8_t)r; +} + + +void PQCLEAN_FRODOKEM640SHAKE_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) { + // Select one of the two input arrays to be moved to r + // If (selector == 0) then load r with a, else if (selector == -1) load r with b + + for (size_t i = 0; i < len; i++) { + r[i] = (~selector & a[i]) | (selector & b[i]); + } +} + + void PQCLEAN_FRODOKEM640SHAKE_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. diff --git a/crypto_kem/frodokem640shake/opt/common.h b/crypto_kem/frodokem640shake/opt/common.h index 2910c84f..32b4a4c1 100644 --- a/crypto_kem/frodokem640shake/opt/common.h +++ b/crypto_kem/frodokem640shake/opt/common.h @@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM640SHAKE_OPT_key_encode(uint16_t *out, const uint16_t *in); void PQCLEAN_FRODOKEM640SHAKE_OPT_key_decode(uint16_t *out, const uint16_t *in); void PQCLEAN_FRODOKEM640SHAKE_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); void PQCLEAN_FRODOKEM640SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); +int8_t PQCLEAN_FRODOKEM640SHAKE_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len); +void PQCLEAN_FRODOKEM640SHAKE_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector); void PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes(uint8_t *mem, size_t n); uint16_t PQCLEAN_FRODOKEM640SHAKE_OPT_LE_TO_UINT16(uint16_t n); uint16_t PQCLEAN_FRODOKEM640SHAKE_OPT_UINT16_TO_LE(uint16_t n); diff --git a/crypto_kem/frodokem640shake/opt/kem.c b/crypto_kem/frodokem640shake/opt/kem.c index 606e5544..e1b50855 100644 --- a/crypto_kem/frodokem640shake/opt/kem.c +++ b/crypto_kem/frodokem640shake/opt/kem.c @@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM640SHAKE_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, 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); - } + // If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s) + // Needs to avoid branching on secret data as per: + // Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum + // primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020. + int8_t selector = PQCLEAN_FRODOKEM640SHAKE_OPT_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM640SHAKE_OPT_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR); + // If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s) + PQCLEAN_FRODOKEM640SHAKE_OPT_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector); shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); // Cleanup: diff --git a/crypto_kem/frodokem640shake/opt/util.c b/crypto_kem/frodokem640shake/opt/util.c index ab2f74c9..6cbe46b0 100644 --- a/crypto_kem/frodokem640shake/opt/util.c +++ b/crypto_kem/frodokem640shake/opt/util.c @@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM640SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const uin } +int8_t PQCLEAN_FRODOKEM640SHAKE_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) { + // Compare two arrays in constant time. + // Returns 0 if the byte arrays are equal, -1 otherwise. + uint16_t r = 0; + + for (size_t i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1); + return (int8_t)r; +} + + +void PQCLEAN_FRODOKEM640SHAKE_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) { + // Select one of the two input arrays to be moved to r + // If (selector == 0) then load r with a, else if (selector == -1) load r with b + + for (size_t i = 0; i < len; i++) { + r[i] = (~selector & a[i]) | (selector & b[i]); + } +} + + void PQCLEAN_FRODOKEM640SHAKE_OPT_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. diff --git a/crypto_kem/frodokem976aes/clean/common.h b/crypto_kem/frodokem976aes/clean/common.h index c6d420bb..6480b837 100644 --- a/crypto_kem/frodokem976aes/clean/common.h +++ b/crypto_kem/frodokem976aes/clean/common.h @@ -12,6 +12,8 @@ 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); +int8_t PQCLEAN_FRODOKEM976AES_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len); +void PQCLEAN_FRODOKEM976AES_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector); 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); diff --git a/crypto_kem/frodokem976aes/clean/kem.c b/crypto_kem/frodokem976aes/clean/kem.c index 4173ba37..125463c5 100644 --- a/crypto_kem/frodokem976aes/clean/kem.c +++ b/crypto_kem/frodokem976aes/clean/kem.c @@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM976AES_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, 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); - } + // If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s) + // Needs to avoid branching on secret data as per: + // Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum + // primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020. + int8_t selector = PQCLEAN_FRODOKEM976AES_CLEAN_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM976AES_CLEAN_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR); + // If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s) + PQCLEAN_FRODOKEM976AES_CLEAN_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector); shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); // Cleanup: diff --git a/crypto_kem/frodokem976aes/clean/util.c b/crypto_kem/frodokem976aes/clean/util.c index 92199cae..dda97621 100644 --- a/crypto_kem/frodokem976aes/clean/util.c +++ b/crypto_kem/frodokem976aes/clean/util.c @@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM976AES_CLEAN_unpack(uint16_t *out, size_t outlen, const uin } +int8_t PQCLEAN_FRODOKEM976AES_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) { + // Compare two arrays in constant time. + // Returns 0 if the byte arrays are equal, -1 otherwise. + uint16_t r = 0; + + for (size_t i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1); + return (int8_t)r; +} + + +void PQCLEAN_FRODOKEM976AES_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) { + // Select one of the two input arrays to be moved to r + // If (selector == 0) then load r with a, else if (selector == -1) load r with b + + for (size_t i = 0; i < len; i++) { + r[i] = (~selector & a[i]) | (selector & b[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. diff --git a/crypto_kem/frodokem976aes/opt/common.h b/crypto_kem/frodokem976aes/opt/common.h index 141e41b0..e655aedf 100644 --- a/crypto_kem/frodokem976aes/opt/common.h +++ b/crypto_kem/frodokem976aes/opt/common.h @@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM976AES_OPT_key_encode(uint16_t *out, const uint16_t *in); void PQCLEAN_FRODOKEM976AES_OPT_key_decode(uint16_t *out, const uint16_t *in); void PQCLEAN_FRODOKEM976AES_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); void PQCLEAN_FRODOKEM976AES_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); +int8_t PQCLEAN_FRODOKEM976AES_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len); +void PQCLEAN_FRODOKEM976AES_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector); void PQCLEAN_FRODOKEM976AES_OPT_clear_bytes(uint8_t *mem, size_t n); uint16_t PQCLEAN_FRODOKEM976AES_OPT_LE_TO_UINT16(uint16_t n); uint16_t PQCLEAN_FRODOKEM976AES_OPT_UINT16_TO_LE(uint16_t n); diff --git a/crypto_kem/frodokem976aes/opt/kem.c b/crypto_kem/frodokem976aes/opt/kem.c index 52b00bee..c2420c8e 100644 --- a/crypto_kem/frodokem976aes/opt/kem.c +++ b/crypto_kem/frodokem976aes/opt/kem.c @@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM976AES_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, co 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); - } + // If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s) + // Needs to avoid branching on secret data as per: + // Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum + // primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020. + int8_t selector = PQCLEAN_FRODOKEM976AES_OPT_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM976AES_OPT_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR); + // If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s) + PQCLEAN_FRODOKEM976AES_OPT_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector); shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); // Cleanup: diff --git a/crypto_kem/frodokem976aes/opt/util.c b/crypto_kem/frodokem976aes/opt/util.c index 0369e652..67019878 100644 --- a/crypto_kem/frodokem976aes/opt/util.c +++ b/crypto_kem/frodokem976aes/opt/util.c @@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM976AES_OPT_unpack(uint16_t *out, size_t outlen, const uint8 } +int8_t PQCLEAN_FRODOKEM976AES_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) { + // Compare two arrays in constant time. + // Returns 0 if the byte arrays are equal, -1 otherwise. + uint16_t r = 0; + + for (size_t i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1); + return (int8_t)r; +} + + +void PQCLEAN_FRODOKEM976AES_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) { + // Select one of the two input arrays to be moved to r + // If (selector == 0) then load r with a, else if (selector == -1) load r with b + + for (size_t i = 0; i < len; i++) { + r[i] = (~selector & a[i]) | (selector & b[i]); + } +} + + void PQCLEAN_FRODOKEM976AES_OPT_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. diff --git a/crypto_kem/frodokem976shake/clean/common.h b/crypto_kem/frodokem976shake/clean/common.h index 12e2b2b8..eadc17c6 100644 --- a/crypto_kem/frodokem976shake/clean/common.h +++ b/crypto_kem/frodokem976shake/clean/common.h @@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM976SHAKE_CLEAN_key_encode(uint16_t *out, const uint16_t *in void PQCLEAN_FRODOKEM976SHAKE_CLEAN_key_decode(uint16_t *out, const uint16_t *in); void PQCLEAN_FRODOKEM976SHAKE_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); void PQCLEAN_FRODOKEM976SHAKE_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); +int8_t PQCLEAN_FRODOKEM976SHAKE_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len); +void PQCLEAN_FRODOKEM976SHAKE_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector); void PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes(uint8_t *mem, size_t n); uint16_t PQCLEAN_FRODOKEM976SHAKE_CLEAN_LE_TO_UINT16(uint16_t n); uint16_t PQCLEAN_FRODOKEM976SHAKE_CLEAN_UINT16_TO_LE(uint16_t n); diff --git a/crypto_kem/frodokem976shake/clean/kem.c b/crypto_kem/frodokem976shake/clean/kem.c index 1b70711c..ec7c3b63 100644 --- a/crypto_kem/frodokem976shake/clean/kem.c +++ b/crypto_kem/frodokem976shake/clean/kem.c @@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM976SHAKE_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct 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); - } + // If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s) + // Needs to avoid branching on secret data as per: + // Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum + // primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020. + int8_t selector = PQCLEAN_FRODOKEM976SHAKE_CLEAN_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM976SHAKE_CLEAN_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR); + // If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s) + PQCLEAN_FRODOKEM976SHAKE_CLEAN_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector); shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); // Cleanup: diff --git a/crypto_kem/frodokem976shake/clean/util.c b/crypto_kem/frodokem976shake/clean/util.c index a92a24b0..b8246b87 100644 --- a/crypto_kem/frodokem976shake/clean/util.c +++ b/crypto_kem/frodokem976shake/clean/util.c @@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM976SHAKE_CLEAN_unpack(uint16_t *out, size_t outlen, const u } +int8_t PQCLEAN_FRODOKEM976SHAKE_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) { + // Compare two arrays in constant time. + // Returns 0 if the byte arrays are equal, -1 otherwise. + uint16_t r = 0; + + for (size_t i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1); + return (int8_t)r; +} + + +void PQCLEAN_FRODOKEM976SHAKE_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) { + // Select one of the two input arrays to be moved to r + // If (selector == 0) then load r with a, else if (selector == -1) load r with b + + for (size_t i = 0; i < len; i++) { + r[i] = (~selector & a[i]) | (selector & b[i]); + } +} + + void PQCLEAN_FRODOKEM976SHAKE_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. diff --git a/crypto_kem/frodokem976shake/opt/common.h b/crypto_kem/frodokem976shake/opt/common.h index b51cfdf1..07dad62e 100644 --- a/crypto_kem/frodokem976shake/opt/common.h +++ b/crypto_kem/frodokem976shake/opt/common.h @@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM976SHAKE_OPT_key_encode(uint16_t *out, const uint16_t *in); void PQCLEAN_FRODOKEM976SHAKE_OPT_key_decode(uint16_t *out, const uint16_t *in); void PQCLEAN_FRODOKEM976SHAKE_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); void PQCLEAN_FRODOKEM976SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); +int8_t PQCLEAN_FRODOKEM976SHAKE_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len); +void PQCLEAN_FRODOKEM976SHAKE_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector); void PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes(uint8_t *mem, size_t n); uint16_t PQCLEAN_FRODOKEM976SHAKE_OPT_LE_TO_UINT16(uint16_t n); uint16_t PQCLEAN_FRODOKEM976SHAKE_OPT_UINT16_TO_LE(uint16_t n); diff --git a/crypto_kem/frodokem976shake/opt/kem.c b/crypto_kem/frodokem976shake/opt/kem.c index 5156b787..7dae303a 100644 --- a/crypto_kem/frodokem976shake/opt/kem.c +++ b/crypto_kem/frodokem976shake/opt/kem.c @@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM976SHAKE_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, 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); - } + // If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s) + // Needs to avoid branching on secret data as per: + // Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum + // primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020. + int8_t selector = PQCLEAN_FRODOKEM976SHAKE_OPT_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM976SHAKE_OPT_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR); + // If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s) + PQCLEAN_FRODOKEM976SHAKE_OPT_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector); shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); // Cleanup: diff --git a/crypto_kem/frodokem976shake/opt/util.c b/crypto_kem/frodokem976shake/opt/util.c index 47b20190..0ae983c1 100644 --- a/crypto_kem/frodokem976shake/opt/util.c +++ b/crypto_kem/frodokem976shake/opt/util.c @@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM976SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const uin } +int8_t PQCLEAN_FRODOKEM976SHAKE_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) { + // Compare two arrays in constant time. + // Returns 0 if the byte arrays are equal, -1 otherwise. + uint16_t r = 0; + + for (size_t i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1); + return (int8_t)r; +} + + +void PQCLEAN_FRODOKEM976SHAKE_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) { + // Select one of the two input arrays to be moved to r + // If (selector == 0) then load r with a, else if (selector == -1) load r with b + + for (size_t i = 0; i < len; i++) { + r[i] = (~selector & a[i]) | (selector & b[i]); + } +} + + void PQCLEAN_FRODOKEM976SHAKE_OPT_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. From de14914a1ce1219c0a8aacd8e3eff91726237118 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Sun, 21 Jun 2020 16:53:26 -0400 Subject: [PATCH 6/7] Mention FrodoKEM timing leak in SECURITY.md --- SECURITY.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SECURITY.md b/SECURITY.md index a07a5a88..9575607a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -24,6 +24,9 @@ Use at your own risk. ### 2019-XX-XX --> +### 2020-06-19 +* A potential timing leak was present in the FrodoKEM decapsulation routine, as identified by [Guo, Johansson, and Nilsson](https://eprint.iacr.org/2020/743). This was fixed in [PR #303](https://github.com/PQClean/PQClean/pull/303). + ### 2019-09-24 * All Falcon implementations before [PR #235][PR 235] got merged were insecure. See [EPRINT report 2019/893][2019/893]. From 9e4d07dba133f0691e694ca16f657d0f97fa291e Mon Sep 17 00:00:00 2001 From: Thom Wiggers Date: Mon, 22 Jun 2020 04:10:07 +0200 Subject: [PATCH 7/7] Speed up test collection (#298) * don't do filesystem operations during collection * Greatly speed up test collection * fixup! Greatly speed up test collection * Silence junit warning * fixup! Greatly speed up test collection --- test/helpers.py | 45 +++++++++++++++++++++++++-------------------- test/pqclean.py | 15 +++++++++++++-- test/pytest.ini | 1 + 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/test/helpers.py b/test/helpers.py index a8b2a27b..5eee1d29 100644 --- a/test/helpers.py +++ b/test/helpers.py @@ -2,11 +2,13 @@ import atexit import functools import logging import os +import secrets import shutil +import string import subprocess import sys -import tempfile import unittest +from functools import lru_cache import pqclean @@ -22,6 +24,12 @@ def cleanup_testcases(): TEST_TEMPDIRS = [] +ALPHABET = string.ascii_letters + string.digits + '_' +def mktmpdir(parent, prefix): + """Returns a unique directory name""" + uniq = ''.join(secrets.choice(ALPHABET) for i in range(8)) + return os.path.join(parent, "{}_{}".format(prefix, uniq)) + def isolate_test_files(impl_path, test_prefix, dir=os.path.join('..', 'testcases')): @@ -34,24 +42,22 @@ def isolate_test_files(impl_path, test_prefix, os.mkdir(dir) except FileExistsError: pass - test_dir = tempfile.mkdtemp(prefix=test_prefix, dir=dir) + test_dir = mktmpdir(dir, test_prefix) test_dir = os.path.abspath(test_dir) TEST_TEMPDIRS.append(test_dir) - # Create layers in folder structure - nested_dir = os.path.join(test_dir, 'crypto_bla') - os.mkdir(nested_dir) - nested_dir = os.path.join(nested_dir, 'scheme') - os.mkdir(nested_dir) - - # Create test dependencies structure - os.mkdir(os.path.join(test_dir, 'test')) - # the implementation will go here. - new_impl_dir = os.path.abspath(os.path.join(nested_dir, 'impl')) + scheme_dir = os.path.join(test_dir, 'crypto_bla', 'scheme') + new_impl_dir = os.path.abspath(os.path.join(scheme_dir, 'impl')) def initializer(): """Isolate the files to be tested""" + # Create layers in folder structure + os.makedirs(scheme_dir) + + # Create test dependencies structure + os.mkdir(os.path.join(test_dir, 'test')) + # Copy common files (randombytes.c, aes.c, ...) shutil.copytree( os.path.join('..', 'common'), os.path.join(test_dir, 'common')) @@ -160,6 +166,7 @@ def slow_test(f): return wrapper +@lru_cache(maxsize=None) def ensure_available(executable): """ Checks if a command is available. @@ -278,18 +285,16 @@ def filtered_test(func): return wrapper -__CPUINFO = None - - +@lru_cache(maxsize=1) def get_cpu_info(): - global __CPUINFO - while __CPUINFO is None or 'flags' not in __CPUINFO: + the_info = None + while the_info is None or 'flags' not in the_info: import cpuinfo - __CPUINFO = cpuinfo.get_cpu_info() + the_info = cpuinfo.get_cpu_info() # CPUINFO is unreliable on Travis CI Macs if 'CI' in os.environ and sys.platform == 'darwin': - __CPUINFO['flags'] = [ + the_info['flags'] = [ 'aes', 'apic', 'avx1.0', 'clfsh', 'cmov', 'cx16', 'cx8', 'de', 'em64t', 'erms', 'f16c', 'fpu', 'fxsr', 'lahf', 'mca', 'mce', 'mmx', 'mon', 'msr', 'mtrr', 'osxsave', 'pae', 'pat', 'pcid', @@ -299,4 +304,4 @@ def get_cpu_info(): 'tsc_thread_offset', 'tsci', 'tsctmr', 'vme', 'vmm', 'x2apic', 'xd', 'xsave'] - return __CPUINFO + return the_info diff --git a/test/pqclean.py b/test/pqclean.py index 8713b5df..c1c27a89 100644 --- a/test/pqclean.py +++ b/test/pqclean.py @@ -1,6 +1,7 @@ import glob import os from typing import Optional +from functools import lru_cache import yaml import platform @@ -20,6 +21,7 @@ class Scheme: return 'PQCLEAN_{}_'.format(self.name.upper()).replace('-', '') @staticmethod + @lru_cache(maxsize=None) def by_name(scheme_name): for scheme in Scheme.all_schemes(): if scheme.name == scheme_name: @@ -27,6 +29,7 @@ class Scheme: raise KeyError() @staticmethod + @lru_cache(maxsize=1) def all_schemes(): schemes = [] schemes.extend(Scheme.all_schemes_of_type('kem')) @@ -34,6 +37,7 @@ class Scheme: return schemes @staticmethod + @lru_cache(maxsize=1) def all_implementations(): implementations = [] for scheme in Scheme.all_schemes(): @@ -41,11 +45,13 @@ class Scheme: return implementations @staticmethod + @lru_cache(maxsize=1) def all_supported_implementations(): return [impl for impl in Scheme.all_implementations() if impl.supported_on_current_platform()] @staticmethod + @lru_cache(maxsize=32) def all_schemes_of_type(type: str) -> list: schemes = [] p = os.path.join('..', 'crypto_' + type) @@ -60,12 +66,13 @@ class Scheme: assert('Unknown type') return schemes + @lru_cache(maxsize=None) def metadata(self): metafile = os.path.join(self.path(), 'META.yml') try: with open(metafile, encoding='utf-8') as f: - metadata = yaml.safe_load(f.read()) - return metadata + metadata = yaml.safe_load(f) + return metadata except Exception as e: print("Can't open {}: {}".format(metafile, e)) return None @@ -80,6 +87,7 @@ class Implementation: self.scheme = scheme self.name = name + @lru_cache(maxsize=None) def metadata(self): for i in self.scheme.metadata()['implementations']: if i['name'] == self.name: @@ -104,6 +112,7 @@ class Implementation: '*.o' if os.name != 'nt' else '*.obj')) @staticmethod + @lru_cache(maxsize=None) def by_name(scheme_name, implementation_name): scheme = Scheme.by_name(scheme_name) for implementation in scheme.implementations: @@ -112,6 +121,7 @@ class Implementation: raise KeyError() @staticmethod + @lru_cache(maxsize=None) def all_implementations(scheme: Scheme) -> list: implementations = [] for d in os.listdir(scheme.path()): @@ -143,6 +153,7 @@ class Implementation: return True + @lru_cache(maxsize=10000) def supported_on_current_platform(self) -> bool: if 'supported_platforms' not in self.metadata(): return True diff --git a/test/pytest.ini b/test/pytest.ini index 204dfc00..c61c29d1 100644 --- a/test/pytest.ini +++ b/test/pytest.ini @@ -2,3 +2,4 @@ norecursedirs = .git * empty_parameter_set_mark = fail_at_collect junit_log_passing_tests = False +junit_family=xunit2