From 8c5b1cf35679c18895c43d69d72a8267eb0164ba Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Sun, 14 Apr 2019 19:44:26 -0400 Subject: [PATCH 1/7] Add ntruhps2048677 --- crypto_kem/ntruhps2048677/META.yml | 22 ++ crypto_kem/ntruhps2048677/clean/LICENSE | 1 + crypto_kem/ntruhps2048677/clean/Makefile | 19 + .../clean/Makefile.Microsoft_nmake | 19 + crypto_kem/ntruhps2048677/clean/api.h | 19 + crypto_kem/ntruhps2048677/clean/crypto_sort.c | 50 +++ crypto_kem/ntruhps2048677/clean/crypto_sort.h | 6 + crypto_kem/ntruhps2048677/clean/kem.c | 60 +++ crypto_kem/ntruhps2048677/clean/owcpa.c | 174 +++++++++ crypto_kem/ntruhps2048677/clean/owcpa.h | 20 + crypto_kem/ntruhps2048677/clean/pack3.c | 51 +++ crypto_kem/ntruhps2048677/clean/packq.c | 91 +++++ crypto_kem/ntruhps2048677/clean/params.h | 34 ++ crypto_kem/ntruhps2048677/clean/poly.c | 345 ++++++++++++++++++ crypto_kem/ntruhps2048677/clean/poly.h | 38 ++ crypto_kem/ntruhps2048677/clean/sample.c | 54 +++ crypto_kem/ntruhps2048677/clean/sample.h | 16 + crypto_kem/ntruhps2048677/clean/verify.c | 29 ++ crypto_kem/ntruhps2048677/clean/verify.h | 12 + 19 files changed, 1060 insertions(+) create mode 100644 crypto_kem/ntruhps2048677/META.yml create mode 100644 crypto_kem/ntruhps2048677/clean/LICENSE create mode 100644 crypto_kem/ntruhps2048677/clean/Makefile create mode 100644 crypto_kem/ntruhps2048677/clean/Makefile.Microsoft_nmake create mode 100644 crypto_kem/ntruhps2048677/clean/api.h create mode 100644 crypto_kem/ntruhps2048677/clean/crypto_sort.c create mode 100644 crypto_kem/ntruhps2048677/clean/crypto_sort.h create mode 100644 crypto_kem/ntruhps2048677/clean/kem.c create mode 100644 crypto_kem/ntruhps2048677/clean/owcpa.c create mode 100644 crypto_kem/ntruhps2048677/clean/owcpa.h create mode 100644 crypto_kem/ntruhps2048677/clean/pack3.c create mode 100644 crypto_kem/ntruhps2048677/clean/packq.c create mode 100644 crypto_kem/ntruhps2048677/clean/params.h create mode 100644 crypto_kem/ntruhps2048677/clean/poly.c create mode 100644 crypto_kem/ntruhps2048677/clean/poly.h create mode 100644 crypto_kem/ntruhps2048677/clean/sample.c create mode 100644 crypto_kem/ntruhps2048677/clean/sample.h create mode 100644 crypto_kem/ntruhps2048677/clean/verify.c create mode 100644 crypto_kem/ntruhps2048677/clean/verify.h diff --git a/crypto_kem/ntruhps2048677/META.yml b/crypto_kem/ntruhps2048677/META.yml new file mode 100644 index 00000000..2f017e52 --- /dev/null +++ b/crypto_kem/ntruhps2048677/META.yml @@ -0,0 +1,22 @@ +name: ntru-hps2048677 +type: kem +claimed-nist-level: 1 +length-public-key: 930 +length-ciphertext: 930 +length-shared-secret: 32 +testvectors-sha256: 949f3ff44551abe3efd08e3527dd72a0bfba1df50deb49f619becf8b19ac283b +nistkat-sha256: 715a5caf1ee22bb4b75ff6b10f911fec77e0d63378ea359c0773ee0a4c6cbb97 +principal-submitter: John M. Schanck +auxiliary-submitters: + - Cong Chen + - Oussama Danba + - Jeffrey Hoffstein + - Andreas Hülsing + - Joost Rijneveld + - Peter Schwabe + - William Whyte + - Zhenfei Zhang +implementations: + - name: clean + version: https://csrc.nist.gov/CSRC/media/Projects/Post-Quantum-Cryptography/documents/round-2/submissions/NTRU-Round2.zip reference implemntation + length-secret-key: 1234 diff --git a/crypto_kem/ntruhps2048677/clean/LICENSE b/crypto_kem/ntruhps2048677/clean/LICENSE new file mode 100644 index 00000000..d5d21fff --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/LICENSE @@ -0,0 +1 @@ +Public Domain diff --git a/crypto_kem/ntruhps2048677/clean/Makefile b/crypto_kem/ntruhps2048677/clean/Makefile new file mode 100644 index 00000000..22ecc1f7 --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/Makefile @@ -0,0 +1,19 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libntruhps2048677_clean.a +HEADERS=api.h crypto_sort.h owcpa.h params.h poly.h sample.h verify.h +OBJECTS=crypto_sort.o kem.o owcpa.o pack3.o packq.o poly.o sample.o verify.o + +CFLAGS=-Wall -Wextra -Wpedantic -Werror -Wmissing-prototypes -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/ntruhps2048677/clean/Makefile.Microsoft_nmake b/crypto_kem/ntruhps2048677/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..31bef0b6 --- /dev/null +++ b/crypto_kem/ntruhps2048677/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=libntruhps2048677_clean.lib +OBJECTS=crypto_sort.obj kem.obj owcpa.obj pack3.obj packq.obj poly.obj sample.obj verify.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/ntruhps2048677/clean/api.h b/crypto_kem/ntruhps2048677/clean/api.h new file mode 100644 index 00000000..6a2042b5 --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/api.h @@ -0,0 +1,19 @@ +#ifndef PQCLEAN_NTRUHPS2048677_CLEAN_API_H +#define PQCLEAN_NTRUHPS2048677_CLEAN_API_H + +#include + +#define PQCLEAN_NTRUHPS2048677_CLEAN_CRYPTO_SECRETKEYBYTES 1234 +#define PQCLEAN_NTRUHPS2048677_CLEAN_CRYPTO_PUBLICKEYBYTES 930 +#define PQCLEAN_NTRUHPS2048677_CLEAN_CRYPTO_CIPHERTEXTBYTES 930 +#define PQCLEAN_NTRUHPS2048677_CLEAN_CRYPTO_BYTES 32 + +#define PQCLEAN_NTRUHPS2048677_CLEAN_CRYPTO_ALGNAME "NTRU-HPS2048677" + +int PQCLEAN_NTRUHPS2048677_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); + +int PQCLEAN_NTRUHPS2048677_CLEAN_crypto_kem_enc(uint8_t *c, uint8_t *k, const uint8_t *pk); + +int PQCLEAN_NTRUHPS2048677_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, const uint8_t *sk); + +#endif diff --git a/crypto_kem/ntruhps2048677/clean/crypto_sort.c b/crypto_kem/ntruhps2048677/clean/crypto_sort.c new file mode 100644 index 00000000..7b36fa70 --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/crypto_sort.c @@ -0,0 +1,50 @@ +// XXX: Temporary placeholder for a faster sort. +// Copied from supercop-20190110/crypto_sort/int32/portable3 + +#include + +#include "crypto_sort.h" + +#define int32_MINMAX(a,b) \ + do { \ + int32_t ab = (b) ^ (a); \ + int32_t c = (b) - (a); \ + c ^= ab & (c ^ (b)); \ + c >>= 31; \ + c &= ab; \ + (a) ^= c; \ + (b) ^= c; \ + } while(0) + +void PQCLEAN_NTRUHPS2048677_CLEAN_crypto_sort(void *array, long long n) { + long long top, p, q, r, i; + int32_t *x = array; + + if (n < 2) { + return; + } + top = 1; + while (top < n - top) { + top += top; + } + + for (p = top; p > 0; p >>= 1) { + for (i = 0; i < n - p; ++i) { + if (!(i & p)) { + int32_MINMAX(x[i], x[i + p]); + } + } + i = 0; + for (q = top; q > p; q >>= 1) { + for (; i < n - q; ++i) { + if (!(i & p)) { + int32_t a = x[i + p]; + for (r = q; r > p; r >>= 1) { + int32_MINMAX(a, x[i + r]); + } + x[i + p] = a; + } + } + } + } +} diff --git a/crypto_kem/ntruhps2048677/clean/crypto_sort.h b/crypto_kem/ntruhps2048677/clean/crypto_sort.h new file mode 100644 index 00000000..ac07efb6 --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/crypto_sort.h @@ -0,0 +1,6 @@ +#ifndef CRYPTO_SORT +#define CRYPTO_SORT + +void PQCLEAN_NTRUHPS2048677_CLEAN_crypto_sort(void *array, long long n); + +#endif diff --git a/crypto_kem/ntruhps2048677/clean/kem.c b/crypto_kem/ntruhps2048677/clean/kem.c new file mode 100644 index 00000000..80123ea1 --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/kem.c @@ -0,0 +1,60 @@ +#include + +#include "api.h" +#include "fips202.h" +#include "owcpa.h" +#include "params.h" +#include "randombytes.h" +#include "verify.h" + +// API FUNCTIONS +int PQCLEAN_NTRUHPS2048677_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + uint8_t seed[NTRU_SAMPLE_FG_BYTES]; + + randombytes(seed, NTRU_SAMPLE_FG_BYTES); + PQCLEAN_NTRUHPS2048677_CLEAN_owcpa_keypair(pk, sk, seed); + + randombytes(sk + NTRU_OWCPA_SECRETKEYBYTES, NTRU_PRFKEYBYTES); + + return 0; +} + +int PQCLEAN_NTRUHPS2048677_CLEAN_crypto_kem_enc(uint8_t *c, uint8_t *k, const uint8_t *pk) { + uint8_t rm[NTRU_OWCPA_MSGBYTES]; + uint8_t rm_seed[NTRU_SAMPLE_RM_BYTES]; + + randombytes(rm_seed, NTRU_SAMPLE_RM_BYTES); + PQCLEAN_NTRUHPS2048677_CLEAN_owcpa_samplemsg(rm, rm_seed); + + sha3_256(k, rm, NTRU_OWCPA_MSGBYTES); + + PQCLEAN_NTRUHPS2048677_CLEAN_owcpa_enc(c, rm, pk); + + return 0; +} + +int PQCLEAN_NTRUHPS2048677_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, const uint8_t *sk) { + int i, fail; + uint8_t rm[NTRU_OWCPA_MSGBYTES]; + uint8_t buf[NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES]; + uint8_t *cmp = buf + NTRU_PRFKEYBYTES; + + fail = PQCLEAN_NTRUHPS2048677_CLEAN_owcpa_dec(rm, c, sk); + /* If fail = 0 then c = Enc(h, rm), there is no need to re-encapsulate. */ + /* See comment in PQCLEAN_NTRUHPS2048677_CLEAN_owcpa_dec for details. */ + + sha3_256(k, rm, NTRU_OWCPA_MSGBYTES); + + /* shake(secret PRF key || input ciphertext) */ + for (i = 0; i < NTRU_PRFKEYBYTES; i++) { + buf[i] = sk[i + NTRU_OWCPA_SECRETKEYBYTES]; + } + for (i = 0; i < NTRU_CIPHERTEXTBYTES; i++) { + cmp[i] = c[i]; + } + sha3_256(rm, cmp, NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES); + + PQCLEAN_NTRUHPS2048677_CLEAN_cmov(k, rm, NTRU_SHAREDKEYBYTES, (unsigned char) fail); + + return 0; +} diff --git a/crypto_kem/ntruhps2048677/clean/owcpa.c b/crypto_kem/ntruhps2048677/clean/owcpa.c new file mode 100644 index 00000000..aaa02b85 --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/owcpa.c @@ -0,0 +1,174 @@ +#include "owcpa.h" +#include "poly.h" +#include "sample.h" + +static int owcpa_check_r(const poly *r) { + /* Check that r is in message space. */ + /* Note: Assumes that r has coefficients in {0, 1, ..., q-1} */ + int i; + uint64_t t = 0; + uint16_t c; + for (i = 0; i < NTRU_N; i++) { + c = MODQ(r->coeffs[i] + 1); + t |= c & (NTRU_Q - 4); /* 0 if c is in {0,1,2,3} */ + t |= (c + 1) & 0x4; /* 0 if c is in {0,1,2} */ + } + t |= r->coeffs[NTRU_N - 1]; /* Coefficient n-1 must be zero */ + t = (~t + 1); // two's complement + t >>= 63; + return (int) t; +} + +static int owcpa_check_m(const poly *m) { + /* Check that m is in message space. */ + /* Note: Assumes that m has coefficients in {0,1,2}. */ + int i; + uint64_t t = 0; + uint16_t p1 = 0; + uint16_t m1 = 0; + for (i = 0; i < NTRU_N; i++) { + p1 += m->coeffs[i] & 0x01; + m1 += (m->coeffs[i] & 0x02) >> 1; + } + /* Need p1 = m1 and p1 + m1 = NTRU_WEIGHT */ + t |= p1 ^ m1; + t |= (p1 + m1) ^ NTRU_WEIGHT; + t = (~t + 1); // two's complement + t >>= 63; + return (int) t; +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_owcpa_samplemsg(unsigned char msg[NTRU_OWCPA_MSGBYTES], + const unsigned char seed[NTRU_SAMPLE_RM_BYTES]) { + poly r, m; + + PQCLEAN_NTRUHPS2048677_CLEAN_sample_rm(&r, &m, seed); + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_tobytes(msg, &r); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_tobytes(msg + NTRU_PACK_TRINARY_BYTES, &m); +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_owcpa_keypair(unsigned char *pk, + unsigned char *sk, + const unsigned char seed[NTRU_SAMPLE_FG_BYTES]) { + int i; + + poly x1, x2, x3, x4, x5; + + poly *f = &x1, *invf_mod3 = &x2; + poly *g = &x3, *G = &x2; + poly *Gf = &x3, *invGf = &x4, *tmp = &x5; + poly *invh = &x3, *h = &x3; + + PQCLEAN_NTRUHPS2048677_CLEAN_sample_fg(f, g, seed); + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_inv(invf_mod3, f); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_tobytes(sk, f); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_tobytes(sk + NTRU_PACK_TRINARY_BYTES, invf_mod3); + + /* Lift coeffs of f and g from Z_p to Z_q */ + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Z3_to_Zq(f); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Z3_to_Zq(g); + + /* G = 3*g */ + for (i = 0; i < NTRU_N; i++) { + G->coeffs[i] = MODQ(3 * g->coeffs[i]); + } + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(Gf, G, f); + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_inv(invGf, Gf); + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(tmp, invGf, f); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Sq_mul(invh, tmp, f); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Sq_tobytes(sk + 2 * NTRU_PACK_TRINARY_BYTES, invh); + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(tmp, invGf, G); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(h, tmp, G); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_sum_zero_tobytes(pk, h); +} + + +void PQCLEAN_NTRUHPS2048677_CLEAN_owcpa_enc(unsigned char *c, + const unsigned char *rm, + const unsigned char *pk) { + int i; + poly x1, x2, x3; + poly *h = &x1, *liftm = &x1; + poly *r = &x2, *m = &x2; + poly *ct = &x3; + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_sum_zero_frombytes(h, pk); + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_frombytes(r, rm); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Z3_to_Zq(r); + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(ct, r, h); + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_frombytes(m, rm + NTRU_PACK_TRINARY_BYTES); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_lift(liftm, m); + for (i = 0; i < NTRU_N; i++) { + ct->coeffs[i] = MODQ(ct->coeffs[i] + liftm->coeffs[i]); + } + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_sum_zero_tobytes(c, ct); +} + +int PQCLEAN_NTRUHPS2048677_CLEAN_owcpa_dec(unsigned char *rm, + const unsigned char *ciphertext, + const unsigned char *secretkey) { + int i; + int fail; + poly x1, x2, x3, x4; + + poly *c = &x1, *f = &x2, *cf = &x3; + poly *mf = &x2, *finv3 = &x3, *m = &x4; + poly *liftm = &x2, *invh = &x3, *r = &x4; + poly *b = &x1; + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_sum_zero_frombytes(c, ciphertext); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_frombytes(f, secretkey); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Z3_to_Zq(f); + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(cf, c, f); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_to_S3(mf, cf); + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_frombytes(finv3, secretkey + NTRU_PACK_TRINARY_BYTES); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_mul(m, mf, finv3); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_tobytes(rm + NTRU_PACK_TRINARY_BYTES, m); + + /* NOTE: For the IND-CCA2 KEM we must ensure that c = Enc(h, (r,m)). */ + /* We can avoid re-computing r*h + Lift(m) as long as we check that */ + /* r (defined as b/h mod (q, Phi_n)) and m are in the message space. */ + /* (m can take any value in S3 in NTRU_HRSS) */ + fail = 0; + fail |= owcpa_check_m(m); + + /* b = c - Lift(m) mod (q, x^n - 1) */ + PQCLEAN_NTRUHPS2048677_CLEAN_poly_lift(liftm, m); + for (i = 0; i < NTRU_N; i++) { + b->coeffs[i] = MODQ(c->coeffs[i] - liftm->coeffs[i]); + } + + /* r = b / h mod (q, Phi_n) */ + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Sq_frombytes(invh, secretkey + 2 * NTRU_PACK_TRINARY_BYTES); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Sq_mul(r, b, invh); + + /* NOTE: Our definition of r as b/h mod (q, Phi_n) follows Figure 4 of */ + /* [Sch18] https://eprint.iacr.org/2018/1174/20181203:032458. */ + /* This differs from Figure 10 of Saito--Xagawa--Yamakawa */ + /* [SXY17] https://eprint.iacr.org/2017/1005/20180516:055500 */ + /* where r gets a final reduction modulo p. */ + /* We need this change to use Proposition 1 of [Sch18]. */ + + /* Proposition 1 of [Sch18] shows that re-encryption with (r,m) yields c. */ + /* if and only if fail==0 after the following call to owcpa_check_r */ + /* The procedure given in Fig. 8 of [Sch18] can be skipped because we have */ + /* c(1) = 0 due to the use of poly_Rq_sum_zero_{to,from}bytes. */ + fail |= owcpa_check_r(r); + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_trinary_Zq_to_Z3(r); + PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_tobytes(rm, r); + + return fail; +} diff --git a/crypto_kem/ntruhps2048677/clean/owcpa.h b/crypto_kem/ntruhps2048677/clean/owcpa.h new file mode 100644 index 00000000..6d63915d --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/owcpa.h @@ -0,0 +1,20 @@ +#ifndef OWCPA_H +#define OWCPA_H + +#include "params.h" + +void PQCLEAN_NTRUHPS2048677_CLEAN_owcpa_samplemsg(unsigned char msg[NTRU_OWCPA_MSGBYTES], + const unsigned char seed[NTRU_SEEDBYTES]); + +void PQCLEAN_NTRUHPS2048677_CLEAN_owcpa_keypair(unsigned char *pk, + unsigned char *sk, + const unsigned char seed[NTRU_SEEDBYTES]); + +void PQCLEAN_NTRUHPS2048677_CLEAN_owcpa_enc(unsigned char *c, + const unsigned char *rm, + const unsigned char *pk); + +int PQCLEAN_NTRUHPS2048677_CLEAN_owcpa_dec(unsigned char *rm, + const unsigned char *ciphertext, + const unsigned char *secretkey); +#endif diff --git a/crypto_kem/ntruhps2048677/clean/pack3.c b/crypto_kem/ntruhps2048677/clean/pack3.c new file mode 100644 index 00000000..121fd44b --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/pack3.c @@ -0,0 +1,51 @@ +#include "poly.h" + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_tobytes(unsigned char msg[NTRU_OWCPA_MSGBYTES], const poly *a) { + int i; + unsigned char c; + int j; + + for (i = 0; i < NTRU_PACK_DEG / 5; i++) { + c = a->coeffs[5 * i + 4] & 255; + c = (3 * c + a->coeffs[5 * i + 3]) & 255; + c = (3 * c + a->coeffs[5 * i + 2]) & 255; + c = (3 * c + a->coeffs[5 * i + 1]) & 255; + c = (3 * c + a->coeffs[5 * i + 0]) & 255; + msg[i] = c; + } + + // if ((NTRU_N - 1) % 5 != 0) + i = NTRU_PACK_DEG / 5; + c = 0; + for (j = NTRU_PACK_DEG - (5 * i) - 1; j >= 0; j--) { + c = (3 * c + a->coeffs[5 * i + j]) & 255; + } + msg[i] = c; + +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_frombytes(poly *r, const unsigned char msg[NTRU_OWCPA_MSGBYTES]) { + int i; + unsigned char c; + int j; + + for (i = 0; i < NTRU_PACK_DEG / 5; i++) { + c = msg[i]; + r->coeffs[5 * i + 0] = PQCLEAN_NTRUHPS2048677_CLEAN_mod3(c); + r->coeffs[5 * i + 1] = PQCLEAN_NTRUHPS2048677_CLEAN_mod3(c * 171 >> 9); // this is division by 3 + r->coeffs[5 * i + 2] = PQCLEAN_NTRUHPS2048677_CLEAN_mod3(c * 57 >> 9); // division by 3^2 + r->coeffs[5 * i + 3] = PQCLEAN_NTRUHPS2048677_CLEAN_mod3(c * 19 >> 9); // division by 3^3 + r->coeffs[5 * i + 4] = PQCLEAN_NTRUHPS2048677_CLEAN_mod3(c * 203 >> 14); // etc. + } + + // if ((NTRU_N - 1) % 5 != 0) + i = NTRU_PACK_DEG / 5; + c = msg[i]; + for (j = 0; (5 * i + j) < NTRU_PACK_DEG; j++) { + r->coeffs[5 * i + j] = PQCLEAN_NTRUHPS2048677_CLEAN_mod3(c); + c = c * 171 >> 9; + } + + r->coeffs[NTRU_N - 1] = 0; +} + diff --git a/crypto_kem/ntruhps2048677/clean/packq.c b/crypto_kem/ntruhps2048677/clean/packq.c new file mode 100644 index 00000000..759b6b16 --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/packq.c @@ -0,0 +1,91 @@ +#include "poly.h" + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Sq_tobytes(unsigned char *r, const poly *a) { + int i, j; + uint16_t t[8]; + + for (i = 0; i < NTRU_PACK_DEG / 8; i++) { + for (j = 0; j < 8; j++) { + t[j] = a->coeffs[8 * i + j]; + } + + r[11 * i + 0] = (unsigned char) ( t[0] & 0xff); + r[11 * i + 1] = (unsigned char) ((t[0] >> 8) | ((t[1] & 0x1f) << 3)); + r[11 * i + 2] = (unsigned char) ((t[1] >> 5) | ((t[2] & 0x03) << 6)); + r[11 * i + 3] = (unsigned char) ((t[2] >> 2) & 0xff); + r[11 * i + 4] = (unsigned char) ((t[2] >> 10) | ((t[3] & 0x7f) << 1)); + r[11 * i + 5] = (unsigned char) ((t[3] >> 7) | ((t[4] & 0x0f) << 4)); + r[11 * i + 6] = (unsigned char) ((t[4] >> 4) | ((t[5] & 0x01) << 7)); + r[11 * i + 7] = (unsigned char) ((t[5] >> 1) & 0xff); + r[11 * i + 8] = (unsigned char) ((t[5] >> 9) | ((t[6] & 0x3f) << 2)); + r[11 * i + 9] = (unsigned char) ((t[6] >> 6) | ((t[7] & 0x07) << 5)); + r[11 * i + 10] = (unsigned char) ((t[7] >> 3)); + } + + for (j = 0; j < NTRU_PACK_DEG - 8 * i; j++) { + t[j] = a->coeffs[8 * i + j]; + } + for (; j < 8; j++) { + t[j] = 0; + } + + switch (NTRU_PACK_DEG - 8 * (NTRU_PACK_DEG / 8)) { + case 6: + r[11 * i + 8] = (unsigned char) ((t[5] >> 9) | ((t[6] & 0x3f) << 2)); + r[11 * i + 7] = (unsigned char) ((t[5] >> 1) & 0xff); + r[11 * i + 6] = (unsigned char) ((t[4] >> 4) | ((t[5] & 0x01) << 7)); + // fallthrough + case 4: + r[11 * i + 5] = (unsigned char) ((t[3] >> 7) | ((t[4] & 0x0f) << 4)); + r[11 * i + 4] = (unsigned char) ((t[2] >> 10) | ((t[3] & 0x7f) << 1)); + r[11 * i + 3] = (unsigned char) ((t[2] >> 2) & 0xff); + // fallthrough + case 2: + r[11 * i + 2] = (unsigned char) ((t[1] >> 5) | ((t[2] & 0x03) << 6)); + r[11 * i + 1] = (unsigned char) ((t[0] >> 8) | ((t[1] & 0x1f) << 3)); + r[11 * i + 0] = (unsigned char) ( t[0] & 0xff); + } +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Sq_frombytes(poly *r, const unsigned char *a) { + int i; + for (i = 0; i < NTRU_PACK_DEG / 8; i++) { + r->coeffs[8 * i + 0] = (a[11 * i + 0] >> 0) | (((uint16_t)a[11 * i + 1] & 0x07) << 8); + r->coeffs[8 * i + 1] = (a[11 * i + 1] >> 3) | (((uint16_t)a[11 * i + 2] & 0x3f) << 5); + r->coeffs[8 * i + 2] = (a[11 * i + 2] >> 6) | (((uint16_t)a[11 * i + 3] & 0xff) << 2) | (((uint16_t)a[11 * i + 4] & 0x01) << 10); + r->coeffs[8 * i + 3] = (a[11 * i + 4] >> 1) | (((uint16_t)a[11 * i + 5] & 0x0f) << 7); + r->coeffs[8 * i + 4] = (a[11 * i + 5] >> 4) | (((uint16_t)a[11 * i + 6] & 0x7f) << 4); + r->coeffs[8 * i + 5] = (a[11 * i + 6] >> 7) | (((uint16_t)a[11 * i + 7] & 0xff) << 1) | (((uint16_t)a[11 * i + 8] & 0x03) << 9); + r->coeffs[8 * i + 6] = (a[11 * i + 8] >> 2) | (((uint16_t)a[11 * i + 9] & 0x1f) << 6); + r->coeffs[8 * i + 7] = (a[11 * i + 9] >> 5) | (((uint16_t)a[11 * i + 10] & 0xff) << 3); + } + switch (NTRU_PACK_DEG - 8 * (NTRU_PACK_DEG / 8)) { + case 6: + r->coeffs[8 * i + 5] = (a[11 * i + 6] >> 7) | (((uint16_t)a[11 * i + 7] & 0xff) << 1) | (((uint16_t)a[11 * i + 8] & 0x03) << 9); + r->coeffs[8 * i + 4] = (a[11 * i + 5] >> 4) | (((uint16_t)a[11 * i + 6] & 0x7f) << 4); + // fallthrough + case 4: + r->coeffs[8 * i + 3] = (a[11 * i + 4] >> 1) | (((uint16_t)a[11 * i + 5] & 0x0f) << 7); + r->coeffs[8 * i + 2] = (a[11 * i + 2] >> 6) | (((uint16_t)a[11 * i + 3] & 0xff) << 2) | (((uint16_t)a[11 * i + 4] & 0x01) << 10); + // fallthrough + case 2: + r->coeffs[8 * i + 1] = (a[11 * i + 1] >> 3) | (((uint16_t)a[11 * i + 2] & 0x3f) << 5); + r->coeffs[8 * i + 0] = (a[11 * i + 0] >> 0) | (((uint16_t)a[11 * i + 1] & 0x07) << 8); + } +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_sum_zero_tobytes(unsigned char *r, const poly *a) { + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Sq_tobytes(r, a); +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_sum_zero_frombytes(poly *r, const unsigned char *a) { + int i; + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Sq_frombytes(r, a); + + /* Set r[n-1] so that the sum of coefficients is zero mod q */ + r->coeffs[NTRU_N - 1] = 0; + for (i = 0; i < NTRU_PACK_DEG; i++) { + r->coeffs[NTRU_N - 1] += r->coeffs[i]; + } + r->coeffs[NTRU_N - 1] = MODQ(-(r->coeffs[NTRU_N - 1])); +} diff --git a/crypto_kem/ntruhps2048677/clean/params.h b/crypto_kem/ntruhps2048677/clean/params.h new file mode 100644 index 00000000..c11f6f12 --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/params.h @@ -0,0 +1,34 @@ +#ifndef PARAMS_H +#define PARAMS_H + +#define NTRU_HPS +#define NTRU_N 677 +#define NTRU_LOGQ 11 + +/* Do not modify below this line */ + +#define NTRU_Q (1 << NTRU_LOGQ) +#define NTRU_WEIGHT (NTRU_Q/8 - 2) + +#define NTRU_SEEDBYTES 32 +#define NTRU_PRFKEYBYTES 32 +#define NTRU_SHAREDKEYBYTES 32 + +#define NTRU_SAMPLE_IID_BYTES (NTRU_N-1) +#define NTRU_SAMPLE_FT_BYTES ((30*(NTRU_N-1)+7)/8) +#define NTRU_SAMPLE_FG_BYTES (NTRU_SAMPLE_IID_BYTES+NTRU_SAMPLE_FT_BYTES) +#define NTRU_SAMPLE_RM_BYTES (NTRU_SAMPLE_IID_BYTES+NTRU_SAMPLE_FT_BYTES) + +#define NTRU_PACK_DEG (NTRU_N-1) +#define NTRU_PACK_TRINARY_BYTES ((NTRU_PACK_DEG+4)/5) + +#define NTRU_OWCPA_MSGBYTES (2*NTRU_PACK_TRINARY_BYTES) +#define NTRU_OWCPA_PUBLICKEYBYTES ((NTRU_LOGQ*NTRU_PACK_DEG+7)/8) +#define NTRU_OWCPA_SECRETKEYBYTES (2*NTRU_PACK_TRINARY_BYTES + NTRU_OWCPA_PUBLICKEYBYTES) +#define NTRU_OWCPA_BYTES ((NTRU_LOGQ*NTRU_PACK_DEG+7)/8) + +#define NTRU_PUBLICKEYBYTES (NTRU_OWCPA_PUBLICKEYBYTES) +#define NTRU_SECRETKEYBYTES (NTRU_OWCPA_SECRETKEYBYTES + NTRU_PRFKEYBYTES) +#define NTRU_CIPHERTEXTBYTES (NTRU_OWCPA_BYTES) + +#endif diff --git a/crypto_kem/ntruhps2048677/clean/poly.c b/crypto_kem/ntruhps2048677/clean/poly.c new file mode 100644 index 00000000..60ae348c --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/poly.c @@ -0,0 +1,345 @@ +#include "poly.h" +#include "fips202.h" +#include "verify.h" + +uint16_t PQCLEAN_NTRUHPS2048677_CLEAN_mod3(uint16_t a) { + uint16_t r; + int16_t t, c; + + r = (a >> 8) + (a & 0xff); // r mod 255 == a mod 255 + r = (r >> 4) + (r & 0xf); // r' mod 15 == r mod 15 + r = (r >> 2) + (r & 0x3); // r' mod 3 == r mod 3 + r = (r >> 2) + (r & 0x3); // r' mod 3 == r mod 3 + + t = r - 3; + c = t >> 15; + + return (c & r) ^ (~c & t); +} + +/* Map {0, 1, 2} -> {0,1,q-1} in place */ +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Z3_to_Zq(poly *r) { + int i; + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = r->coeffs[i] | ((-(r->coeffs[i] >> 1)) & (NTRU_Q - 1)); + } +} + +/* Map {0, 1, q-1} -> {0,1,2} in place */ +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_trinary_Zq_to_Z3(poly *r) { + int i; + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = 3 & (r->coeffs[i] ^ (r->coeffs[i] >> (NTRU_LOGQ - 1))); + } +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(poly *r, const poly *a, const poly *b) { + int k, i; + + for (k = 0; k < NTRU_N; k++) { + r->coeffs[k] = 0; + for (i = 1; i < NTRU_N - k; i++) { + r->coeffs[k] += a->coeffs[k + i] * b->coeffs[NTRU_N - i]; + } + for (i = 0; i < k + 1; i++) { + r->coeffs[k] += a->coeffs[k - i] * b->coeffs[i]; + } + r->coeffs[k] = MODQ(r->coeffs[k]); + } +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Sq_mul(poly *r, const poly *a, const poly *b) { + int i; + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(r, a, b); + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = MODQ(r->coeffs[i] - r->coeffs[NTRU_N - 1]); + } +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_mul(poly *r, const poly *a, const poly *b) { + int k, i; + + for (k = 0; k < NTRU_N; k++) { + r->coeffs[k] = 0; + for (i = 1; i < NTRU_N - k; i++) { + r->coeffs[k] += a->coeffs[k + i] * b->coeffs[NTRU_N - i]; + } + for (i = 0; i < k + 1; i++) { + r->coeffs[k] += a->coeffs[k - i] * b->coeffs[i]; + } + } + for (k = 0; k < NTRU_N; k++) { + r->coeffs[k] = PQCLEAN_NTRUHPS2048677_CLEAN_mod3(r->coeffs[k] + 2 * r->coeffs[NTRU_N - 1]); + } +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul_x_minus_1(poly *r, const poly *a) { + int i; + uint16_t last_coeff = a->coeffs[NTRU_N - 1]; + + for (i = NTRU_N - 1; i > 0; i--) { + r->coeffs[i] = MODQ(a->coeffs[i - 1] + (NTRU_Q - a->coeffs[i])); + } + r->coeffs[0] = MODQ(last_coeff + (NTRU_Q - a->coeffs[0])); +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_lift(poly *r, const poly *a) { + int i; + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = a->coeffs[i]; + } + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Z3_to_Zq(r); +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_to_S3(poly *r, const poly *a) { + /* NOTE: Assumes input is in [0,Q-1]^N */ + /* Produces output in {0,1,2}^N */ + int i; + + /* Center coeffs around 3Q: [0, Q-1] -> [3Q - Q/2, 3Q + Q/2) */ + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = ((a->coeffs[i] >> (NTRU_LOGQ - 1)) ^ 3) << NTRU_LOGQ; + r->coeffs[i] += a->coeffs[i]; + } + /* Reduce mod (3, Phi) */ + r->coeffs[NTRU_N - 1] = PQCLEAN_NTRUHPS2048677_CLEAN_mod3(r->coeffs[NTRU_N - 1]); + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = PQCLEAN_NTRUHPS2048677_CLEAN_mod3(r->coeffs[i] + 2 * r->coeffs[NTRU_N - 1]); + } +} + +#define POLY_R2_ADD(I,A,B,S) \ + for ((I)=0; (I)coeffs[i] ^ b->coeffs[i]) & swap; + a->coeffs[i] ^= t; + b->coeffs[i] ^= t; + } +} + +static inline void poly_divx(poly *a, int s) { + int i; + + for (i = 1; i < NTRU_N; i++) { + a->coeffs[i - 1] = (unsigned char) ((s * a->coeffs[i]) | (!s * a->coeffs[i - 1])); + } + a->coeffs[NTRU_N - 1] = (!s * a->coeffs[NTRU_N - 1]); +} + +static inline void poly_mulx(poly *a, int s) { + int i; + + for (i = 1; i < NTRU_N; i++) { + a->coeffs[NTRU_N - i] = (unsigned char) ((s * a->coeffs[NTRU_N - i - 1]) | (!s * a->coeffs[NTRU_N - i])); + } + a->coeffs[0] = (!s * a->coeffs[0]); +} + +static void poly_R2_inv(poly *r, const poly *a) { + /* Schroeppel--Orman--O'Malley--Spatscheck + * "Almost Inverse" algorithm as described + * by Silverman in NTRU Tech Report #14 */ + // with several modifications to make it run in constant-time + int i, j; + int k = 0; + uint16_t degf = NTRU_N - 1; + uint16_t degg = NTRU_N - 1; + int sign, t, swap; + int16_t done = 0; + poly b, f, g; + poly *c = r; // save some stack space + poly *temp_r = &f; + + /* b(X) := 1 */ + for (i = 1; i < NTRU_N; i++) { + b.coeffs[i] = 0; + } + b.coeffs[0] = 1; + + /* c(X) := 0 */ + for (i = 0; i < NTRU_N; i++) { + c->coeffs[i] = 0; + } + + /* f(X) := a(X) */ + for (i = 0; i < NTRU_N; i++) { + f.coeffs[i] = a->coeffs[i] & 1; + } + + /* g(X) := 1 + X + X^2 + ... + X^{N-1} */ + for (i = 0; i < NTRU_N; i++) { + g.coeffs[i] = 1; + } + + for (j = 0; j < 2 * (NTRU_N - 1) - 1; j++) { + sign = f.coeffs[0]; + swap = sign & !done & ((degf - degg) >> 15); + + cswappoly(&f, &g, swap); + cswappoly(&b, c, swap); + t = (degf ^ degg) & (-swap); + degf ^= t; + degg ^= t; + + POLY_R2_ADD(i, f, g, sign * (!done)); + POLY_R2_ADD(i, b, (*c), sign * (!done)); + + poly_divx(&f, !done); + poly_mulx(c, !done); + degf -= !done; + k += !done; + + done = 1 - (((uint16_t) - degf) >> 15); + } + + k = k - NTRU_N * ((uint16_t)(NTRU_N - k - 1) >> 15); + + /* Return X^{N-k} * b(X) */ + /* This is a k-coefficient rotation. We do this by looking at the binary + representation of k, rotating for every power of 2, and performing a cmov + if the respective bit is set. */ + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = b.coeffs[i]; + } + + for (i = 0; i < 10; i++) { + for (j = 0; j < NTRU_N; j++) { + temp_r->coeffs[j] = r->coeffs[(j + (1 << i)) % NTRU_N]; + } + PQCLEAN_NTRUHPS2048677_CLEAN_cmov((unsigned char *) & (r->coeffs), + (unsigned char *) & (temp_r->coeffs), sizeof(uint16_t) * NTRU_N, k & 1); + k >>= 1; + } +} + +static void poly_R2_inv_to_Rq_inv(poly *r, const poly *ai, const poly *a) { + + int i; + poly b, c; + poly s; + + // for 0..4 + // ai = ai * (2 - a*ai) mod q + for (i = 0; i < NTRU_N; i++) { + b.coeffs[i] = MODQ(NTRU_Q - a->coeffs[i]); // b = -a + } + + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = ai->coeffs[i]; + } + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(&c, r, &b); + c.coeffs[0] += 2; // c = 2 - a*ai + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(&s, &c, r); // s = ai*c + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(&c, &s, &b); + c.coeffs[0] += 2; // c = 2 - a*s + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(r, &c, &s); // r = s*c + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(&c, r, &b); + c.coeffs[0] += 2; // c = 2 - a*r + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(&s, &c, r); // s = r*c + + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(&c, &s, &b); + c.coeffs[0] += 2; // c = 2 - a*s + PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(r, &c, &s); // r = s*c +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_inv(poly *r, const poly *a) { + poly ai2; + poly_R2_inv(&ai2, a); + poly_R2_inv_to_Rq_inv(r, &ai2, a); +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_inv(poly *r, const poly *a) { + /* Schroeppel--Orman--O'Malley--Spatscheck + * "Almost Inverse" algorithm as described + * by Silverman in NTRU Tech Report #14 */ + // with several modifications to make it run in constant-time + int i, j; + uint16_t k = 0; + uint16_t degf = NTRU_N - 1; + uint16_t degg = NTRU_N - 1; + int sign, fsign = 0, t, swap; + int16_t done = 0; + poly b, c, f, g; + poly *temp_r = &f; + + /* b(X) := 1 */ + for (i = 1; i < NTRU_N; i++) { + b.coeffs[i] = 0; + } + b.coeffs[0] = 1; + + /* c(X) := 0 */ + for (i = 0; i < NTRU_N; i++) { + c.coeffs[i] = 0; + } + + /* f(X) := a(X) */ + for (i = 0; i < NTRU_N; i++) { + f.coeffs[i] = a->coeffs[i]; + } + + /* g(X) := 1 + X + X^2 + ... + X^{N-1} */ + for (i = 0; i < NTRU_N; i++) { + g.coeffs[i] = 1; + } + + for (j = 0; j < 2 * (NTRU_N - 1) - 1; j++) { + sign = PQCLEAN_NTRUHPS2048677_CLEAN_mod3(2 * g.coeffs[0] * f.coeffs[0]); + swap = (((sign & 2) >> 1) | sign) & !done & ((degf - degg) >> 15); + + cswappoly(&f, &g, swap); + cswappoly(&b, &c, swap); + t = (degf ^ degg) & (-swap); + degf ^= t; + degg ^= t; + + for (i = 0; i < NTRU_N; i++) { + f.coeffs[i] = PQCLEAN_NTRUHPS2048677_CLEAN_mod3(f.coeffs[i] + ((uint16_t) (sign * (!done))) * g.coeffs[i]); + } + for (i = 0; i < NTRU_N; i++) { + b.coeffs[i] = PQCLEAN_NTRUHPS2048677_CLEAN_mod3(b.coeffs[i] + ((uint16_t) (sign * (!done))) * c.coeffs[i]); + } + + poly_divx(&f, !done); + poly_mulx(&c, !done); + degf -= !done; + k += !done; + + done = 1 - (((uint16_t) - degf) >> 15); + } + + fsign = f.coeffs[0]; + k = k - NTRU_N * ((uint16_t)(NTRU_N - k - 1) >> 15); + + /* Return X^{N-k} * b(X) */ + /* This is a k-coefficient rotation. We do this by looking at the binary + representation of k, rotating for every power of 2, and performing a cmov + if the respective bit is set. */ + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = PQCLEAN_NTRUHPS2048677_CLEAN_mod3((uint16_t) fsign * b.coeffs[i]); + } + + for (i = 0; i < 10; i++) { + for (j = 0; j < NTRU_N; j++) { + temp_r->coeffs[j] = r->coeffs[(j + (1 << i)) % NTRU_N]; + } + PQCLEAN_NTRUHPS2048677_CLEAN_cmov((unsigned char *) & (r->coeffs), + (unsigned char *) & (temp_r->coeffs), sizeof(uint16_t) * NTRU_N, k & 1); + k >>= 1; + } + + /* Reduce modulo Phi_n */ + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = PQCLEAN_NTRUHPS2048677_CLEAN_mod3(r->coeffs[i] + 2 * r->coeffs[NTRU_N - 1]); + } +} diff --git a/crypto_kem/ntruhps2048677/clean/poly.h b/crypto_kem/ntruhps2048677/clean/poly.h new file mode 100644 index 00000000..0402282a --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/poly.h @@ -0,0 +1,38 @@ +#ifndef POLY_H +#define POLY_H + +#include + +#include "params.h" + +#define MODQ(X) ((X) & (NTRU_Q-1)) +uint16_t PQCLEAN_NTRUHPS2048677_CLEAN_mod3(uint16_t a); + +typedef struct { + uint16_t coeffs[NTRU_N]; +} poly; + + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Sq_tobytes(unsigned char *r, const poly *a); +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Sq_frombytes(poly *r, const unsigned char *a); + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_sum_zero_tobytes(unsigned char *r, const poly *a); +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_sum_zero_frombytes(poly *r, const unsigned char *a); + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_tobytes(unsigned char msg[NTRU_PACK_TRINARY_BYTES], const poly *a); +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_frombytes(poly *r, const unsigned char msg[NTRU_PACK_TRINARY_BYTES]); + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Sq_mul(poly *r, const poly *a, const poly *b); +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul(poly *r, const poly *a, const poly *b); +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_mul_x_minus_1(poly *r, const poly *a); +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_mul(poly *r, const poly *a, const poly *b); +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_lift(poly *r, const poly *a); +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_to_S3(poly *r, const poly *a); + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Rq_inv(poly *r, const poly *a); +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_S3_inv(poly *r, const poly *a); + +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_Z3_to_Zq(poly *r); +void PQCLEAN_NTRUHPS2048677_CLEAN_poly_trinary_Zq_to_Z3(poly *r); + +#endif diff --git a/crypto_kem/ntruhps2048677/clean/sample.c b/crypto_kem/ntruhps2048677/clean/sample.c new file mode 100644 index 00000000..a462b214 --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/sample.c @@ -0,0 +1,54 @@ +#include "sample.h" +#include "fips202.h" + +void PQCLEAN_NTRUHPS2048677_CLEAN_sample_fg(poly *f, poly *g, const unsigned char uniformbytes[NTRU_SAMPLE_FG_BYTES]) { + PQCLEAN_NTRUHPS2048677_CLEAN_sample_iid(f, uniformbytes); + PQCLEAN_NTRUHPS2048677_CLEAN_sample_fixed_type(g, uniformbytes + NTRU_SAMPLE_IID_BYTES); +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_sample_rm(poly *r, poly *m, const unsigned char uniformbytes[NTRU_SAMPLE_RM_BYTES]) { + PQCLEAN_NTRUHPS2048677_CLEAN_sample_iid(r, uniformbytes); + PQCLEAN_NTRUHPS2048677_CLEAN_sample_fixed_type(m, uniformbytes + NTRU_SAMPLE_IID_BYTES); +} + +void PQCLEAN_NTRUHPS2048677_CLEAN_sample_iid(poly *r, const unsigned char uniformbytes[NTRU_SAMPLE_IID_BYTES]) { + int i; + /* {0,1,...,255} -> {0,1,2}; Pr[0] = 86/256, Pr[1] = Pr[-1] = 85/256 */ + for (i = 0; i < NTRU_N - 1; i++) { + r->coeffs[i] = PQCLEAN_NTRUHPS2048677_CLEAN_mod3(uniformbytes[i]); + } + + r->coeffs[NTRU_N - 1] = 0; +} + +#include "crypto_sort.h" +void PQCLEAN_NTRUHPS2048677_CLEAN_sample_fixed_type(poly *r, const unsigned char u[NTRU_SAMPLE_FT_BYTES]) { + // Assumes NTRU_SAMPLE_FT_BYTES = ceil(30*(n-1)/8) + + int32_t s[NTRU_N - 1]; + int i; + + // Use 30 bits of u per word + for (i = 0; i < (NTRU_N - 1) / 4; i++) { + s[4 * i + 0] = (u[15 * i + 0] << 2) + (u[15 * i + 1] << 10) + (u[15 * i + 2] << 18) + (u[15 * i + 3] << 26); + s[4 * i + 1] = ((u[15 * i + 3] & 0xc0) >> 4) + (u[15 * i + 4] << 4) + (u[15 * i + 5] << 12) + (u[15 * i + 6] << 20) + (u[15 * i + 7] << 28); + s[4 * i + 2] = ((u[15 * i + 7] & 0xf0) >> 2) + (u[15 * i + 8] << 6) + (u[15 * i + 9] << 14) + (u[15 * i + 10] << 22) + (u[15 * i + 11] << 30); + s[4 * i + 3] = (u[15 * i + 11] & 0xfc) + (u[15 * i + 12] << 8) + (u[15 * i + 13] << 15) + (u[15 * i + 14] << 24); + } + + for (i = 0; i < NTRU_WEIGHT / 2; i++) { + s[i] |= 1; + } + + for (i = NTRU_WEIGHT / 2; i < NTRU_WEIGHT; i++) { + s[i] |= 2; + } + + PQCLEAN_NTRUHPS2048677_CLEAN_crypto_sort(s, NTRU_N - 1); + + for (i = 0; i < NTRU_N - 1; i++) { + r->coeffs[i] = ((uint16_t) (s[i] & 3)); + } + + r->coeffs[NTRU_N - 1] = 0; +} diff --git a/crypto_kem/ntruhps2048677/clean/sample.h b/crypto_kem/ntruhps2048677/clean/sample.h new file mode 100644 index 00000000..93155fc4 --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/sample.h @@ -0,0 +1,16 @@ +#ifndef SAMPLE_H +#define SAMPLE_H + +#include + +#include "params.h" +#include "poly.h" + +void PQCLEAN_NTRUHPS2048677_CLEAN_sample_fg(poly *f, poly *g, const unsigned char uniformbytes[NTRU_SAMPLE_FG_BYTES]); +void PQCLEAN_NTRUHPS2048677_CLEAN_sample_rm(poly *r, poly *m, const unsigned char uniformbytes[NTRU_SAMPLE_RM_BYTES]); + +void PQCLEAN_NTRUHPS2048677_CLEAN_sample_iid(poly *r, const unsigned char uniformbytes[NTRU_SAMPLE_IID_BYTES]); + +void PQCLEAN_NTRUHPS2048677_CLEAN_sample_fixed_type(poly *r, const unsigned char uniformbytes[NTRU_SAMPLE_FT_BYTES]); + +#endif diff --git a/crypto_kem/ntruhps2048677/clean/verify.c b/crypto_kem/ntruhps2048677/clean/verify.c new file mode 100644 index 00000000..6c3e0a2d --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/verify.c @@ -0,0 +1,29 @@ +#include +#include + +#include "verify.h" + +/* returns 0 for equal strings, 1 for non-equal strings */ +unsigned char PQCLEAN_NTRUHPS2048677_CLEAN_verify(const unsigned char *a, const unsigned char *b, size_t len) { + uint64_t r; + size_t i; + + r = 0; + for (i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (~r + 1); // Two's complement + r >>= 63; + return (unsigned char)r; +} + +/* b = 1 means mov, b = 0 means don't mov*/ +void PQCLEAN_NTRUHPS2048677_CLEAN_cmov(unsigned char *r, const unsigned char *x, size_t len, unsigned char b) { + size_t i; + + b = (~b + 1); // Two's complement + for (i = 0; i < len; i++) { + r[i] ^= b & (x[i] ^ r[i]); + } +} diff --git a/crypto_kem/ntruhps2048677/clean/verify.h b/crypto_kem/ntruhps2048677/clean/verify.h new file mode 100644 index 00000000..10a101f9 --- /dev/null +++ b/crypto_kem/ntruhps2048677/clean/verify.h @@ -0,0 +1,12 @@ +#ifndef VERIFY_H +#define VERIFY_H + +#include + +/* returns 0 for equal strings, 1 for non-equal strings */ +unsigned char PQCLEAN_NTRUHPS2048677_CLEAN_verify(const unsigned char *a, const unsigned char *b, size_t len); + +/* b = 1 means mov, b = 0 means don't mov*/ +void PQCLEAN_NTRUHPS2048677_CLEAN_cmov(unsigned char *r, const unsigned char *x, size_t len, unsigned char b); + +#endif From 9cf75a2ab28e73797e01752712bdd4685f8e5e74 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Sun, 14 Apr 2019 19:58:46 -0400 Subject: [PATCH 2/7] Metadata and duplicate consistency fixes for ntruhps2048677 --- crypto_kem/ntruhps2048677/META.yml | 2 +- .../ntruhps2048677_clean.yml | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 test/duplicate_consistency/ntruhps2048677_clean.yml diff --git a/crypto_kem/ntruhps2048677/META.yml b/crypto_kem/ntruhps2048677/META.yml index 2f017e52..7a8218e4 100644 --- a/crypto_kem/ntruhps2048677/META.yml +++ b/crypto_kem/ntruhps2048677/META.yml @@ -1,6 +1,6 @@ name: ntru-hps2048677 type: kem -claimed-nist-level: 1 +claimed-nist-level: 3 length-public-key: 930 length-ciphertext: 930 length-shared-secret: 32 diff --git a/test/duplicate_consistency/ntruhps2048677_clean.yml b/test/duplicate_consistency/ntruhps2048677_clean.yml new file mode 100644 index 00000000..3437e899 --- /dev/null +++ b/test/duplicate_consistency/ntruhps2048677_clean.yml @@ -0,0 +1,18 @@ +consistency_checks: +- source: + scheme: ntruhps2048509 + implementation: clean + files: + - crypto_sort.c + - crypto_sort.h + - kem.c + - owcpa.c + - owcpa.h + - pack3.c + - packq.c + - poly.c + - poly.h + - sample.c + - sample.h + - verify.c + - verify.h From 7a175c9e58c3c5b3c9275bd3edacc90883580198 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Sun, 14 Apr 2019 19:58:56 -0400 Subject: [PATCH 3/7] Add ntruhps4096821 --- crypto_kem/ntruhps4096821/META.yml | 22 ++ crypto_kem/ntruhps4096821/clean/LICENSE | 1 + crypto_kem/ntruhps4096821/clean/Makefile | 19 + .../clean/Makefile.Microsoft_nmake | 19 + crypto_kem/ntruhps4096821/clean/api.h | 19 + crypto_kem/ntruhps4096821/clean/crypto_sort.c | 50 +++ crypto_kem/ntruhps4096821/clean/crypto_sort.h | 6 + crypto_kem/ntruhps4096821/clean/kem.c | 60 +++ crypto_kem/ntruhps4096821/clean/owcpa.c | 174 +++++++++ crypto_kem/ntruhps4096821/clean/owcpa.h | 20 + crypto_kem/ntruhps4096821/clean/pack3.c | 32 ++ crypto_kem/ntruhps4096821/clean/packq.c | 36 ++ crypto_kem/ntruhps4096821/clean/params.h | 34 ++ crypto_kem/ntruhps4096821/clean/poly.c | 345 ++++++++++++++++++ crypto_kem/ntruhps4096821/clean/poly.h | 38 ++ crypto_kem/ntruhps4096821/clean/sample.c | 54 +++ crypto_kem/ntruhps4096821/clean/sample.h | 16 + crypto_kem/ntruhps4096821/clean/verify.c | 29 ++ crypto_kem/ntruhps4096821/clean/verify.h | 12 + .../ntruhps4096821_clean.yml | 16 + 20 files changed, 1002 insertions(+) create mode 100644 crypto_kem/ntruhps4096821/META.yml create mode 100644 crypto_kem/ntruhps4096821/clean/LICENSE create mode 100644 crypto_kem/ntruhps4096821/clean/Makefile create mode 100644 crypto_kem/ntruhps4096821/clean/Makefile.Microsoft_nmake create mode 100644 crypto_kem/ntruhps4096821/clean/api.h create mode 100644 crypto_kem/ntruhps4096821/clean/crypto_sort.c create mode 100644 crypto_kem/ntruhps4096821/clean/crypto_sort.h create mode 100644 crypto_kem/ntruhps4096821/clean/kem.c create mode 100644 crypto_kem/ntruhps4096821/clean/owcpa.c create mode 100644 crypto_kem/ntruhps4096821/clean/owcpa.h create mode 100644 crypto_kem/ntruhps4096821/clean/pack3.c create mode 100644 crypto_kem/ntruhps4096821/clean/packq.c create mode 100644 crypto_kem/ntruhps4096821/clean/params.h create mode 100644 crypto_kem/ntruhps4096821/clean/poly.c create mode 100644 crypto_kem/ntruhps4096821/clean/poly.h create mode 100644 crypto_kem/ntruhps4096821/clean/sample.c create mode 100644 crypto_kem/ntruhps4096821/clean/sample.h create mode 100644 crypto_kem/ntruhps4096821/clean/verify.c create mode 100644 crypto_kem/ntruhps4096821/clean/verify.h create mode 100644 test/duplicate_consistency/ntruhps4096821_clean.yml diff --git a/crypto_kem/ntruhps4096821/META.yml b/crypto_kem/ntruhps4096821/META.yml new file mode 100644 index 00000000..216ffa0e --- /dev/null +++ b/crypto_kem/ntruhps4096821/META.yml @@ -0,0 +1,22 @@ +name: ntru-hps4096821 +type: kem +claimed-nist-level: 5 +length-public-key: 1230 +length-ciphertext: 1230 +length-shared-secret: 32 +testvectors-sha256: 099c3d9b6700608f9c7c95b89efbda75841a1e620a0d3bb0b6e7d403ca249e3f +nistkat-sha256: 0c5b6b159fab6eb677da469ec35aaa7e6b16162b315dcdb55a3b5da857e10519 +principal-submitter: John M. Schanck +auxiliary-submitters: + - Cong Chen + - Oussama Danba + - Jeffrey Hoffstein + - Andreas Hülsing + - Joost Rijneveld + - Peter Schwabe + - William Whyte + - Zhenfei Zhang +implementations: + - name: clean + version: https://csrc.nist.gov/CSRC/media/Projects/Post-Quantum-Cryptography/documents/round-2/submissions/NTRU-Round2.zip reference implemntation + length-secret-key: 1590 diff --git a/crypto_kem/ntruhps4096821/clean/LICENSE b/crypto_kem/ntruhps4096821/clean/LICENSE new file mode 100644 index 00000000..d5d21fff --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/LICENSE @@ -0,0 +1 @@ +Public Domain diff --git a/crypto_kem/ntruhps4096821/clean/Makefile b/crypto_kem/ntruhps4096821/clean/Makefile new file mode 100644 index 00000000..c04c7f38 --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/Makefile @@ -0,0 +1,19 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libntruhps4096821_clean.a +HEADERS=api.h crypto_sort.h owcpa.h params.h poly.h sample.h verify.h +OBJECTS=crypto_sort.o kem.o owcpa.o pack3.o packq.o poly.o sample.o verify.o + +CFLAGS=-Wall -Wextra -Wpedantic -Werror -Wmissing-prototypes -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/ntruhps4096821/clean/Makefile.Microsoft_nmake b/crypto_kem/ntruhps4096821/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..e7be2f2c --- /dev/null +++ b/crypto_kem/ntruhps4096821/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=libntruhps4096821_clean.lib +OBJECTS=crypto_sort.obj kem.obj owcpa.obj pack3.obj packq.obj poly.obj sample.obj verify.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/ntruhps4096821/clean/api.h b/crypto_kem/ntruhps4096821/clean/api.h new file mode 100644 index 00000000..bd916d14 --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/api.h @@ -0,0 +1,19 @@ +#ifndef PQCLEAN_NTRUHPS4096821_CLEAN_API_H +#define PQCLEAN_NTRUHPS4096821_CLEAN_API_H + +#include + +#define PQCLEAN_NTRUHPS4096821_CLEAN_CRYPTO_SECRETKEYBYTES 1590 +#define PQCLEAN_NTRUHPS4096821_CLEAN_CRYPTO_PUBLICKEYBYTES 1230 +#define PQCLEAN_NTRUHPS4096821_CLEAN_CRYPTO_CIPHERTEXTBYTES 1230 +#define PQCLEAN_NTRUHPS4096821_CLEAN_CRYPTO_BYTES 32 + +#define PQCLEAN_NTRUHPS4096821_CLEAN_CRYPTO_ALGNAME "NTRU-HPS4096821" + +int PQCLEAN_NTRUHPS4096821_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); + +int PQCLEAN_NTRUHPS4096821_CLEAN_crypto_kem_enc(uint8_t *c, uint8_t *k, const uint8_t *pk); + +int PQCLEAN_NTRUHPS4096821_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, const uint8_t *sk); + +#endif diff --git a/crypto_kem/ntruhps4096821/clean/crypto_sort.c b/crypto_kem/ntruhps4096821/clean/crypto_sort.c new file mode 100644 index 00000000..2add05c2 --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/crypto_sort.c @@ -0,0 +1,50 @@ +// XXX: Temporary placeholder for a faster sort. +// Copied from supercop-20190110/crypto_sort/int32/portable3 + +#include + +#include "crypto_sort.h" + +#define int32_MINMAX(a,b) \ + do { \ + int32_t ab = (b) ^ (a); \ + int32_t c = (b) - (a); \ + c ^= ab & (c ^ (b)); \ + c >>= 31; \ + c &= ab; \ + (a) ^= c; \ + (b) ^= c; \ + } while(0) + +void PQCLEAN_NTRUHPS4096821_CLEAN_crypto_sort(void *array, long long n) { + long long top, p, q, r, i; + int32_t *x = array; + + if (n < 2) { + return; + } + top = 1; + while (top < n - top) { + top += top; + } + + for (p = top; p > 0; p >>= 1) { + for (i = 0; i < n - p; ++i) { + if (!(i & p)) { + int32_MINMAX(x[i], x[i + p]); + } + } + i = 0; + for (q = top; q > p; q >>= 1) { + for (; i < n - q; ++i) { + if (!(i & p)) { + int32_t a = x[i + p]; + for (r = q; r > p; r >>= 1) { + int32_MINMAX(a, x[i + r]); + } + x[i + p] = a; + } + } + } + } +} diff --git a/crypto_kem/ntruhps4096821/clean/crypto_sort.h b/crypto_kem/ntruhps4096821/clean/crypto_sort.h new file mode 100644 index 00000000..2bc4ef15 --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/crypto_sort.h @@ -0,0 +1,6 @@ +#ifndef CRYPTO_SORT +#define CRYPTO_SORT + +void PQCLEAN_NTRUHPS4096821_CLEAN_crypto_sort(void *array, long long n); + +#endif diff --git a/crypto_kem/ntruhps4096821/clean/kem.c b/crypto_kem/ntruhps4096821/clean/kem.c new file mode 100644 index 00000000..32ddc94b --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/kem.c @@ -0,0 +1,60 @@ +#include + +#include "api.h" +#include "fips202.h" +#include "owcpa.h" +#include "params.h" +#include "randombytes.h" +#include "verify.h" + +// API FUNCTIONS +int PQCLEAN_NTRUHPS4096821_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + uint8_t seed[NTRU_SAMPLE_FG_BYTES]; + + randombytes(seed, NTRU_SAMPLE_FG_BYTES); + PQCLEAN_NTRUHPS4096821_CLEAN_owcpa_keypair(pk, sk, seed); + + randombytes(sk + NTRU_OWCPA_SECRETKEYBYTES, NTRU_PRFKEYBYTES); + + return 0; +} + +int PQCLEAN_NTRUHPS4096821_CLEAN_crypto_kem_enc(uint8_t *c, uint8_t *k, const uint8_t *pk) { + uint8_t rm[NTRU_OWCPA_MSGBYTES]; + uint8_t rm_seed[NTRU_SAMPLE_RM_BYTES]; + + randombytes(rm_seed, NTRU_SAMPLE_RM_BYTES); + PQCLEAN_NTRUHPS4096821_CLEAN_owcpa_samplemsg(rm, rm_seed); + + sha3_256(k, rm, NTRU_OWCPA_MSGBYTES); + + PQCLEAN_NTRUHPS4096821_CLEAN_owcpa_enc(c, rm, pk); + + return 0; +} + +int PQCLEAN_NTRUHPS4096821_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, const uint8_t *sk) { + int i, fail; + uint8_t rm[NTRU_OWCPA_MSGBYTES]; + uint8_t buf[NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES]; + uint8_t *cmp = buf + NTRU_PRFKEYBYTES; + + fail = PQCLEAN_NTRUHPS4096821_CLEAN_owcpa_dec(rm, c, sk); + /* If fail = 0 then c = Enc(h, rm), there is no need to re-encapsulate. */ + /* See comment in PQCLEAN_NTRUHPS4096821_CLEAN_owcpa_dec for details. */ + + sha3_256(k, rm, NTRU_OWCPA_MSGBYTES); + + /* shake(secret PRF key || input ciphertext) */ + for (i = 0; i < NTRU_PRFKEYBYTES; i++) { + buf[i] = sk[i + NTRU_OWCPA_SECRETKEYBYTES]; + } + for (i = 0; i < NTRU_CIPHERTEXTBYTES; i++) { + cmp[i] = c[i]; + } + sha3_256(rm, cmp, NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES); + + PQCLEAN_NTRUHPS4096821_CLEAN_cmov(k, rm, NTRU_SHAREDKEYBYTES, (unsigned char) fail); + + return 0; +} diff --git a/crypto_kem/ntruhps4096821/clean/owcpa.c b/crypto_kem/ntruhps4096821/clean/owcpa.c new file mode 100644 index 00000000..29845252 --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/owcpa.c @@ -0,0 +1,174 @@ +#include "owcpa.h" +#include "poly.h" +#include "sample.h" + +static int owcpa_check_r(const poly *r) { + /* Check that r is in message space. */ + /* Note: Assumes that r has coefficients in {0, 1, ..., q-1} */ + int i; + uint64_t t = 0; + uint16_t c; + for (i = 0; i < NTRU_N; i++) { + c = MODQ(r->coeffs[i] + 1); + t |= c & (NTRU_Q - 4); /* 0 if c is in {0,1,2,3} */ + t |= (c + 1) & 0x4; /* 0 if c is in {0,1,2} */ + } + t |= r->coeffs[NTRU_N - 1]; /* Coefficient n-1 must be zero */ + t = (~t + 1); // two's complement + t >>= 63; + return (int) t; +} + +static int owcpa_check_m(const poly *m) { + /* Check that m is in message space. */ + /* Note: Assumes that m has coefficients in {0,1,2}. */ + int i; + uint64_t t = 0; + uint16_t p1 = 0; + uint16_t m1 = 0; + for (i = 0; i < NTRU_N; i++) { + p1 += m->coeffs[i] & 0x01; + m1 += (m->coeffs[i] & 0x02) >> 1; + } + /* Need p1 = m1 and p1 + m1 = NTRU_WEIGHT */ + t |= p1 ^ m1; + t |= (p1 + m1) ^ NTRU_WEIGHT; + t = (~t + 1); // two's complement + t >>= 63; + return (int) t; +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_owcpa_samplemsg(unsigned char msg[NTRU_OWCPA_MSGBYTES], + const unsigned char seed[NTRU_SAMPLE_RM_BYTES]) { + poly r, m; + + PQCLEAN_NTRUHPS4096821_CLEAN_sample_rm(&r, &m, seed); + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_tobytes(msg, &r); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_tobytes(msg + NTRU_PACK_TRINARY_BYTES, &m); +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_owcpa_keypair(unsigned char *pk, + unsigned char *sk, + const unsigned char seed[NTRU_SAMPLE_FG_BYTES]) { + int i; + + poly x1, x2, x3, x4, x5; + + poly *f = &x1, *invf_mod3 = &x2; + poly *g = &x3, *G = &x2; + poly *Gf = &x3, *invGf = &x4, *tmp = &x5; + poly *invh = &x3, *h = &x3; + + PQCLEAN_NTRUHPS4096821_CLEAN_sample_fg(f, g, seed); + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_inv(invf_mod3, f); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_tobytes(sk, f); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_tobytes(sk + NTRU_PACK_TRINARY_BYTES, invf_mod3); + + /* Lift coeffs of f and g from Z_p to Z_q */ + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Z3_to_Zq(f); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Z3_to_Zq(g); + + /* G = 3*g */ + for (i = 0; i < NTRU_N; i++) { + G->coeffs[i] = MODQ(3 * g->coeffs[i]); + } + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(Gf, G, f); + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_inv(invGf, Gf); + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(tmp, invGf, f); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_mul(invh, tmp, f); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_tobytes(sk + 2 * NTRU_PACK_TRINARY_BYTES, invh); + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(tmp, invGf, G); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(h, tmp, G); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_sum_zero_tobytes(pk, h); +} + + +void PQCLEAN_NTRUHPS4096821_CLEAN_owcpa_enc(unsigned char *c, + const unsigned char *rm, + const unsigned char *pk) { + int i; + poly x1, x2, x3; + poly *h = &x1, *liftm = &x1; + poly *r = &x2, *m = &x2; + poly *ct = &x3; + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_sum_zero_frombytes(h, pk); + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_frombytes(r, rm); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Z3_to_Zq(r); + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(ct, r, h); + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_frombytes(m, rm + NTRU_PACK_TRINARY_BYTES); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_lift(liftm, m); + for (i = 0; i < NTRU_N; i++) { + ct->coeffs[i] = MODQ(ct->coeffs[i] + liftm->coeffs[i]); + } + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_sum_zero_tobytes(c, ct); +} + +int PQCLEAN_NTRUHPS4096821_CLEAN_owcpa_dec(unsigned char *rm, + const unsigned char *ciphertext, + const unsigned char *secretkey) { + int i; + int fail; + poly x1, x2, x3, x4; + + poly *c = &x1, *f = &x2, *cf = &x3; + poly *mf = &x2, *finv3 = &x3, *m = &x4; + poly *liftm = &x2, *invh = &x3, *r = &x4; + poly *b = &x1; + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_sum_zero_frombytes(c, ciphertext); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_frombytes(f, secretkey); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Z3_to_Zq(f); + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(cf, c, f); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_to_S3(mf, cf); + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_frombytes(finv3, secretkey + NTRU_PACK_TRINARY_BYTES); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_mul(m, mf, finv3); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_tobytes(rm + NTRU_PACK_TRINARY_BYTES, m); + + /* NOTE: For the IND-CCA2 KEM we must ensure that c = Enc(h, (r,m)). */ + /* We can avoid re-computing r*h + Lift(m) as long as we check that */ + /* r (defined as b/h mod (q, Phi_n)) and m are in the message space. */ + /* (m can take any value in S3 in NTRU_HRSS) */ + fail = 0; + fail |= owcpa_check_m(m); + + /* b = c - Lift(m) mod (q, x^n - 1) */ + PQCLEAN_NTRUHPS4096821_CLEAN_poly_lift(liftm, m); + for (i = 0; i < NTRU_N; i++) { + b->coeffs[i] = MODQ(c->coeffs[i] - liftm->coeffs[i]); + } + + /* r = b / h mod (q, Phi_n) */ + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_frombytes(invh, secretkey + 2 * NTRU_PACK_TRINARY_BYTES); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_mul(r, b, invh); + + /* NOTE: Our definition of r as b/h mod (q, Phi_n) follows Figure 4 of */ + /* [Sch18] https://eprint.iacr.org/2018/1174/20181203:032458. */ + /* This differs from Figure 10 of Saito--Xagawa--Yamakawa */ + /* [SXY17] https://eprint.iacr.org/2017/1005/20180516:055500 */ + /* where r gets a final reduction modulo p. */ + /* We need this change to use Proposition 1 of [Sch18]. */ + + /* Proposition 1 of [Sch18] shows that re-encryption with (r,m) yields c. */ + /* if and only if fail==0 after the following call to owcpa_check_r */ + /* The procedure given in Fig. 8 of [Sch18] can be skipped because we have */ + /* c(1) = 0 due to the use of poly_Rq_sum_zero_{to,from}bytes. */ + fail |= owcpa_check_r(r); + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_trinary_Zq_to_Z3(r); + PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_tobytes(rm, r); + + return fail; +} diff --git a/crypto_kem/ntruhps4096821/clean/owcpa.h b/crypto_kem/ntruhps4096821/clean/owcpa.h new file mode 100644 index 00000000..c0997575 --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/owcpa.h @@ -0,0 +1,20 @@ +#ifndef OWCPA_H +#define OWCPA_H + +#include "params.h" + +void PQCLEAN_NTRUHPS4096821_CLEAN_owcpa_samplemsg(unsigned char msg[NTRU_OWCPA_MSGBYTES], + const unsigned char seed[NTRU_SEEDBYTES]); + +void PQCLEAN_NTRUHPS4096821_CLEAN_owcpa_keypair(unsigned char *pk, + unsigned char *sk, + const unsigned char seed[NTRU_SEEDBYTES]); + +void PQCLEAN_NTRUHPS4096821_CLEAN_owcpa_enc(unsigned char *c, + const unsigned char *rm, + const unsigned char *pk); + +int PQCLEAN_NTRUHPS4096821_CLEAN_owcpa_dec(unsigned char *rm, + const unsigned char *ciphertext, + const unsigned char *secretkey); +#endif diff --git a/crypto_kem/ntruhps4096821/clean/pack3.c b/crypto_kem/ntruhps4096821/clean/pack3.c new file mode 100644 index 00000000..6bfed9a8 --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/pack3.c @@ -0,0 +1,32 @@ +#include "poly.h" + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_tobytes(unsigned char msg[NTRU_OWCPA_MSGBYTES], const poly *a) { + int i; + unsigned char c; + + for (i = 0; i < NTRU_PACK_DEG / 5; i++) { + c = a->coeffs[5 * i + 4] & 255; + c = (3 * c + a->coeffs[5 * i + 3]) & 255; + c = (3 * c + a->coeffs[5 * i + 2]) & 255; + c = (3 * c + a->coeffs[5 * i + 1]) & 255; + c = (3 * c + a->coeffs[5 * i + 0]) & 255; + msg[i] = c; + } + +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_frombytes(poly *r, const unsigned char msg[NTRU_OWCPA_MSGBYTES]) { + int i; + unsigned char c; + + for (i = 0; i < NTRU_PACK_DEG / 5; i++) { + c = msg[i]; + r->coeffs[5 * i + 0] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(c); + r->coeffs[5 * i + 1] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(c * 171 >> 9); // this is division by 3 + r->coeffs[5 * i + 2] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(c * 57 >> 9); // division by 3^2 + r->coeffs[5 * i + 3] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(c * 19 >> 9); // division by 3^3 + r->coeffs[5 * i + 4] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(c * 203 >> 14); // etc. + } + r->coeffs[NTRU_N - 1] = 0; +} + diff --git a/crypto_kem/ntruhps4096821/clean/packq.c b/crypto_kem/ntruhps4096821/clean/packq.c new file mode 100644 index 00000000..f23ae357 --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/packq.c @@ -0,0 +1,36 @@ +#include "poly.h" + + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_tobytes(unsigned char *r, const poly *a) { + int i; + + for (i = 0; i < NTRU_PACK_DEG / 2; i++) { + r[3 * i + 0] = a->coeffs[2 * i + 0] & 0xff; + r[3 * i + 1] = (a->coeffs[2 * i + 0] >> 8) | ((a->coeffs[2 * i + 1] & 0x0f) << 4); + r[3 * i + 2] = (a->coeffs[2 * i + 1] >> 4); + } +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_frombytes(poly *r, const unsigned char *a) { + int i; + for (i = 0; i < NTRU_PACK_DEG / 2; i++) { + r->coeffs[2 * i + 0] = (a[3 * i + 0] >> 0) | (((uint16_t)a[3 * i + 1] & 0x0f) << 8); + r->coeffs[2 * i + 1] = (a[3 * i + 1] >> 4) | (((uint16_t)a[3 * i + 2] & 0xff) << 4); + } +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_sum_zero_tobytes(unsigned char *r, const poly *a) { + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_tobytes(r, a); +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_sum_zero_frombytes(poly *r, const unsigned char *a) { + int i; + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_frombytes(r, a); + + /* Set r[n-1] so that the sum of coefficients is zero mod q */ + r->coeffs[NTRU_N - 1] = 0; + for (i = 0; i < NTRU_PACK_DEG; i++) { + r->coeffs[NTRU_N - 1] += r->coeffs[i]; + } + r->coeffs[NTRU_N - 1] = MODQ(-(r->coeffs[NTRU_N - 1])); +} diff --git a/crypto_kem/ntruhps4096821/clean/params.h b/crypto_kem/ntruhps4096821/clean/params.h new file mode 100644 index 00000000..18e8b1d7 --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/params.h @@ -0,0 +1,34 @@ +#ifndef PARAMS_H +#define PARAMS_H + +#define NTRU_HPS +#define NTRU_N 821 +#define NTRU_LOGQ 12 + +/* Do not modify below this line */ + +#define NTRU_Q (1 << NTRU_LOGQ) +#define NTRU_WEIGHT (NTRU_Q/8 - 2) + +#define NTRU_SEEDBYTES 32 +#define NTRU_PRFKEYBYTES 32 +#define NTRU_SHAREDKEYBYTES 32 + +#define NTRU_SAMPLE_IID_BYTES (NTRU_N-1) +#define NTRU_SAMPLE_FT_BYTES ((30*(NTRU_N-1)+7)/8) +#define NTRU_SAMPLE_FG_BYTES (NTRU_SAMPLE_IID_BYTES+NTRU_SAMPLE_FT_BYTES) +#define NTRU_SAMPLE_RM_BYTES (NTRU_SAMPLE_IID_BYTES+NTRU_SAMPLE_FT_BYTES) + +#define NTRU_PACK_DEG (NTRU_N-1) +#define NTRU_PACK_TRINARY_BYTES ((NTRU_PACK_DEG+4)/5) + +#define NTRU_OWCPA_MSGBYTES (2*NTRU_PACK_TRINARY_BYTES) +#define NTRU_OWCPA_PUBLICKEYBYTES ((NTRU_LOGQ*NTRU_PACK_DEG+7)/8) +#define NTRU_OWCPA_SECRETKEYBYTES (2*NTRU_PACK_TRINARY_BYTES + NTRU_OWCPA_PUBLICKEYBYTES) +#define NTRU_OWCPA_BYTES ((NTRU_LOGQ*NTRU_PACK_DEG+7)/8) + +#define NTRU_PUBLICKEYBYTES (NTRU_OWCPA_PUBLICKEYBYTES) +#define NTRU_SECRETKEYBYTES (NTRU_OWCPA_SECRETKEYBYTES + NTRU_PRFKEYBYTES) +#define NTRU_CIPHERTEXTBYTES (NTRU_OWCPA_BYTES) + +#endif diff --git a/crypto_kem/ntruhps4096821/clean/poly.c b/crypto_kem/ntruhps4096821/clean/poly.c new file mode 100644 index 00000000..bda3c857 --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/poly.c @@ -0,0 +1,345 @@ +#include "poly.h" +#include "fips202.h" +#include "verify.h" + +uint16_t PQCLEAN_NTRUHPS4096821_CLEAN_mod3(uint16_t a) { + uint16_t r; + int16_t t, c; + + r = (a >> 8) + (a & 0xff); // r mod 255 == a mod 255 + r = (r >> 4) + (r & 0xf); // r' mod 15 == r mod 15 + r = (r >> 2) + (r & 0x3); // r' mod 3 == r mod 3 + r = (r >> 2) + (r & 0x3); // r' mod 3 == r mod 3 + + t = r - 3; + c = t >> 15; + + return (c & r) ^ (~c & t); +} + +/* Map {0, 1, 2} -> {0,1,q-1} in place */ +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Z3_to_Zq(poly *r) { + int i; + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = r->coeffs[i] | ((-(r->coeffs[i] >> 1)) & (NTRU_Q - 1)); + } +} + +/* Map {0, 1, q-1} -> {0,1,2} in place */ +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_trinary_Zq_to_Z3(poly *r) { + int i; + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = 3 & (r->coeffs[i] ^ (r->coeffs[i] >> (NTRU_LOGQ - 1))); + } +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(poly *r, const poly *a, const poly *b) { + int k, i; + + for (k = 0; k < NTRU_N; k++) { + r->coeffs[k] = 0; + for (i = 1; i < NTRU_N - k; i++) { + r->coeffs[k] += a->coeffs[k + i] * b->coeffs[NTRU_N - i]; + } + for (i = 0; i < k + 1; i++) { + r->coeffs[k] += a->coeffs[k - i] * b->coeffs[i]; + } + r->coeffs[k] = MODQ(r->coeffs[k]); + } +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_mul(poly *r, const poly *a, const poly *b) { + int i; + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(r, a, b); + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = MODQ(r->coeffs[i] - r->coeffs[NTRU_N - 1]); + } +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_mul(poly *r, const poly *a, const poly *b) { + int k, i; + + for (k = 0; k < NTRU_N; k++) { + r->coeffs[k] = 0; + for (i = 1; i < NTRU_N - k; i++) { + r->coeffs[k] += a->coeffs[k + i] * b->coeffs[NTRU_N - i]; + } + for (i = 0; i < k + 1; i++) { + r->coeffs[k] += a->coeffs[k - i] * b->coeffs[i]; + } + } + for (k = 0; k < NTRU_N; k++) { + r->coeffs[k] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(r->coeffs[k] + 2 * r->coeffs[NTRU_N - 1]); + } +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul_x_minus_1(poly *r, const poly *a) { + int i; + uint16_t last_coeff = a->coeffs[NTRU_N - 1]; + + for (i = NTRU_N - 1; i > 0; i--) { + r->coeffs[i] = MODQ(a->coeffs[i - 1] + (NTRU_Q - a->coeffs[i])); + } + r->coeffs[0] = MODQ(last_coeff + (NTRU_Q - a->coeffs[0])); +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_lift(poly *r, const poly *a) { + int i; + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = a->coeffs[i]; + } + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Z3_to_Zq(r); +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_to_S3(poly *r, const poly *a) { + /* NOTE: Assumes input is in [0,Q-1]^N */ + /* Produces output in {0,1,2}^N */ + int i; + + /* Center coeffs around 3Q: [0, Q-1] -> [3Q - Q/2, 3Q + Q/2) */ + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = ((a->coeffs[i] >> (NTRU_LOGQ - 1)) ^ 3) << NTRU_LOGQ; + r->coeffs[i] += a->coeffs[i]; + } + /* Reduce mod (3, Phi) */ + r->coeffs[NTRU_N - 1] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(r->coeffs[NTRU_N - 1]); + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(r->coeffs[i] + 2 * r->coeffs[NTRU_N - 1]); + } +} + +#define POLY_R2_ADD(I,A,B,S) \ + for ((I)=0; (I)coeffs[i] ^ b->coeffs[i]) & swap; + a->coeffs[i] ^= t; + b->coeffs[i] ^= t; + } +} + +static inline void poly_divx(poly *a, int s) { + int i; + + for (i = 1; i < NTRU_N; i++) { + a->coeffs[i - 1] = (unsigned char) ((s * a->coeffs[i]) | (!s * a->coeffs[i - 1])); + } + a->coeffs[NTRU_N - 1] = (!s * a->coeffs[NTRU_N - 1]); +} + +static inline void poly_mulx(poly *a, int s) { + int i; + + for (i = 1; i < NTRU_N; i++) { + a->coeffs[NTRU_N - i] = (unsigned char) ((s * a->coeffs[NTRU_N - i - 1]) | (!s * a->coeffs[NTRU_N - i])); + } + a->coeffs[0] = (!s * a->coeffs[0]); +} + +static void poly_R2_inv(poly *r, const poly *a) { + /* Schroeppel--Orman--O'Malley--Spatscheck + * "Almost Inverse" algorithm as described + * by Silverman in NTRU Tech Report #14 */ + // with several modifications to make it run in constant-time + int i, j; + int k = 0; + uint16_t degf = NTRU_N - 1; + uint16_t degg = NTRU_N - 1; + int sign, t, swap; + int16_t done = 0; + poly b, f, g; + poly *c = r; // save some stack space + poly *temp_r = &f; + + /* b(X) := 1 */ + for (i = 1; i < NTRU_N; i++) { + b.coeffs[i] = 0; + } + b.coeffs[0] = 1; + + /* c(X) := 0 */ + for (i = 0; i < NTRU_N; i++) { + c->coeffs[i] = 0; + } + + /* f(X) := a(X) */ + for (i = 0; i < NTRU_N; i++) { + f.coeffs[i] = a->coeffs[i] & 1; + } + + /* g(X) := 1 + X + X^2 + ... + X^{N-1} */ + for (i = 0; i < NTRU_N; i++) { + g.coeffs[i] = 1; + } + + for (j = 0; j < 2 * (NTRU_N - 1) - 1; j++) { + sign = f.coeffs[0]; + swap = sign & !done & ((degf - degg) >> 15); + + cswappoly(&f, &g, swap); + cswappoly(&b, c, swap); + t = (degf ^ degg) & (-swap); + degf ^= t; + degg ^= t; + + POLY_R2_ADD(i, f, g, sign * (!done)); + POLY_R2_ADD(i, b, (*c), sign * (!done)); + + poly_divx(&f, !done); + poly_mulx(c, !done); + degf -= !done; + k += !done; + + done = 1 - (((uint16_t) - degf) >> 15); + } + + k = k - NTRU_N * ((uint16_t)(NTRU_N - k - 1) >> 15); + + /* Return X^{N-k} * b(X) */ + /* This is a k-coefficient rotation. We do this by looking at the binary + representation of k, rotating for every power of 2, and performing a cmov + if the respective bit is set. */ + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = b.coeffs[i]; + } + + for (i = 0; i < 10; i++) { + for (j = 0; j < NTRU_N; j++) { + temp_r->coeffs[j] = r->coeffs[(j + (1 << i)) % NTRU_N]; + } + PQCLEAN_NTRUHPS4096821_CLEAN_cmov((unsigned char *) & (r->coeffs), + (unsigned char *) & (temp_r->coeffs), sizeof(uint16_t) * NTRU_N, k & 1); + k >>= 1; + } +} + +static void poly_R2_inv_to_Rq_inv(poly *r, const poly *ai, const poly *a) { + + int i; + poly b, c; + poly s; + + // for 0..4 + // ai = ai * (2 - a*ai) mod q + for (i = 0; i < NTRU_N; i++) { + b.coeffs[i] = MODQ(NTRU_Q - a->coeffs[i]); // b = -a + } + + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = ai->coeffs[i]; + } + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(&c, r, &b); + c.coeffs[0] += 2; // c = 2 - a*ai + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(&s, &c, r); // s = ai*c + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(&c, &s, &b); + c.coeffs[0] += 2; // c = 2 - a*s + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(r, &c, &s); // r = s*c + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(&c, r, &b); + c.coeffs[0] += 2; // c = 2 - a*r + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(&s, &c, r); // s = r*c + + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(&c, &s, &b); + c.coeffs[0] += 2; // c = 2 - a*s + PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(r, &c, &s); // r = s*c +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_inv(poly *r, const poly *a) { + poly ai2; + poly_R2_inv(&ai2, a); + poly_R2_inv_to_Rq_inv(r, &ai2, a); +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_inv(poly *r, const poly *a) { + /* Schroeppel--Orman--O'Malley--Spatscheck + * "Almost Inverse" algorithm as described + * by Silverman in NTRU Tech Report #14 */ + // with several modifications to make it run in constant-time + int i, j; + uint16_t k = 0; + uint16_t degf = NTRU_N - 1; + uint16_t degg = NTRU_N - 1; + int sign, fsign = 0, t, swap; + int16_t done = 0; + poly b, c, f, g; + poly *temp_r = &f; + + /* b(X) := 1 */ + for (i = 1; i < NTRU_N; i++) { + b.coeffs[i] = 0; + } + b.coeffs[0] = 1; + + /* c(X) := 0 */ + for (i = 0; i < NTRU_N; i++) { + c.coeffs[i] = 0; + } + + /* f(X) := a(X) */ + for (i = 0; i < NTRU_N; i++) { + f.coeffs[i] = a->coeffs[i]; + } + + /* g(X) := 1 + X + X^2 + ... + X^{N-1} */ + for (i = 0; i < NTRU_N; i++) { + g.coeffs[i] = 1; + } + + for (j = 0; j < 2 * (NTRU_N - 1) - 1; j++) { + sign = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(2 * g.coeffs[0] * f.coeffs[0]); + swap = (((sign & 2) >> 1) | sign) & !done & ((degf - degg) >> 15); + + cswappoly(&f, &g, swap); + cswappoly(&b, &c, swap); + t = (degf ^ degg) & (-swap); + degf ^= t; + degg ^= t; + + for (i = 0; i < NTRU_N; i++) { + f.coeffs[i] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(f.coeffs[i] + ((uint16_t) (sign * (!done))) * g.coeffs[i]); + } + for (i = 0; i < NTRU_N; i++) { + b.coeffs[i] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(b.coeffs[i] + ((uint16_t) (sign * (!done))) * c.coeffs[i]); + } + + poly_divx(&f, !done); + poly_mulx(&c, !done); + degf -= !done; + k += !done; + + done = 1 - (((uint16_t) - degf) >> 15); + } + + fsign = f.coeffs[0]; + k = k - NTRU_N * ((uint16_t)(NTRU_N - k - 1) >> 15); + + /* Return X^{N-k} * b(X) */ + /* This is a k-coefficient rotation. We do this by looking at the binary + representation of k, rotating for every power of 2, and performing a cmov + if the respective bit is set. */ + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3((uint16_t) fsign * b.coeffs[i]); + } + + for (i = 0; i < 10; i++) { + for (j = 0; j < NTRU_N; j++) { + temp_r->coeffs[j] = r->coeffs[(j + (1 << i)) % NTRU_N]; + } + PQCLEAN_NTRUHPS4096821_CLEAN_cmov((unsigned char *) & (r->coeffs), + (unsigned char *) & (temp_r->coeffs), sizeof(uint16_t) * NTRU_N, k & 1); + k >>= 1; + } + + /* Reduce modulo Phi_n */ + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(r->coeffs[i] + 2 * r->coeffs[NTRU_N - 1]); + } +} diff --git a/crypto_kem/ntruhps4096821/clean/poly.h b/crypto_kem/ntruhps4096821/clean/poly.h new file mode 100644 index 00000000..d7b2dc85 --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/poly.h @@ -0,0 +1,38 @@ +#ifndef POLY_H +#define POLY_H + +#include + +#include "params.h" + +#define MODQ(X) ((X) & (NTRU_Q-1)) +uint16_t PQCLEAN_NTRUHPS4096821_CLEAN_mod3(uint16_t a); + +typedef struct { + uint16_t coeffs[NTRU_N]; +} poly; + + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_tobytes(unsigned char *r, const poly *a); +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_frombytes(poly *r, const unsigned char *a); + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_sum_zero_tobytes(unsigned char *r, const poly *a); +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_sum_zero_frombytes(poly *r, const unsigned char *a); + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_tobytes(unsigned char msg[NTRU_PACK_TRINARY_BYTES], const poly *a); +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_frombytes(poly *r, const unsigned char msg[NTRU_PACK_TRINARY_BYTES]); + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_mul(poly *r, const poly *a, const poly *b); +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul(poly *r, const poly *a, const poly *b); +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_mul_x_minus_1(poly *r, const poly *a); +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_mul(poly *r, const poly *a, const poly *b); +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_lift(poly *r, const poly *a); +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_to_S3(poly *r, const poly *a); + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Rq_inv(poly *r, const poly *a); +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_S3_inv(poly *r, const poly *a); + +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Z3_to_Zq(poly *r); +void PQCLEAN_NTRUHPS4096821_CLEAN_poly_trinary_Zq_to_Z3(poly *r); + +#endif diff --git a/crypto_kem/ntruhps4096821/clean/sample.c b/crypto_kem/ntruhps4096821/clean/sample.c new file mode 100644 index 00000000..1140be46 --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/sample.c @@ -0,0 +1,54 @@ +#include "sample.h" +#include "fips202.h" + +void PQCLEAN_NTRUHPS4096821_CLEAN_sample_fg(poly *f, poly *g, const unsigned char uniformbytes[NTRU_SAMPLE_FG_BYTES]) { + PQCLEAN_NTRUHPS4096821_CLEAN_sample_iid(f, uniformbytes); + PQCLEAN_NTRUHPS4096821_CLEAN_sample_fixed_type(g, uniformbytes + NTRU_SAMPLE_IID_BYTES); +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_sample_rm(poly *r, poly *m, const unsigned char uniformbytes[NTRU_SAMPLE_RM_BYTES]) { + PQCLEAN_NTRUHPS4096821_CLEAN_sample_iid(r, uniformbytes); + PQCLEAN_NTRUHPS4096821_CLEAN_sample_fixed_type(m, uniformbytes + NTRU_SAMPLE_IID_BYTES); +} + +void PQCLEAN_NTRUHPS4096821_CLEAN_sample_iid(poly *r, const unsigned char uniformbytes[NTRU_SAMPLE_IID_BYTES]) { + int i; + /* {0,1,...,255} -> {0,1,2}; Pr[0] = 86/256, Pr[1] = Pr[-1] = 85/256 */ + for (i = 0; i < NTRU_N - 1; i++) { + r->coeffs[i] = PQCLEAN_NTRUHPS4096821_CLEAN_mod3(uniformbytes[i]); + } + + r->coeffs[NTRU_N - 1] = 0; +} + +#include "crypto_sort.h" +void PQCLEAN_NTRUHPS4096821_CLEAN_sample_fixed_type(poly *r, const unsigned char u[NTRU_SAMPLE_FT_BYTES]) { + // Assumes NTRU_SAMPLE_FT_BYTES = ceil(30*(n-1)/8) + + int32_t s[NTRU_N - 1]; + int i; + + // Use 30 bits of u per word + for (i = 0; i < (NTRU_N - 1) / 4; i++) { + s[4 * i + 0] = (u[15 * i + 0] << 2) + (u[15 * i + 1] << 10) + (u[15 * i + 2] << 18) + (u[15 * i + 3] << 26); + s[4 * i + 1] = ((u[15 * i + 3] & 0xc0) >> 4) + (u[15 * i + 4] << 4) + (u[15 * i + 5] << 12) + (u[15 * i + 6] << 20) + (u[15 * i + 7] << 28); + s[4 * i + 2] = ((u[15 * i + 7] & 0xf0) >> 2) + (u[15 * i + 8] << 6) + (u[15 * i + 9] << 14) + (u[15 * i + 10] << 22) + (u[15 * i + 11] << 30); + s[4 * i + 3] = (u[15 * i + 11] & 0xfc) + (u[15 * i + 12] << 8) + (u[15 * i + 13] << 15) + (u[15 * i + 14] << 24); + } + + for (i = 0; i < NTRU_WEIGHT / 2; i++) { + s[i] |= 1; + } + + for (i = NTRU_WEIGHT / 2; i < NTRU_WEIGHT; i++) { + s[i] |= 2; + } + + PQCLEAN_NTRUHPS4096821_CLEAN_crypto_sort(s, NTRU_N - 1); + + for (i = 0; i < NTRU_N - 1; i++) { + r->coeffs[i] = ((uint16_t) (s[i] & 3)); + } + + r->coeffs[NTRU_N - 1] = 0; +} diff --git a/crypto_kem/ntruhps4096821/clean/sample.h b/crypto_kem/ntruhps4096821/clean/sample.h new file mode 100644 index 00000000..3512c06e --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/sample.h @@ -0,0 +1,16 @@ +#ifndef SAMPLE_H +#define SAMPLE_H + +#include + +#include "params.h" +#include "poly.h" + +void PQCLEAN_NTRUHPS4096821_CLEAN_sample_fg(poly *f, poly *g, const unsigned char uniformbytes[NTRU_SAMPLE_FG_BYTES]); +void PQCLEAN_NTRUHPS4096821_CLEAN_sample_rm(poly *r, poly *m, const unsigned char uniformbytes[NTRU_SAMPLE_RM_BYTES]); + +void PQCLEAN_NTRUHPS4096821_CLEAN_sample_iid(poly *r, const unsigned char uniformbytes[NTRU_SAMPLE_IID_BYTES]); + +void PQCLEAN_NTRUHPS4096821_CLEAN_sample_fixed_type(poly *r, const unsigned char uniformbytes[NTRU_SAMPLE_FT_BYTES]); + +#endif diff --git a/crypto_kem/ntruhps4096821/clean/verify.c b/crypto_kem/ntruhps4096821/clean/verify.c new file mode 100644 index 00000000..380f5c98 --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/verify.c @@ -0,0 +1,29 @@ +#include +#include + +#include "verify.h" + +/* returns 0 for equal strings, 1 for non-equal strings */ +unsigned char PQCLEAN_NTRUHPS4096821_CLEAN_verify(const unsigned char *a, const unsigned char *b, size_t len) { + uint64_t r; + size_t i; + + r = 0; + for (i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (~r + 1); // Two's complement + r >>= 63; + return (unsigned char)r; +} + +/* b = 1 means mov, b = 0 means don't mov*/ +void PQCLEAN_NTRUHPS4096821_CLEAN_cmov(unsigned char *r, const unsigned char *x, size_t len, unsigned char b) { + size_t i; + + b = (~b + 1); // Two's complement + for (i = 0; i < len; i++) { + r[i] ^= b & (x[i] ^ r[i]); + } +} diff --git a/crypto_kem/ntruhps4096821/clean/verify.h b/crypto_kem/ntruhps4096821/clean/verify.h new file mode 100644 index 00000000..c1f2a21b --- /dev/null +++ b/crypto_kem/ntruhps4096821/clean/verify.h @@ -0,0 +1,12 @@ +#ifndef VERIFY_H +#define VERIFY_H + +#include + +/* returns 0 for equal strings, 1 for non-equal strings */ +unsigned char PQCLEAN_NTRUHPS4096821_CLEAN_verify(const unsigned char *a, const unsigned char *b, size_t len); + +/* b = 1 means mov, b = 0 means don't mov*/ +void PQCLEAN_NTRUHPS4096821_CLEAN_cmov(unsigned char *r, const unsigned char *x, size_t len, unsigned char b); + +#endif diff --git a/test/duplicate_consistency/ntruhps4096821_clean.yml b/test/duplicate_consistency/ntruhps4096821_clean.yml new file mode 100644 index 00000000..232dcec5 --- /dev/null +++ b/test/duplicate_consistency/ntruhps4096821_clean.yml @@ -0,0 +1,16 @@ +consistency_checks: +- source: + scheme: ntruhps2048509 + implementation: clean + files: + - crypto_sort.c + - crypto_sort.h + - kem.c + - owcpa.c + - owcpa.h + - poly.c + - poly.h + - sample.c + - sample.h + - verify.c + - verify.h From 56ce82a87c232dbe2455db72a554dfe019f66552 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Sun, 14 Apr 2019 20:26:42 -0400 Subject: [PATCH 4/7] Add ntruhrss701 --- crypto_kem/ntruhrss701/META.yml | 22 + crypto_kem/ntruhrss701/clean/LICENSE | 1 + crypto_kem/ntruhrss701/clean/Makefile | 19 + .../clean/Makefile.Microsoft_nmake | 19 + crypto_kem/ntruhrss701/clean/api.h | 19 + crypto_kem/ntruhrss701/clean/kem.c | 60 +++ crypto_kem/ntruhrss701/clean/owcpa.c | 155 +++++++ crypto_kem/ntruhrss701/clean/owcpa.h | 20 + crypto_kem/ntruhrss701/clean/pack3.c | 32 ++ crypto_kem/ntruhrss701/clean/packq.c | 95 +++++ crypto_kem/ntruhrss701/clean/params.h | 32 ++ crypto_kem/ntruhrss701/clean/poly.c | 387 ++++++++++++++++++ crypto_kem/ntruhrss701/clean/poly.h | 38 ++ crypto_kem/ntruhrss701/clean/sample.c | 54 +++ crypto_kem/ntruhrss701/clean/sample.h | 16 + crypto_kem/ntruhrss701/clean/verify.c | 29 ++ crypto_kem/ntruhrss701/clean/verify.h | 12 + .../ntruhrss701_clean.yml | 14 + 18 files changed, 1024 insertions(+) create mode 100644 crypto_kem/ntruhrss701/META.yml create mode 100644 crypto_kem/ntruhrss701/clean/LICENSE create mode 100644 crypto_kem/ntruhrss701/clean/Makefile create mode 100644 crypto_kem/ntruhrss701/clean/Makefile.Microsoft_nmake create mode 100644 crypto_kem/ntruhrss701/clean/api.h create mode 100644 crypto_kem/ntruhrss701/clean/kem.c create mode 100644 crypto_kem/ntruhrss701/clean/owcpa.c create mode 100644 crypto_kem/ntruhrss701/clean/owcpa.h create mode 100644 crypto_kem/ntruhrss701/clean/pack3.c create mode 100644 crypto_kem/ntruhrss701/clean/packq.c create mode 100644 crypto_kem/ntruhrss701/clean/params.h create mode 100644 crypto_kem/ntruhrss701/clean/poly.c create mode 100644 crypto_kem/ntruhrss701/clean/poly.h create mode 100644 crypto_kem/ntruhrss701/clean/sample.c create mode 100644 crypto_kem/ntruhrss701/clean/sample.h create mode 100644 crypto_kem/ntruhrss701/clean/verify.c create mode 100644 crypto_kem/ntruhrss701/clean/verify.h create mode 100644 test/duplicate_consistency/ntruhrss701_clean.yml diff --git a/crypto_kem/ntruhrss701/META.yml b/crypto_kem/ntruhrss701/META.yml new file mode 100644 index 00000000..21e1930f --- /dev/null +++ b/crypto_kem/ntruhrss701/META.yml @@ -0,0 +1,22 @@ +name: ntru-hrss701 +type: kem +claimed-nist-level: 3 +length-public-key: 1138 +length-ciphertext: 1138 +length-shared-secret: 32 +testvectors-sha256: a0082a6607ab054c296a02e1bea1b5d7ee8465077ee7989ab4295f9e464d0558 +nistkat-sha256: 501e000c3eb374ffbfb81b0f16673a6282116465936608d7d164b05635e769e8 +principal-submitter: John M. Schanck +auxiliary-submitters: + - Cong Chen + - Oussama Danba + - Jeffrey Hoffstein + - Andreas Hülsing + - Joost Rijneveld + - Peter Schwabe + - William Whyte + - Zhenfei Zhang +implementations: + - name: clean + version: https://csrc.nist.gov/CSRC/media/Projects/Post-Quantum-Cryptography/documents/round-2/submissions/NTRU-Round2.zip reference implemntation + length-secret-key: 1450 diff --git a/crypto_kem/ntruhrss701/clean/LICENSE b/crypto_kem/ntruhrss701/clean/LICENSE new file mode 100644 index 00000000..d5d21fff --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/LICENSE @@ -0,0 +1 @@ +Public Domain diff --git a/crypto_kem/ntruhrss701/clean/Makefile b/crypto_kem/ntruhrss701/clean/Makefile new file mode 100644 index 00000000..88cc97b8 --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/Makefile @@ -0,0 +1,19 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libntruhrss701_clean.a +HEADERS=api.h owcpa.h params.h poly.h sample.h verify.h +OBJECTS=kem.o owcpa.o pack3.o packq.o poly.o sample.o verify.o + +CFLAGS=-Wall -Wextra -Wpedantic -Werror -Wmissing-prototypes -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/ntruhrss701/clean/Makefile.Microsoft_nmake b/crypto_kem/ntruhrss701/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..a367a4ec --- /dev/null +++ b/crypto_kem/ntruhrss701/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=libntruhrss701_clean.lib +OBJECTS=kem.obj owcpa.obj pack3.obj packq.obj poly.obj sample.obj verify.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/ntruhrss701/clean/api.h b/crypto_kem/ntruhrss701/clean/api.h new file mode 100644 index 00000000..de442a06 --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/api.h @@ -0,0 +1,19 @@ +#ifndef PQCLEAN_NTRUHRSS701_CLEAN_API_H +#define PQCLEAN_NTRUHRSS701_CLEAN_API_H + +#include + +#define PQCLEAN_NTRUHRSS701_CLEAN_CRYPTO_SECRETKEYBYTES 1450 +#define PQCLEAN_NTRUHRSS701_CLEAN_CRYPTO_PUBLICKEYBYTES 1138 +#define PQCLEAN_NTRUHRSS701_CLEAN_CRYPTO_CIPHERTEXTBYTES 1138 +#define PQCLEAN_NTRUHRSS701_CLEAN_CRYPTO_BYTES 32 + +#define PQCLEAN_NTRUHRSS701_CLEAN_CRYPTO_ALGNAME "NTRU-HRSS701" + +int PQCLEAN_NTRUHRSS701_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); + +int PQCLEAN_NTRUHRSS701_CLEAN_crypto_kem_enc(uint8_t *c, uint8_t *k, const uint8_t *pk); + +int PQCLEAN_NTRUHRSS701_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, const uint8_t *sk); + +#endif diff --git a/crypto_kem/ntruhrss701/clean/kem.c b/crypto_kem/ntruhrss701/clean/kem.c new file mode 100644 index 00000000..8ca6f691 --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/kem.c @@ -0,0 +1,60 @@ +#include + +#include "api.h" +#include "fips202.h" +#include "owcpa.h" +#include "params.h" +#include "randombytes.h" +#include "verify.h" + +// API FUNCTIONS +int PQCLEAN_NTRUHRSS701_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + uint8_t seed[NTRU_SAMPLE_FG_BYTES]; + + randombytes(seed, NTRU_SAMPLE_FG_BYTES); + PQCLEAN_NTRUHRSS701_CLEAN_owcpa_keypair(pk, sk, seed); + + randombytes(sk + NTRU_OWCPA_SECRETKEYBYTES, NTRU_PRFKEYBYTES); + + return 0; +} + +int PQCLEAN_NTRUHRSS701_CLEAN_crypto_kem_enc(uint8_t *c, uint8_t *k, const uint8_t *pk) { + uint8_t rm[NTRU_OWCPA_MSGBYTES]; + uint8_t rm_seed[NTRU_SAMPLE_RM_BYTES]; + + randombytes(rm_seed, NTRU_SAMPLE_RM_BYTES); + PQCLEAN_NTRUHRSS701_CLEAN_owcpa_samplemsg(rm, rm_seed); + + sha3_256(k, rm, NTRU_OWCPA_MSGBYTES); + + PQCLEAN_NTRUHRSS701_CLEAN_owcpa_enc(c, rm, pk); + + return 0; +} + +int PQCLEAN_NTRUHRSS701_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, const uint8_t *sk) { + int i, fail; + uint8_t rm[NTRU_OWCPA_MSGBYTES]; + uint8_t buf[NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES]; + uint8_t *cmp = buf + NTRU_PRFKEYBYTES; + + fail = PQCLEAN_NTRUHRSS701_CLEAN_owcpa_dec(rm, c, sk); + /* If fail = 0 then c = Enc(h, rm), there is no need to re-encapsulate. */ + /* See comment in PQCLEAN_NTRUHRSS701_CLEAN_owcpa_dec for details. */ + + sha3_256(k, rm, NTRU_OWCPA_MSGBYTES); + + /* shake(secret PRF key || input ciphertext) */ + for (i = 0; i < NTRU_PRFKEYBYTES; i++) { + buf[i] = sk[i + NTRU_OWCPA_SECRETKEYBYTES]; + } + for (i = 0; i < NTRU_CIPHERTEXTBYTES; i++) { + cmp[i] = c[i]; + } + sha3_256(rm, cmp, NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES); + + PQCLEAN_NTRUHRSS701_CLEAN_cmov(k, rm, NTRU_SHAREDKEYBYTES, (unsigned char) fail); + + return 0; +} diff --git a/crypto_kem/ntruhrss701/clean/owcpa.c b/crypto_kem/ntruhrss701/clean/owcpa.c new file mode 100644 index 00000000..0e2227a4 --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/owcpa.c @@ -0,0 +1,155 @@ +#include "owcpa.h" +#include "poly.h" +#include "sample.h" + +static int owcpa_check_r(const poly *r) { + /* Check that r is in message space. */ + /* Note: Assumes that r has coefficients in {0, 1, ..., q-1} */ + int i; + uint64_t t = 0; + uint16_t c; + for (i = 0; i < NTRU_N; i++) { + c = MODQ(r->coeffs[i] + 1); + t |= c & (NTRU_Q - 4); /* 0 if c is in {0,1,2,3} */ + t |= (c + 1) & 0x4; /* 0 if c is in {0,1,2} */ + } + t |= r->coeffs[NTRU_N - 1]; /* Coefficient n-1 must be zero */ + t = (~t + 1); // two's complement + t >>= 63; + return (int) t; +} + +void PQCLEAN_NTRUHRSS701_CLEAN_owcpa_samplemsg(unsigned char msg[NTRU_OWCPA_MSGBYTES], + const unsigned char seed[NTRU_SAMPLE_RM_BYTES]) { + poly r, m; + + PQCLEAN_NTRUHRSS701_CLEAN_sample_rm(&r, &m, seed); + + PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_tobytes(msg, &r); + PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_tobytes(msg + NTRU_PACK_TRINARY_BYTES, &m); +} + +void PQCLEAN_NTRUHRSS701_CLEAN_owcpa_keypair(unsigned char *pk, + unsigned char *sk, + const unsigned char seed[NTRU_SAMPLE_FG_BYTES]) { + int i; + + poly x1, x2, x3, x4, x5; + + poly *f = &x1, *invf_mod3 = &x2; + poly *g = &x3, *G = &x2; + poly *Gf = &x3, *invGf = &x4, *tmp = &x5; + poly *invh = &x3, *h = &x3; + + PQCLEAN_NTRUHRSS701_CLEAN_sample_fg(f, g, seed); + + PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_inv(invf_mod3, f); + PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_tobytes(sk, f); + PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_tobytes(sk + NTRU_PACK_TRINARY_BYTES, invf_mod3); + + /* Lift coeffs of f and g from Z_p to Z_q */ + PQCLEAN_NTRUHRSS701_CLEAN_poly_Z3_to_Zq(f); + PQCLEAN_NTRUHRSS701_CLEAN_poly_Z3_to_Zq(g); + + /* G = 3*(x-1)*g */ + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul_x_minus_1(G, g); + for (i = 0; i < NTRU_N; i++) { + G->coeffs[i] = MODQ(3 * G->coeffs[i]); + } + + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(Gf, G, f); + + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_inv(invGf, Gf); + + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(tmp, invGf, f); + PQCLEAN_NTRUHRSS701_CLEAN_poly_Sq_mul(invh, tmp, f); + PQCLEAN_NTRUHRSS701_CLEAN_poly_Sq_tobytes(sk + 2 * NTRU_PACK_TRINARY_BYTES, invh); + + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(tmp, invGf, G); + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(h, tmp, G); + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_sum_zero_tobytes(pk, h); +} + + +void PQCLEAN_NTRUHRSS701_CLEAN_owcpa_enc(unsigned char *c, + const unsigned char *rm, + const unsigned char *pk) { + int i; + poly x1, x2, x3; + poly *h = &x1, *liftm = &x1; + poly *r = &x2, *m = &x2; + poly *ct = &x3; + + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_sum_zero_frombytes(h, pk); + + PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_frombytes(r, rm); + PQCLEAN_NTRUHRSS701_CLEAN_poly_Z3_to_Zq(r); + + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(ct, r, h); + + PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_frombytes(m, rm + NTRU_PACK_TRINARY_BYTES); + PQCLEAN_NTRUHRSS701_CLEAN_poly_lift(liftm, m); + for (i = 0; i < NTRU_N; i++) { + ct->coeffs[i] = MODQ(ct->coeffs[i] + liftm->coeffs[i]); + } + + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_sum_zero_tobytes(c, ct); +} + +int PQCLEAN_NTRUHRSS701_CLEAN_owcpa_dec(unsigned char *rm, + const unsigned char *ciphertext, + const unsigned char *secretkey) { + int i; + int fail; + poly x1, x2, x3, x4; + + poly *c = &x1, *f = &x2, *cf = &x3; + poly *mf = &x2, *finv3 = &x3, *m = &x4; + poly *liftm = &x2, *invh = &x3, *r = &x4; + poly *b = &x1; + + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_sum_zero_frombytes(c, ciphertext); + PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_frombytes(f, secretkey); + PQCLEAN_NTRUHRSS701_CLEAN_poly_Z3_to_Zq(f); + + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(cf, c, f); + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_to_S3(mf, cf); + + PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_frombytes(finv3, secretkey + NTRU_PACK_TRINARY_BYTES); + PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_mul(m, mf, finv3); + PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_tobytes(rm + NTRU_PACK_TRINARY_BYTES, m); + + /* NOTE: For the IND-CCA2 KEM we must ensure that c = Enc(h, (r,m)). */ + /* We can avoid re-computing r*h + Lift(m) as long as we check that */ + /* r (defined as b/h mod (q, Phi_n)) and m are in the message space. */ + /* (m can take any value in S3 in NTRU_HRSS) */ + fail = 0; + + /* b = c - Lift(m) mod (q, x^n - 1) */ + PQCLEAN_NTRUHRSS701_CLEAN_poly_lift(liftm, m); + for (i = 0; i < NTRU_N; i++) { + b->coeffs[i] = MODQ(c->coeffs[i] - liftm->coeffs[i]); + } + + /* r = b / h mod (q, Phi_n) */ + PQCLEAN_NTRUHRSS701_CLEAN_poly_Sq_frombytes(invh, secretkey + 2 * NTRU_PACK_TRINARY_BYTES); + PQCLEAN_NTRUHRSS701_CLEAN_poly_Sq_mul(r, b, invh); + + /* NOTE: Our definition of r as b/h mod (q, Phi_n) follows Figure 4 of */ + /* [Sch18] https://eprint.iacr.org/2018/1174/20181203:032458. */ + /* This differs from Figure 10 of Saito--Xagawa--Yamakawa */ + /* [SXY17] https://eprint.iacr.org/2017/1005/20180516:055500 */ + /* where r gets a final reduction modulo p. */ + /* We need this change to use Proposition 1 of [Sch18]. */ + + /* Proposition 1 of [Sch18] shows that re-encryption with (r,m) yields c. */ + /* if and only if fail==0 after the following call to owcpa_check_r */ + /* The procedure given in Fig. 8 of [Sch18] can be skipped because we have */ + /* c(1) = 0 due to the use of poly_Rq_sum_zero_{to,from}bytes. */ + fail |= owcpa_check_r(r); + + PQCLEAN_NTRUHRSS701_CLEAN_poly_trinary_Zq_to_Z3(r); + PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_tobytes(rm, r); + + return fail; +} diff --git a/crypto_kem/ntruhrss701/clean/owcpa.h b/crypto_kem/ntruhrss701/clean/owcpa.h new file mode 100644 index 00000000..1f2addc9 --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/owcpa.h @@ -0,0 +1,20 @@ +#ifndef OWCPA_H +#define OWCPA_H + +#include "params.h" + +void PQCLEAN_NTRUHRSS701_CLEAN_owcpa_samplemsg(unsigned char msg[NTRU_OWCPA_MSGBYTES], + const unsigned char seed[NTRU_SEEDBYTES]); + +void PQCLEAN_NTRUHRSS701_CLEAN_owcpa_keypair(unsigned char *pk, + unsigned char *sk, + const unsigned char seed[NTRU_SEEDBYTES]); + +void PQCLEAN_NTRUHRSS701_CLEAN_owcpa_enc(unsigned char *c, + const unsigned char *rm, + const unsigned char *pk); + +int PQCLEAN_NTRUHRSS701_CLEAN_owcpa_dec(unsigned char *rm, + const unsigned char *ciphertext, + const unsigned char *secretkey); +#endif diff --git a/crypto_kem/ntruhrss701/clean/pack3.c b/crypto_kem/ntruhrss701/clean/pack3.c new file mode 100644 index 00000000..f27a38f9 --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/pack3.c @@ -0,0 +1,32 @@ +#include "poly.h" + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_tobytes(unsigned char msg[NTRU_OWCPA_MSGBYTES], const poly *a) { + int i; + unsigned char c; + + for (i = 0; i < NTRU_PACK_DEG / 5; i++) { + c = a->coeffs[5 * i + 4] & 255; + c = (3 * c + a->coeffs[5 * i + 3]) & 255; + c = (3 * c + a->coeffs[5 * i + 2]) & 255; + c = (3 * c + a->coeffs[5 * i + 1]) & 255; + c = (3 * c + a->coeffs[5 * i + 0]) & 255; + msg[i] = c; + } + +} + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_frombytes(poly *r, const unsigned char msg[NTRU_OWCPA_MSGBYTES]) { + int i; + unsigned char c; + + for (i = 0; i < NTRU_PACK_DEG / 5; i++) { + c = msg[i]; + r->coeffs[5 * i + 0] = PQCLEAN_NTRUHRSS701_CLEAN_mod3(c); + r->coeffs[5 * i + 1] = PQCLEAN_NTRUHRSS701_CLEAN_mod3(c * 171 >> 9); // this is division by 3 + r->coeffs[5 * i + 2] = PQCLEAN_NTRUHRSS701_CLEAN_mod3(c * 57 >> 9); // division by 3^2 + r->coeffs[5 * i + 3] = PQCLEAN_NTRUHRSS701_CLEAN_mod3(c * 19 >> 9); // division by 3^3 + r->coeffs[5 * i + 4] = PQCLEAN_NTRUHRSS701_CLEAN_mod3(c * 203 >> 14); // etc. + } + r->coeffs[NTRU_N - 1] = 0; +} + diff --git a/crypto_kem/ntruhrss701/clean/packq.c b/crypto_kem/ntruhrss701/clean/packq.c new file mode 100644 index 00000000..ce9e2481 --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/packq.c @@ -0,0 +1,95 @@ +#include "poly.h" + + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Sq_tobytes(unsigned char *r, const poly *a) { + int i, j; + uint16_t t[8]; + + for (i = 0; i < NTRU_PACK_DEG / 8; i++) { + for (j = 0; j < 8; j++) { + t[j] = a->coeffs[8 * i + j]; + } + + r[13 * i + 0] = t[0] & 0xff; + r[13 * i + 1] = (t[0] >> 8) | ((t[1] & 0x07) << 5); + r[13 * i + 2] = (t[1] >> 3) & 0xff; + r[13 * i + 3] = (t[1] >> 11) | ((t[2] & 0x3f) << 2); + r[13 * i + 4] = (t[2] >> 6) | ((t[3] & 0x01) << 7); + r[13 * i + 5] = (t[3] >> 1) & 0xff; + r[13 * i + 6] = (t[3] >> 9) | ((t[4] & 0x0f) << 4); + r[13 * i + 7] = (t[4] >> 4) & 0xff; + r[13 * i + 8] = (t[4] >> 12) | ((t[5] & 0x7f) << 1); + r[13 * i + 9] = (t[5] >> 7) | ((t[6] & 0x03) << 6); + r[13 * i + 10] = (t[6] >> 2) & 0xff; + r[13 * i + 11] = (t[6] >> 10) | ((t[7] & 0x1f) << 3); + r[13 * i + 12] = (t[7] >> 5); + } + + for (j = 0; j < NTRU_PACK_DEG - 8 * i; j++) { + t[j] = a->coeffs[8 * i + j]; + } + for (; j < 8; j++) { + t[j] = 0; + } + + switch (NTRU_PACK_DEG - 8 * (NTRU_PACK_DEG / 8)) { + case 6: + r[13 * i + 9] = (t[5] >> 7) | ((t[6] & 0x03) << 6); + r[13 * i + 8] = (t[4] >> 12) | ((t[5] & 0x7f) << 1); + r[13 * i + 7] = (t[4] >> 4) & 0xff; + // fallthrough + case 4: + r[13 * i + 6] = (t[3] >> 9) | ((t[4] & 0x0f) << 4); + r[13 * i + 5] = (t[3] >> 1) & 0xff; + r[13 * i + 4] = (t[2] >> 6) | ((t[3] & 0x01) << 7); + // fallthrough + case 2: + r[13 * i + 3] = (t[1] >> 11) | ((t[2] & 0x3f) << 2); + r[13 * i + 2] = (t[1] >> 3) & 0xff; + r[13 * i + 1] = (t[0] >> 8) | ((t[1] & 0x07) << 5); + r[13 * i + 0] = t[0] & 0xff; + } +} + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Sq_frombytes(poly *r, const unsigned char *a) { + int i; + for (i = 0; i < NTRU_PACK_DEG / 8; i++) { + r->coeffs[8 * i + 0] = a[13 * i + 0] | (((uint16_t)a[13 * i + 1] & 0x1f) << 8); + r->coeffs[8 * i + 1] = (a[13 * i + 1] >> 5) | (((uint16_t)a[13 * i + 2] ) << 3) | (((uint16_t)a[13 * i + 3] & 0x03) << 11); + r->coeffs[8 * i + 2] = (a[13 * i + 3] >> 2) | (((uint16_t)a[13 * i + 4] & 0x7f) << 6); + r->coeffs[8 * i + 3] = (a[13 * i + 4] >> 7) | (((uint16_t)a[13 * i + 5] ) << 1) | (((uint16_t)a[13 * i + 6] & 0x0f) << 9); + r->coeffs[8 * i + 4] = (a[13 * i + 6] >> 4) | (((uint16_t)a[13 * i + 7] ) << 4) | (((uint16_t)a[13 * i + 8] & 0x01) << 12); + r->coeffs[8 * i + 5] = (a[13 * i + 8] >> 1) | (((uint16_t)a[13 * i + 9] & 0x3f) << 7); + r->coeffs[8 * i + 6] = (a[13 * i + 9] >> 6) | (((uint16_t)a[13 * i + 10] ) << 2) | (((uint16_t)a[13 * i + 11] & 0x07) << 10); + r->coeffs[8 * i + 7] = (a[13 * i + 11] >> 3) | (((uint16_t)a[13 * i + 12] ) << 5); + } + switch (NTRU_PACK_DEG - 8 * (NTRU_PACK_DEG / 8)) { + case 6: + r->coeffs[8 * i + 5] = (a[13 * i + 8] >> 1) | (((uint16_t)a[13 * i + 9] & 0x3f) << 7); + r->coeffs[8 * i + 4] = (a[13 * i + 6] >> 4) | (((uint16_t)a[13 * i + 7] ) << 4) | (((uint16_t)a[13 * i + 8] & 0x01) << 12); + // fallthrough + case 4: + r->coeffs[8 * i + 3] = (a[13 * i + 4] >> 7) | (((uint16_t)a[13 * i + 5] ) << 1) | (((uint16_t)a[13 * i + 6] & 0x0f) << 9); + r->coeffs[8 * i + 2] = (a[13 * i + 3] >> 2) | (((uint16_t)a[13 * i + 4] & 0x7f) << 6); + // fallthrough + case 2: + r->coeffs[8 * i + 1] = (a[13 * i + 1] >> 5) | (((uint16_t)a[13 * i + 2] ) << 3) | (((uint16_t)a[13 * i + 3] & 0x03) << 11); + r->coeffs[8 * i + 0] = a[13 * i + 0] | (((uint16_t)a[13 * i + 1] & 0x1f) << 8); + } +} + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_sum_zero_tobytes(unsigned char *r, const poly *a) { + PQCLEAN_NTRUHRSS701_CLEAN_poly_Sq_tobytes(r, a); +} + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_sum_zero_frombytes(poly *r, const unsigned char *a) { + int i; + PQCLEAN_NTRUHRSS701_CLEAN_poly_Sq_frombytes(r, a); + + /* Set r[n-1] so that the sum of coefficients is zero mod q */ + r->coeffs[NTRU_N - 1] = 0; + for (i = 0; i < NTRU_PACK_DEG; i++) { + r->coeffs[NTRU_N - 1] += r->coeffs[i]; + } + r->coeffs[NTRU_N - 1] = MODQ(-(r->coeffs[NTRU_N - 1])); +} diff --git a/crypto_kem/ntruhrss701/clean/params.h b/crypto_kem/ntruhrss701/clean/params.h new file mode 100644 index 00000000..7948514a --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/params.h @@ -0,0 +1,32 @@ +#ifndef PARAMS_H +#define PARAMS_H + +#define NTRU_HRSS +#define NTRU_N 701 +#define NTRU_LOGQ 13 + +/* Do not modify below this line */ + +#define NTRU_Q (1 << NTRU_LOGQ) + +#define NTRU_SEEDBYTES 32 +#define NTRU_PRFKEYBYTES 32 +#define NTRU_SHAREDKEYBYTES 32 + +#define NTRU_SAMPLE_IID_BYTES (NTRU_N-1) +#define NTRU_SAMPLE_FG_BYTES (2*NTRU_SAMPLE_IID_BYTES) +#define NTRU_SAMPLE_RM_BYTES (2*NTRU_SAMPLE_IID_BYTES) + +#define NTRU_PACK_DEG (NTRU_N-1) +#define NTRU_PACK_TRINARY_BYTES ((NTRU_PACK_DEG+4)/5) + +#define NTRU_OWCPA_MSGBYTES (2*NTRU_PACK_TRINARY_BYTES) +#define NTRU_OWCPA_PUBLICKEYBYTES ((NTRU_LOGQ*NTRU_PACK_DEG+7)/8) +#define NTRU_OWCPA_SECRETKEYBYTES (2*NTRU_PACK_TRINARY_BYTES + NTRU_OWCPA_PUBLICKEYBYTES) +#define NTRU_OWCPA_BYTES ((NTRU_LOGQ*NTRU_PACK_DEG+7)/8) + +#define NTRU_PUBLICKEYBYTES (NTRU_OWCPA_PUBLICKEYBYTES) +#define NTRU_SECRETKEYBYTES (NTRU_OWCPA_SECRETKEYBYTES + NTRU_PRFKEYBYTES) +#define NTRU_CIPHERTEXTBYTES (NTRU_OWCPA_BYTES) + +#endif diff --git a/crypto_kem/ntruhrss701/clean/poly.c b/crypto_kem/ntruhrss701/clean/poly.c new file mode 100644 index 00000000..3d1738fc --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/poly.c @@ -0,0 +1,387 @@ +#include "poly.h" +#include "fips202.h" +#include "verify.h" + +uint16_t PQCLEAN_NTRUHRSS701_CLEAN_mod3(uint16_t a) { + uint16_t r; + int16_t t, c; + + r = (a >> 8) + (a & 0xff); // r mod 255 == a mod 255 + r = (r >> 4) + (r & 0xf); // r' mod 15 == r mod 15 + r = (r >> 2) + (r & 0x3); // r' mod 3 == r mod 3 + r = (r >> 2) + (r & 0x3); // r' mod 3 == r mod 3 + + t = r - 3; + c = t >> 15; + + return (c & r) ^ (~c & t); +} + +/* Map {0, 1, 2} -> {0,1,q-1} in place */ +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Z3_to_Zq(poly *r) { + int i; + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = r->coeffs[i] | ((-(r->coeffs[i] >> 1)) & (NTRU_Q - 1)); + } +} + +/* Map {0, 1, q-1} -> {0,1,2} in place */ +void PQCLEAN_NTRUHRSS701_CLEAN_poly_trinary_Zq_to_Z3(poly *r) { + int i; + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = 3 & (r->coeffs[i] ^ (r->coeffs[i] >> (NTRU_LOGQ - 1))); + } +} + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(poly *r, const poly *a, const poly *b) { + int k, i; + + for (k = 0; k < NTRU_N; k++) { + r->coeffs[k] = 0; + for (i = 1; i < NTRU_N - k; i++) { + r->coeffs[k] += a->coeffs[k + i] * b->coeffs[NTRU_N - i]; + } + for (i = 0; i < k + 1; i++) { + r->coeffs[k] += a->coeffs[k - i] * b->coeffs[i]; + } + r->coeffs[k] = MODQ(r->coeffs[k]); + } +} + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Sq_mul(poly *r, const poly *a, const poly *b) { + int i; + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(r, a, b); + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = MODQ(r->coeffs[i] - r->coeffs[NTRU_N - 1]); + } +} + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_mul(poly *r, const poly *a, const poly *b) { + int k, i; + + for (k = 0; k < NTRU_N; k++) { + r->coeffs[k] = 0; + for (i = 1; i < NTRU_N - k; i++) { + r->coeffs[k] += a->coeffs[k + i] * b->coeffs[NTRU_N - i]; + } + for (i = 0; i < k + 1; i++) { + r->coeffs[k] += a->coeffs[k - i] * b->coeffs[i]; + } + } + for (k = 0; k < NTRU_N; k++) { + r->coeffs[k] = PQCLEAN_NTRUHRSS701_CLEAN_mod3(r->coeffs[k] + 2 * r->coeffs[NTRU_N - 1]); + } +} + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul_x_minus_1(poly *r, const poly *a) { + int i; + uint16_t last_coeff = a->coeffs[NTRU_N - 1]; + + for (i = NTRU_N - 1; i > 0; i--) { + r->coeffs[i] = MODQ(a->coeffs[i - 1] + (NTRU_Q - a->coeffs[i])); + } + r->coeffs[0] = MODQ(last_coeff + (NTRU_Q - a->coeffs[0])); +} + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_lift(poly *r, const poly *a) { + /* NOTE: Assumes input is in {0,1,2}^N */ + /* Produces output in [0,Q-1]^N */ + int i; + poly b; + uint16_t t, zj; + + /* Define z by = delta_{i,0} mod 3: */ + /* t = -1/N mod p = -N mod 3 */ + /* z[0] = 2 - t mod 3 */ + /* z[1] = 0 mod 3 */ + /* z[j] = z[j-1] + t mod 3 */ + /* We'll compute b = a/(x-1) mod (3, Phi) using */ + /* b[0] = , b[1] = , b[2] = */ + /* b[i] = b[i-3] - (a[i] + a[i-1] + a[i-2]) */ + t = 3 - (NTRU_N % 3); + b.coeffs[0] = a->coeffs[0] * (2 - t) + a->coeffs[1] * 0 + a->coeffs[2] * t; + b.coeffs[1] = a->coeffs[1] * (2 - t) + a->coeffs[2] * 0; + b.coeffs[2] = a->coeffs[2] * (2 - t); + + zj = 0; /* z[1] */ + for (i = 3; i < NTRU_N; i++) { + b.coeffs[0] += a->coeffs[i] * (zj + 2 * t); + b.coeffs[1] += a->coeffs[i] * (zj + t); + b.coeffs[2] += a->coeffs[i] * zj; + zj = (zj + t) % 3; + } + b.coeffs[1] += a->coeffs[0] * (zj + t); + b.coeffs[2] += a->coeffs[0] * zj; + b.coeffs[2] += a->coeffs[1] * (zj + t); + + b.coeffs[0] = b.coeffs[0]; + b.coeffs[1] = b.coeffs[1]; + b.coeffs[2] = b.coeffs[2]; + for (i = 3; i < NTRU_N; i++) { + b.coeffs[i] = b.coeffs[i - 3] + 2 * (a->coeffs[i] + a->coeffs[i - 1] + a->coeffs[i - 2]); + } + + /* Finish reduction mod Phi by subtracting Phi * b[N-1] */ + for (i = 0; i < NTRU_N; i++) { + b.coeffs[i] = PQCLEAN_NTRUHRSS701_CLEAN_mod3(b.coeffs[i] + 2 * b.coeffs[NTRU_N - 1]); + } + + /* Switch from {0,1,2} to {0,1,q-1} coefficient representation */ + PQCLEAN_NTRUHRSS701_CLEAN_poly_Z3_to_Zq(&b); + + /* Multiply by (x-1) */ + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul_x_minus_1(r, &b); +} + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_to_S3(poly *r, const poly *a) { + /* NOTE: Assumes input is in [0,Q-1]^N */ + /* Produces output in {0,1,2}^N */ + int i; + + /* Center coeffs around 3Q: [0, Q-1] -> [3Q - Q/2, 3Q + Q/2) */ + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = ((a->coeffs[i] >> (NTRU_LOGQ - 1)) ^ 3) << NTRU_LOGQ; + r->coeffs[i] += a->coeffs[i]; + } + /* Reduce mod (3, Phi) */ + r->coeffs[NTRU_N - 1] = PQCLEAN_NTRUHRSS701_CLEAN_mod3(r->coeffs[NTRU_N - 1]); + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = PQCLEAN_NTRUHRSS701_CLEAN_mod3(r->coeffs[i] + 2 * r->coeffs[NTRU_N - 1]); + } +} + +#define POLY_R2_ADD(I,A,B,S) \ + for ((I)=0; (I)coeffs[i] ^ b->coeffs[i]) & swap; + a->coeffs[i] ^= t; + b->coeffs[i] ^= t; + } +} + +static inline void poly_divx(poly *a, int s) { + int i; + + for (i = 1; i < NTRU_N; i++) { + a->coeffs[i - 1] = (unsigned char) ((s * a->coeffs[i]) | (!s * a->coeffs[i - 1])); + } + a->coeffs[NTRU_N - 1] = (!s * a->coeffs[NTRU_N - 1]); +} + +static inline void poly_mulx(poly *a, int s) { + int i; + + for (i = 1; i < NTRU_N; i++) { + a->coeffs[NTRU_N - i] = (unsigned char) ((s * a->coeffs[NTRU_N - i - 1]) | (!s * a->coeffs[NTRU_N - i])); + } + a->coeffs[0] = (!s * a->coeffs[0]); +} + +static void poly_R2_inv(poly *r, const poly *a) { + /* Schroeppel--Orman--O'Malley--Spatscheck + * "Almost Inverse" algorithm as described + * by Silverman in NTRU Tech Report #14 */ + // with several modifications to make it run in constant-time + int i, j; + int k = 0; + uint16_t degf = NTRU_N - 1; + uint16_t degg = NTRU_N - 1; + int sign, t, swap; + int16_t done = 0; + poly b, f, g; + poly *c = r; // save some stack space + poly *temp_r = &f; + + /* b(X) := 1 */ + for (i = 1; i < NTRU_N; i++) { + b.coeffs[i] = 0; + } + b.coeffs[0] = 1; + + /* c(X) := 0 */ + for (i = 0; i < NTRU_N; i++) { + c->coeffs[i] = 0; + } + + /* f(X) := a(X) */ + for (i = 0; i < NTRU_N; i++) { + f.coeffs[i] = a->coeffs[i] & 1; + } + + /* g(X) := 1 + X + X^2 + ... + X^{N-1} */ + for (i = 0; i < NTRU_N; i++) { + g.coeffs[i] = 1; + } + + for (j = 0; j < 2 * (NTRU_N - 1) - 1; j++) { + sign = f.coeffs[0]; + swap = sign & !done & ((degf - degg) >> 15); + + cswappoly(&f, &g, swap); + cswappoly(&b, c, swap); + t = (degf ^ degg) & (-swap); + degf ^= t; + degg ^= t; + + POLY_R2_ADD(i, f, g, sign * (!done)); + POLY_R2_ADD(i, b, (*c), sign * (!done)); + + poly_divx(&f, !done); + poly_mulx(c, !done); + degf -= !done; + k += !done; + + done = 1 - (((uint16_t) - degf) >> 15); + } + + k = k - NTRU_N * ((uint16_t)(NTRU_N - k - 1) >> 15); + + /* Return X^{N-k} * b(X) */ + /* This is a k-coefficient rotation. We do this by looking at the binary + representation of k, rotating for every power of 2, and performing a cmov + if the respective bit is set. */ + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = b.coeffs[i]; + } + + for (i = 0; i < 10; i++) { + for (j = 0; j < NTRU_N; j++) { + temp_r->coeffs[j] = r->coeffs[(j + (1 << i)) % NTRU_N]; + } + PQCLEAN_NTRUHRSS701_CLEAN_cmov((unsigned char *) & (r->coeffs), + (unsigned char *) & (temp_r->coeffs), sizeof(uint16_t) * NTRU_N, k & 1); + k >>= 1; + } +} + +static void poly_R2_inv_to_Rq_inv(poly *r, const poly *ai, const poly *a) { + + int i; + poly b, c; + poly s; + + // for 0..4 + // ai = ai * (2 - a*ai) mod q + for (i = 0; i < NTRU_N; i++) { + b.coeffs[i] = MODQ(NTRU_Q - a->coeffs[i]); // b = -a + } + + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = ai->coeffs[i]; + } + + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(&c, r, &b); + c.coeffs[0] += 2; // c = 2 - a*ai + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(&s, &c, r); // s = ai*c + + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(&c, &s, &b); + c.coeffs[0] += 2; // c = 2 - a*s + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(r, &c, &s); // r = s*c + + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(&c, r, &b); + c.coeffs[0] += 2; // c = 2 - a*r + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(&s, &c, r); // s = r*c + + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(&c, &s, &b); + c.coeffs[0] += 2; // c = 2 - a*s + PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(r, &c, &s); // r = s*c +} + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_inv(poly *r, const poly *a) { + poly ai2; + poly_R2_inv(&ai2, a); + poly_R2_inv_to_Rq_inv(r, &ai2, a); +} + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_inv(poly *r, const poly *a) { + /* Schroeppel--Orman--O'Malley--Spatscheck + * "Almost Inverse" algorithm as described + * by Silverman in NTRU Tech Report #14 */ + // with several modifications to make it run in constant-time + int i, j; + uint16_t k = 0; + uint16_t degf = NTRU_N - 1; + uint16_t degg = NTRU_N - 1; + int sign, fsign = 0, t, swap; + int16_t done = 0; + poly b, c, f, g; + poly *temp_r = &f; + + /* b(X) := 1 */ + for (i = 1; i < NTRU_N; i++) { + b.coeffs[i] = 0; + } + b.coeffs[0] = 1; + + /* c(X) := 0 */ + for (i = 0; i < NTRU_N; i++) { + c.coeffs[i] = 0; + } + + /* f(X) := a(X) */ + for (i = 0; i < NTRU_N; i++) { + f.coeffs[i] = a->coeffs[i]; + } + + /* g(X) := 1 + X + X^2 + ... + X^{N-1} */ + for (i = 0; i < NTRU_N; i++) { + g.coeffs[i] = 1; + } + + for (j = 0; j < 2 * (NTRU_N - 1) - 1; j++) { + sign = PQCLEAN_NTRUHRSS701_CLEAN_mod3(2 * g.coeffs[0] * f.coeffs[0]); + swap = (((sign & 2) >> 1) | sign) & !done & ((degf - degg) >> 15); + + cswappoly(&f, &g, swap); + cswappoly(&b, &c, swap); + t = (degf ^ degg) & (-swap); + degf ^= t; + degg ^= t; + + for (i = 0; i < NTRU_N; i++) { + f.coeffs[i] = PQCLEAN_NTRUHRSS701_CLEAN_mod3(f.coeffs[i] + ((uint16_t) (sign * (!done))) * g.coeffs[i]); + } + for (i = 0; i < NTRU_N; i++) { + b.coeffs[i] = PQCLEAN_NTRUHRSS701_CLEAN_mod3(b.coeffs[i] + ((uint16_t) (sign * (!done))) * c.coeffs[i]); + } + + poly_divx(&f, !done); + poly_mulx(&c, !done); + degf -= !done; + k += !done; + + done = 1 - (((uint16_t) - degf) >> 15); + } + + fsign = f.coeffs[0]; + k = k - NTRU_N * ((uint16_t)(NTRU_N - k - 1) >> 15); + + /* Return X^{N-k} * b(X) */ + /* This is a k-coefficient rotation. We do this by looking at the binary + representation of k, rotating for every power of 2, and performing a cmov + if the respective bit is set. */ + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = PQCLEAN_NTRUHRSS701_CLEAN_mod3((uint16_t) fsign * b.coeffs[i]); + } + + for (i = 0; i < 10; i++) { + for (j = 0; j < NTRU_N; j++) { + temp_r->coeffs[j] = r->coeffs[(j + (1 << i)) % NTRU_N]; + } + PQCLEAN_NTRUHRSS701_CLEAN_cmov((unsigned char *) & (r->coeffs), + (unsigned char *) & (temp_r->coeffs), sizeof(uint16_t) * NTRU_N, k & 1); + k >>= 1; + } + + /* Reduce modulo Phi_n */ + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = PQCLEAN_NTRUHRSS701_CLEAN_mod3(r->coeffs[i] + 2 * r->coeffs[NTRU_N - 1]); + } +} diff --git a/crypto_kem/ntruhrss701/clean/poly.h b/crypto_kem/ntruhrss701/clean/poly.h new file mode 100644 index 00000000..4935987b --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/poly.h @@ -0,0 +1,38 @@ +#ifndef POLY_H +#define POLY_H + +#include + +#include "params.h" + +#define MODQ(X) ((X) & (NTRU_Q-1)) +uint16_t PQCLEAN_NTRUHRSS701_CLEAN_mod3(uint16_t a); + +typedef struct { + uint16_t coeffs[NTRU_N]; +} poly; + + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Sq_tobytes(unsigned char *r, const poly *a); +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Sq_frombytes(poly *r, const unsigned char *a); + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_sum_zero_tobytes(unsigned char *r, const poly *a); +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_sum_zero_frombytes(poly *r, const unsigned char *a); + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_tobytes(unsigned char msg[NTRU_PACK_TRINARY_BYTES], const poly *a); +void PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_frombytes(poly *r, const unsigned char msg[NTRU_PACK_TRINARY_BYTES]); + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Sq_mul(poly *r, const poly *a, const poly *b); +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul(poly *r, const poly *a, const poly *b); +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_mul_x_minus_1(poly *r, const poly *a); +void PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_mul(poly *r, const poly *a, const poly *b); +void PQCLEAN_NTRUHRSS701_CLEAN_poly_lift(poly *r, const poly *a); +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_to_S3(poly *r, const poly *a); + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Rq_inv(poly *r, const poly *a); +void PQCLEAN_NTRUHRSS701_CLEAN_poly_S3_inv(poly *r, const poly *a); + +void PQCLEAN_NTRUHRSS701_CLEAN_poly_Z3_to_Zq(poly *r); +void PQCLEAN_NTRUHRSS701_CLEAN_poly_trinary_Zq_to_Z3(poly *r); + +#endif diff --git a/crypto_kem/ntruhrss701/clean/sample.c b/crypto_kem/ntruhrss701/clean/sample.c new file mode 100644 index 00000000..f1030904 --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/sample.c @@ -0,0 +1,54 @@ +#include "sample.h" +#include "fips202.h" + +void PQCLEAN_NTRUHRSS701_CLEAN_sample_fg(poly *f, poly *g, const unsigned char uniformbytes[NTRU_SAMPLE_FG_BYTES]) { + PQCLEAN_NTRUHRSS701_CLEAN_sample_iid_plus(f, uniformbytes); + PQCLEAN_NTRUHRSS701_CLEAN_sample_iid_plus(g, uniformbytes + NTRU_SAMPLE_IID_BYTES); +} + +void PQCLEAN_NTRUHRSS701_CLEAN_sample_rm(poly *r, poly *m, const unsigned char uniformbytes[NTRU_SAMPLE_RM_BYTES]) { + PQCLEAN_NTRUHRSS701_CLEAN_sample_iid(r, uniformbytes); + PQCLEAN_NTRUHRSS701_CLEAN_sample_iid(m, uniformbytes + NTRU_SAMPLE_IID_BYTES); +} + +void PQCLEAN_NTRUHRSS701_CLEAN_sample_iid(poly *r, const unsigned char uniformbytes[NTRU_SAMPLE_IID_BYTES]) { + int i; + /* {0,1,...,255} -> {0,1,2}; Pr[0] = 86/256, Pr[1] = Pr[-1] = 85/256 */ + for (i = 0; i < NTRU_N - 1; i++) { + r->coeffs[i] = PQCLEAN_NTRUHRSS701_CLEAN_mod3(uniformbytes[i]); + } + + r->coeffs[NTRU_N - 1] = 0; +} + +void PQCLEAN_NTRUHRSS701_CLEAN_sample_iid_plus(poly *r, const unsigned char uniformbytes[NTRU_SAMPLE_IID_BYTES]) { + /* Sample r using sample_iid then conditionally flip */ + /* signs of even index coefficients so that >= 0. */ + + int i; + uint16_t s = 0; + + PQCLEAN_NTRUHRSS701_CLEAN_sample_iid(r, uniformbytes); + + /* Map {0,1,2} -> {0, 1, 2^16 - 1} */ + for (i = 0; i < NTRU_N - 1; i++) { + r->coeffs[i] = r->coeffs[i] | (-(r->coeffs[i] >> 1)); + } + + /* s = . (r[n-1] = 0) */ + for (i = 0; i < NTRU_N - 1; i++) { + s += r->coeffs[i + 1] * r->coeffs[i]; + } + + /* Extract sign of s (sign(0) = 1) */ + s = 1 | (-(s >> 15)); + + for (i = 0; i < NTRU_N; i += 2) { + r->coeffs[i] = s * r->coeffs[i]; + } + + /* Map {0,1,2^16-1} -> {0, 1, 2} */ + for (i = 0; i < NTRU_N; i++) { + r->coeffs[i] = 3 & (r->coeffs[i] ^ (r->coeffs[i] >> 15)); + } +} diff --git a/crypto_kem/ntruhrss701/clean/sample.h b/crypto_kem/ntruhrss701/clean/sample.h new file mode 100644 index 00000000..518a9433 --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/sample.h @@ -0,0 +1,16 @@ +#ifndef SAMPLE_H +#define SAMPLE_H + +#include + +#include "params.h" +#include "poly.h" + +void PQCLEAN_NTRUHRSS701_CLEAN_sample_fg(poly *f, poly *g, const unsigned char uniformbytes[NTRU_SAMPLE_FG_BYTES]); +void PQCLEAN_NTRUHRSS701_CLEAN_sample_rm(poly *r, poly *m, const unsigned char uniformbytes[NTRU_SAMPLE_RM_BYTES]); + +void PQCLEAN_NTRUHRSS701_CLEAN_sample_iid(poly *r, const unsigned char uniformbytes[NTRU_SAMPLE_IID_BYTES]); + +void PQCLEAN_NTRUHRSS701_CLEAN_sample_iid_plus(poly *r, const unsigned char uniformbytes[NTRU_SAMPLE_IID_BYTES]); + +#endif diff --git a/crypto_kem/ntruhrss701/clean/verify.c b/crypto_kem/ntruhrss701/clean/verify.c new file mode 100644 index 00000000..8df86250 --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/verify.c @@ -0,0 +1,29 @@ +#include +#include + +#include "verify.h" + +/* returns 0 for equal strings, 1 for non-equal strings */ +unsigned char PQCLEAN_NTRUHRSS701_CLEAN_verify(const unsigned char *a, const unsigned char *b, size_t len) { + uint64_t r; + size_t i; + + r = 0; + for (i = 0; i < len; i++) { + r |= a[i] ^ b[i]; + } + + r = (~r + 1); // Two's complement + r >>= 63; + return (unsigned char)r; +} + +/* b = 1 means mov, b = 0 means don't mov*/ +void PQCLEAN_NTRUHRSS701_CLEAN_cmov(unsigned char *r, const unsigned char *x, size_t len, unsigned char b) { + size_t i; + + b = (~b + 1); // Two's complement + for (i = 0; i < len; i++) { + r[i] ^= b & (x[i] ^ r[i]); + } +} diff --git a/crypto_kem/ntruhrss701/clean/verify.h b/crypto_kem/ntruhrss701/clean/verify.h new file mode 100644 index 00000000..d9507c76 --- /dev/null +++ b/crypto_kem/ntruhrss701/clean/verify.h @@ -0,0 +1,12 @@ +#ifndef VERIFY_H +#define VERIFY_H + +#include + +/* returns 0 for equal strings, 1 for non-equal strings */ +unsigned char PQCLEAN_NTRUHRSS701_CLEAN_verify(const unsigned char *a, const unsigned char *b, size_t len); + +/* b = 1 means mov, b = 0 means don't mov*/ +void PQCLEAN_NTRUHRSS701_CLEAN_cmov(unsigned char *r, const unsigned char *x, size_t len, unsigned char b); + +#endif diff --git a/test/duplicate_consistency/ntruhrss701_clean.yml b/test/duplicate_consistency/ntruhrss701_clean.yml new file mode 100644 index 00000000..cab7beaf --- /dev/null +++ b/test/duplicate_consistency/ntruhrss701_clean.yml @@ -0,0 +1,14 @@ +consistency_checks: +- source: + scheme: ntruhps2048509 + implementation: clean + files: + - kem.c + - poly.h + - verify.c + - verify.h +- source: + scheme: ntruhps4096821 + implementation: clean + files: + - pack3.c From 91f94cdd1cb89279d650bf11e54613db26a621d5 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Sun, 14 Apr 2019 20:50:16 -0400 Subject: [PATCH 5/7] Fix Windows compiler warnings --- crypto_kem/ntruhps4096821/clean/packq.c | 10 +++--- crypto_kem/ntruhrss701/clean/packq.c | 46 ++++++++++++------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/crypto_kem/ntruhps4096821/clean/packq.c b/crypto_kem/ntruhps4096821/clean/packq.c index f23ae357..d31934bb 100644 --- a/crypto_kem/ntruhps4096821/clean/packq.c +++ b/crypto_kem/ntruhps4096821/clean/packq.c @@ -5,17 +5,17 @@ void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_tobytes(unsigned char *r, const poly * int i; for (i = 0; i < NTRU_PACK_DEG / 2; i++) { - r[3 * i + 0] = a->coeffs[2 * i + 0] & 0xff; - r[3 * i + 1] = (a->coeffs[2 * i + 0] >> 8) | ((a->coeffs[2 * i + 1] & 0x0f) << 4); - r[3 * i + 2] = (a->coeffs[2 * i + 1] >> 4); + r[3 * i + 0] = (unsigned char) ( a->coeffs[2 * i + 0] & 0xff); + r[3 * i + 1] = (unsigned char) ((a->coeffs[2 * i + 0] >> 8) | ((a->coeffs[2 * i + 1] & 0x0f) << 4)); + r[3 * i + 2] = (unsigned char) ((a->coeffs[2 * i + 1] >> 4)); } } void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_frombytes(poly *r, const unsigned char *a) { int i; for (i = 0; i < NTRU_PACK_DEG / 2; i++) { - r->coeffs[2 * i + 0] = (a[3 * i + 0] >> 0) | (((uint16_t)a[3 * i + 1] & 0x0f) << 8); - r->coeffs[2 * i + 1] = (a[3 * i + 1] >> 4) | (((uint16_t)a[3 * i + 2] & 0xff) << 4); + r->coeffs[2 * i + 0] = (unsigned char) ((a[3 * i + 0] >> 0) | (((uint16_t)a[3 * i + 1] & 0x0f) << 8)); + r->coeffs[2 * i + 1] = (unsigned char) ((a[3 * i + 1] >> 4) | (((uint16_t)a[3 * i + 2] & 0xff) << 4)); } } diff --git a/crypto_kem/ntruhrss701/clean/packq.c b/crypto_kem/ntruhrss701/clean/packq.c index ce9e2481..238db8a6 100644 --- a/crypto_kem/ntruhrss701/clean/packq.c +++ b/crypto_kem/ntruhrss701/clean/packq.c @@ -10,19 +10,19 @@ void PQCLEAN_NTRUHRSS701_CLEAN_poly_Sq_tobytes(unsigned char *r, const poly *a) t[j] = a->coeffs[8 * i + j]; } - r[13 * i + 0] = t[0] & 0xff; - r[13 * i + 1] = (t[0] >> 8) | ((t[1] & 0x07) << 5); - r[13 * i + 2] = (t[1] >> 3) & 0xff; - r[13 * i + 3] = (t[1] >> 11) | ((t[2] & 0x3f) << 2); - r[13 * i + 4] = (t[2] >> 6) | ((t[3] & 0x01) << 7); - r[13 * i + 5] = (t[3] >> 1) & 0xff; - r[13 * i + 6] = (t[3] >> 9) | ((t[4] & 0x0f) << 4); - r[13 * i + 7] = (t[4] >> 4) & 0xff; - r[13 * i + 8] = (t[4] >> 12) | ((t[5] & 0x7f) << 1); - r[13 * i + 9] = (t[5] >> 7) | ((t[6] & 0x03) << 6); - r[13 * i + 10] = (t[6] >> 2) & 0xff; - r[13 * i + 11] = (t[6] >> 10) | ((t[7] & 0x1f) << 3); - r[13 * i + 12] = (t[7] >> 5); + r[13 * i + 0] = (unsigned char) ( t[0] & 0xff); + r[13 * i + 1] = (unsigned char) ((t[0] >> 8) | ((t[1] & 0x07) << 5)); + r[13 * i + 2] = (unsigned char) ((t[1] >> 3) & 0xff); + r[13 * i + 3] = (unsigned char) ((t[1] >> 11) | ((t[2] & 0x3f) << 2)); + r[13 * i + 4] = (unsigned char) ((t[2] >> 6) | ((t[3] & 0x01) << 7)); + r[13 * i + 5] = (unsigned char) ((t[3] >> 1) & 0xff); + r[13 * i + 6] = (unsigned char) ((t[3] >> 9) | ((t[4] & 0x0f) << 4)); + r[13 * i + 7] = (unsigned char) ((t[4] >> 4) & 0xff); + r[13 * i + 8] = (unsigned char) ((t[4] >> 12) | ((t[5] & 0x7f) << 1)); + r[13 * i + 9] = (unsigned char) ((t[5] >> 7) | ((t[6] & 0x03) << 6)); + r[13 * i + 10] = (unsigned char) ((t[6] >> 2) & 0xff); + r[13 * i + 11] = (unsigned char) ((t[6] >> 10) | ((t[7] & 0x1f) << 3)); + r[13 * i + 12] = (unsigned char) ((t[7] >> 5)); } for (j = 0; j < NTRU_PACK_DEG - 8 * i; j++) { @@ -34,20 +34,20 @@ void PQCLEAN_NTRUHRSS701_CLEAN_poly_Sq_tobytes(unsigned char *r, const poly *a) switch (NTRU_PACK_DEG - 8 * (NTRU_PACK_DEG / 8)) { case 6: - r[13 * i + 9] = (t[5] >> 7) | ((t[6] & 0x03) << 6); - r[13 * i + 8] = (t[4] >> 12) | ((t[5] & 0x7f) << 1); - r[13 * i + 7] = (t[4] >> 4) & 0xff; + r[13 * i + 9] = (unsigned char) ((t[5] >> 7) | ((t[6] & 0x03) << 6)); + r[13 * i + 8] = (unsigned char) ((t[4] >> 12) | ((t[5] & 0x7f) << 1)); + r[13 * i + 7] = (unsigned char) ((t[4] >> 4) & 0xff); // fallthrough case 4: - r[13 * i + 6] = (t[3] >> 9) | ((t[4] & 0x0f) << 4); - r[13 * i + 5] = (t[3] >> 1) & 0xff; - r[13 * i + 4] = (t[2] >> 6) | ((t[3] & 0x01) << 7); + r[13 * i + 6] = (unsigned char) ((t[3] >> 9) | ((t[4] & 0x0f) << 4)); + r[13 * i + 5] = (unsigned char) ((t[3] >> 1) & 0xff); + r[13 * i + 4] = (unsigned char) ((t[2] >> 6) | ((t[3] & 0x01) << 7)); // fallthrough case 2: - r[13 * i + 3] = (t[1] >> 11) | ((t[2] & 0x3f) << 2); - r[13 * i + 2] = (t[1] >> 3) & 0xff; - r[13 * i + 1] = (t[0] >> 8) | ((t[1] & 0x07) << 5); - r[13 * i + 0] = t[0] & 0xff; + r[13 * i + 3] = (unsigned char) ((t[1] >> 11) | ((t[2] & 0x3f) << 2)); + r[13 * i + 2] = (unsigned char) ((t[1] >> 3) & 0xff); + r[13 * i + 1] = (unsigned char) ((t[0] >> 8) | ((t[1] & 0x07) << 5)); + r[13 * i + 0] = (unsigned char) ( t[0] & 0xff); } } From 6fce9cf1b9bbecdbd5106db46ffcfcbc27cdb4cd Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Mon, 15 Apr 2019 09:48:56 -0400 Subject: [PATCH 6/7] Undo breaking compiler warning fix --- crypto_kem/ntruhps4096821/clean/packq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto_kem/ntruhps4096821/clean/packq.c b/crypto_kem/ntruhps4096821/clean/packq.c index d31934bb..c0ffe4cc 100644 --- a/crypto_kem/ntruhps4096821/clean/packq.c +++ b/crypto_kem/ntruhps4096821/clean/packq.c @@ -14,8 +14,8 @@ void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_tobytes(unsigned char *r, const poly * void PQCLEAN_NTRUHPS4096821_CLEAN_poly_Sq_frombytes(poly *r, const unsigned char *a) { int i; for (i = 0; i < NTRU_PACK_DEG / 2; i++) { - r->coeffs[2 * i + 0] = (unsigned char) ((a[3 * i + 0] >> 0) | (((uint16_t)a[3 * i + 1] & 0x0f) << 8)); - r->coeffs[2 * i + 1] = (unsigned char) ((a[3 * i + 1] >> 4) | (((uint16_t)a[3 * i + 2] & 0xff) << 4)); + r->coeffs[2 * i + 0] = (a[3 * i + 0] >> 0) | (((uint16_t)a[3 * i + 1] & 0x0f) << 8); + r->coeffs[2 * i + 1] = (a[3 * i + 1] >> 4) | (((uint16_t)a[3 * i + 2] & 0xff) << 4); } } From 4924ea0a6a88e51bc4c863eeb89a72d053147ee8 Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Tue, 16 Apr 2019 21:11:36 -0400 Subject: [PATCH 7/7] Copy ntru fixes from recent commits --- crypto_kem/ntruhps2048509/clean/sample.c | 8 ++++---- crypto_kem/ntruhps2048677/clean/crypto_sort.c | 2 +- crypto_kem/ntruhps2048677/clean/kem.c | 5 ++--- crypto_kem/ntruhps2048677/clean/sample.c | 10 +++++----- crypto_kem/ntruhps4096821/clean/crypto_sort.c | 2 +- crypto_kem/ntruhps4096821/clean/kem.c | 5 ++--- crypto_kem/ntruhps4096821/clean/sample.c | 10 +++++----- crypto_kem/ntruhrss701/clean/kem.c | 5 ++--- 8 files changed, 22 insertions(+), 25 deletions(-) diff --git a/crypto_kem/ntruhps2048509/clean/sample.c b/crypto_kem/ntruhps2048509/clean/sample.c index 461e15f4..c4fc9709 100644 --- a/crypto_kem/ntruhps2048509/clean/sample.c +++ b/crypto_kem/ntruhps2048509/clean/sample.c @@ -30,10 +30,10 @@ void PQCLEAN_NTRUHPS2048509_CLEAN_sample_fixed_type(poly *r, const unsigned char // Use 30 bits of u per word for (i = 0; i < (NTRU_N - 1) / 4; i++) { - s[4 * i + 0] = (u[15 * i + 0] << 2) + (u[15 * i + 1] << 10) + (u[15 * i + 2] << 18) + ((uint32_t)u[15 * i + 3] << 26); - s[4 * i + 1] = ((u[15 * i + 3] & 0xc0) >> 4) + (u[15 * i + 4] << 4) + (u[15 * i + 5] << 12) + (u[15 * i + 6] << 20) + ((uint32_t)u[15 * i + 7] << 28); - s[4 * i + 2] = ((u[15 * i + 7] & 0xf0) >> 2) + (u[15 * i + 8] << 6) + (u[15 * i + 9] << 14) + (u[15 * i + 10] << 22) + ((uint32_t)u[15 * i + 11] << 30); - s[4 * i + 3] = (u[15 * i + 11] & 0xfc) + (u[15 * i + 12] << 8) + (u[15 * i + 13] << 15) + ((uint32_t)u[15 * i + 14] << 24); + s[4 * i + 0] = (u[15 * i + 0] << 2) + (u[15 * i + 1] << 10) + (u[15 * i + 2] << 18) + ((uint32_t) u[15 * i + 3] << 26); + s[4 * i + 1] = ((u[15 * i + 3] & 0xc0) >> 4) + (u[15 * i + 4] << 4) + (u[15 * i + 5] << 12) + (u[15 * i + 6] << 20) + ((uint32_t) u[15 * i + 7] << 28); + s[4 * i + 2] = ((u[15 * i + 7] & 0xf0) >> 2) + (u[15 * i + 8] << 6) + (u[15 * i + 9] << 14) + (u[15 * i + 10] << 22) + ((uint32_t) u[15 * i + 11] << 30); + s[4 * i + 3] = (u[15 * i + 11] & 0xfc) + (u[15 * i + 12] << 8) + (u[15 * i + 13] << 15) + ((uint32_t) u[15 * i + 14] << 24); } for (i = 0; i < NTRU_WEIGHT / 2; i++) { diff --git a/crypto_kem/ntruhps2048677/clean/crypto_sort.c b/crypto_kem/ntruhps2048677/clean/crypto_sort.c index 7b36fa70..1cb88c95 100644 --- a/crypto_kem/ntruhps2048677/clean/crypto_sort.c +++ b/crypto_kem/ntruhps2048677/clean/crypto_sort.c @@ -8,7 +8,7 @@ #define int32_MINMAX(a,b) \ do { \ int32_t ab = (b) ^ (a); \ - int32_t c = (b) - (a); \ + int32_t c = (int32_t)((int64_t)(b) - (int64_t)(a)); \ c ^= ab & (c ^ (b)); \ c >>= 31; \ c &= ab; \ diff --git a/crypto_kem/ntruhps2048677/clean/kem.c b/crypto_kem/ntruhps2048677/clean/kem.c index 80123ea1..d3ff7a15 100644 --- a/crypto_kem/ntruhps2048677/clean/kem.c +++ b/crypto_kem/ntruhps2048677/clean/kem.c @@ -37,7 +37,6 @@ int PQCLEAN_NTRUHPS2048677_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, co int i, fail; uint8_t rm[NTRU_OWCPA_MSGBYTES]; uint8_t buf[NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES]; - uint8_t *cmp = buf + NTRU_PRFKEYBYTES; fail = PQCLEAN_NTRUHPS2048677_CLEAN_owcpa_dec(rm, c, sk); /* If fail = 0 then c = Enc(h, rm), there is no need to re-encapsulate. */ @@ -50,9 +49,9 @@ int PQCLEAN_NTRUHPS2048677_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, co buf[i] = sk[i + NTRU_OWCPA_SECRETKEYBYTES]; } for (i = 0; i < NTRU_CIPHERTEXTBYTES; i++) { - cmp[i] = c[i]; + buf[NTRU_PRFKEYBYTES + i] = c[i]; } - sha3_256(rm, cmp, NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES); + sha3_256(rm, buf, NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES); PQCLEAN_NTRUHPS2048677_CLEAN_cmov(k, rm, NTRU_SHAREDKEYBYTES, (unsigned char) fail); diff --git a/crypto_kem/ntruhps2048677/clean/sample.c b/crypto_kem/ntruhps2048677/clean/sample.c index a462b214..7cc893ad 100644 --- a/crypto_kem/ntruhps2048677/clean/sample.c +++ b/crypto_kem/ntruhps2048677/clean/sample.c @@ -25,15 +25,15 @@ void PQCLEAN_NTRUHPS2048677_CLEAN_sample_iid(poly *r, const unsigned char unifor void PQCLEAN_NTRUHPS2048677_CLEAN_sample_fixed_type(poly *r, const unsigned char u[NTRU_SAMPLE_FT_BYTES]) { // Assumes NTRU_SAMPLE_FT_BYTES = ceil(30*(n-1)/8) - int32_t s[NTRU_N - 1]; + uint32_t s[NTRU_N - 1]; int i; // Use 30 bits of u per word for (i = 0; i < (NTRU_N - 1) / 4; i++) { - s[4 * i + 0] = (u[15 * i + 0] << 2) + (u[15 * i + 1] << 10) + (u[15 * i + 2] << 18) + (u[15 * i + 3] << 26); - s[4 * i + 1] = ((u[15 * i + 3] & 0xc0) >> 4) + (u[15 * i + 4] << 4) + (u[15 * i + 5] << 12) + (u[15 * i + 6] << 20) + (u[15 * i + 7] << 28); - s[4 * i + 2] = ((u[15 * i + 7] & 0xf0) >> 2) + (u[15 * i + 8] << 6) + (u[15 * i + 9] << 14) + (u[15 * i + 10] << 22) + (u[15 * i + 11] << 30); - s[4 * i + 3] = (u[15 * i + 11] & 0xfc) + (u[15 * i + 12] << 8) + (u[15 * i + 13] << 15) + (u[15 * i + 14] << 24); + s[4 * i + 0] = (u[15 * i + 0] << 2) + (u[15 * i + 1] << 10) + (u[15 * i + 2] << 18) + ((uint32_t) u[15 * i + 3] << 26); + s[4 * i + 1] = ((u[15 * i + 3] & 0xc0) >> 4) + (u[15 * i + 4] << 4) + (u[15 * i + 5] << 12) + (u[15 * i + 6] << 20) + ((uint32_t) u[15 * i + 7] << 28); + s[4 * i + 2] = ((u[15 * i + 7] & 0xf0) >> 2) + (u[15 * i + 8] << 6) + (u[15 * i + 9] << 14) + (u[15 * i + 10] << 22) + ((uint32_t) u[15 * i + 11] << 30); + s[4 * i + 3] = (u[15 * i + 11] & 0xfc) + (u[15 * i + 12] << 8) + (u[15 * i + 13] << 15) + ((uint32_t) u[15 * i + 14] << 24); } for (i = 0; i < NTRU_WEIGHT / 2; i++) { diff --git a/crypto_kem/ntruhps4096821/clean/crypto_sort.c b/crypto_kem/ntruhps4096821/clean/crypto_sort.c index 2add05c2..5df65a79 100644 --- a/crypto_kem/ntruhps4096821/clean/crypto_sort.c +++ b/crypto_kem/ntruhps4096821/clean/crypto_sort.c @@ -8,7 +8,7 @@ #define int32_MINMAX(a,b) \ do { \ int32_t ab = (b) ^ (a); \ - int32_t c = (b) - (a); \ + int32_t c = (int32_t)((int64_t)(b) - (int64_t)(a)); \ c ^= ab & (c ^ (b)); \ c >>= 31; \ c &= ab; \ diff --git a/crypto_kem/ntruhps4096821/clean/kem.c b/crypto_kem/ntruhps4096821/clean/kem.c index 32ddc94b..b1f0941c 100644 --- a/crypto_kem/ntruhps4096821/clean/kem.c +++ b/crypto_kem/ntruhps4096821/clean/kem.c @@ -37,7 +37,6 @@ int PQCLEAN_NTRUHPS4096821_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, co int i, fail; uint8_t rm[NTRU_OWCPA_MSGBYTES]; uint8_t buf[NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES]; - uint8_t *cmp = buf + NTRU_PRFKEYBYTES; fail = PQCLEAN_NTRUHPS4096821_CLEAN_owcpa_dec(rm, c, sk); /* If fail = 0 then c = Enc(h, rm), there is no need to re-encapsulate. */ @@ -50,9 +49,9 @@ int PQCLEAN_NTRUHPS4096821_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, co buf[i] = sk[i + NTRU_OWCPA_SECRETKEYBYTES]; } for (i = 0; i < NTRU_CIPHERTEXTBYTES; i++) { - cmp[i] = c[i]; + buf[NTRU_PRFKEYBYTES + i] = c[i]; } - sha3_256(rm, cmp, NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES); + sha3_256(rm, buf, NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES); PQCLEAN_NTRUHPS4096821_CLEAN_cmov(k, rm, NTRU_SHAREDKEYBYTES, (unsigned char) fail); diff --git a/crypto_kem/ntruhps4096821/clean/sample.c b/crypto_kem/ntruhps4096821/clean/sample.c index 1140be46..f0409663 100644 --- a/crypto_kem/ntruhps4096821/clean/sample.c +++ b/crypto_kem/ntruhps4096821/clean/sample.c @@ -25,15 +25,15 @@ void PQCLEAN_NTRUHPS4096821_CLEAN_sample_iid(poly *r, const unsigned char unifor void PQCLEAN_NTRUHPS4096821_CLEAN_sample_fixed_type(poly *r, const unsigned char u[NTRU_SAMPLE_FT_BYTES]) { // Assumes NTRU_SAMPLE_FT_BYTES = ceil(30*(n-1)/8) - int32_t s[NTRU_N - 1]; + uint32_t s[NTRU_N - 1]; int i; // Use 30 bits of u per word for (i = 0; i < (NTRU_N - 1) / 4; i++) { - s[4 * i + 0] = (u[15 * i + 0] << 2) + (u[15 * i + 1] << 10) + (u[15 * i + 2] << 18) + (u[15 * i + 3] << 26); - s[4 * i + 1] = ((u[15 * i + 3] & 0xc0) >> 4) + (u[15 * i + 4] << 4) + (u[15 * i + 5] << 12) + (u[15 * i + 6] << 20) + (u[15 * i + 7] << 28); - s[4 * i + 2] = ((u[15 * i + 7] & 0xf0) >> 2) + (u[15 * i + 8] << 6) + (u[15 * i + 9] << 14) + (u[15 * i + 10] << 22) + (u[15 * i + 11] << 30); - s[4 * i + 3] = (u[15 * i + 11] & 0xfc) + (u[15 * i + 12] << 8) + (u[15 * i + 13] << 15) + (u[15 * i + 14] << 24); + s[4 * i + 0] = (u[15 * i + 0] << 2) + (u[15 * i + 1] << 10) + (u[15 * i + 2] << 18) + ((uint32_t) u[15 * i + 3] << 26); + s[4 * i + 1] = ((u[15 * i + 3] & 0xc0) >> 4) + (u[15 * i + 4] << 4) + (u[15 * i + 5] << 12) + (u[15 * i + 6] << 20) + ((uint32_t) u[15 * i + 7] << 28); + s[4 * i + 2] = ((u[15 * i + 7] & 0xf0) >> 2) + (u[15 * i + 8] << 6) + (u[15 * i + 9] << 14) + (u[15 * i + 10] << 22) + ((uint32_t) u[15 * i + 11] << 30); + s[4 * i + 3] = (u[15 * i + 11] & 0xfc) + (u[15 * i + 12] << 8) + (u[15 * i + 13] << 15) + ((uint32_t) u[15 * i + 14] << 24); } for (i = 0; i < NTRU_WEIGHT / 2; i++) { diff --git a/crypto_kem/ntruhrss701/clean/kem.c b/crypto_kem/ntruhrss701/clean/kem.c index 8ca6f691..8adc31aa 100644 --- a/crypto_kem/ntruhrss701/clean/kem.c +++ b/crypto_kem/ntruhrss701/clean/kem.c @@ -37,7 +37,6 @@ int PQCLEAN_NTRUHRSS701_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, const int i, fail; uint8_t rm[NTRU_OWCPA_MSGBYTES]; uint8_t buf[NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES]; - uint8_t *cmp = buf + NTRU_PRFKEYBYTES; fail = PQCLEAN_NTRUHRSS701_CLEAN_owcpa_dec(rm, c, sk); /* If fail = 0 then c = Enc(h, rm), there is no need to re-encapsulate. */ @@ -50,9 +49,9 @@ int PQCLEAN_NTRUHRSS701_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, const buf[i] = sk[i + NTRU_OWCPA_SECRETKEYBYTES]; } for (i = 0; i < NTRU_CIPHERTEXTBYTES; i++) { - cmp[i] = c[i]; + buf[NTRU_PRFKEYBYTES + i] = c[i]; } - sha3_256(rm, cmp, NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES); + sha3_256(rm, buf, NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES); PQCLEAN_NTRUHRSS701_CLEAN_cmov(k, rm, NTRU_SHAREDKEYBYTES, (unsigned char) fail);