Add remaining NTRU Round 2 schemes (revised)master
@@ -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++) { | |||
@@ -0,0 +1,22 @@ | |||
name: ntru-hps2048677 | |||
type: kem | |||
claimed-nist-level: 3 | |||
length-public-key: 930 | |||
length-secret-key: 1234 | |||
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 |
@@ -0,0 +1 @@ | |||
Public Domain |
@@ -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) |
@@ -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) |
@@ -0,0 +1,19 @@ | |||
#ifndef PQCLEAN_NTRUHPS2048677_CLEAN_API_H | |||
#define PQCLEAN_NTRUHPS2048677_CLEAN_API_H | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,50 @@ | |||
// XXX: Temporary placeholder for a faster sort. | |||
// Copied from supercop-20190110/crypto_sort/int32/portable3 | |||
#include <stdint.h> | |||
#include "crypto_sort.h" | |||
#define int32_MINMAX(a,b) \ | |||
do { \ | |||
int32_t ab = (b) ^ (a); \ | |||
int32_t c = (int32_t)((int64_t)(b) - (int64_t)(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; | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,6 @@ | |||
#ifndef CRYPTO_SORT | |||
#define CRYPTO_SORT | |||
void PQCLEAN_NTRUHPS2048677_CLEAN_crypto_sort(void *array, long long n); | |||
#endif |
@@ -0,0 +1,59 @@ | |||
#include <stdint.h> | |||
#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]; | |||
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++) { | |||
buf[NTRU_PRFKEYBYTES + i] = c[i]; | |||
} | |||
sha3_256(rm, buf, NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES); | |||
PQCLEAN_NTRUHPS2048677_CLEAN_cmov(k, rm, NTRU_SHAREDKEYBYTES, (unsigned char) fail); | |||
return 0; | |||
} |
@@ -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; | |||
} |
@@ -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 |
@@ -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; | |||
} | |||
@@ -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])); | |||
} |
@@ -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 |
@@ -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)<NTRU_N; (I)++) { \ | |||
(A).coeffs[(I)] ^= (B).coeffs[(I)] * (S); \ | |||
} | |||
static void cswappoly(poly *a, poly *b, int swap) { | |||
int i; | |||
uint16_t t; | |||
swap = -swap; | |||
for (i = 0; i < NTRU_N; i++) { | |||
t = (a->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]); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
#ifndef POLY_H | |||
#define POLY_H | |||
#include <stdint.h> | |||
#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 |
@@ -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) | |||
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) + ((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++) { | |||
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; | |||
} |
@@ -0,0 +1,16 @@ | |||
#ifndef SAMPLE_H | |||
#define SAMPLE_H | |||
#include <stdlib.h> | |||
#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 |
@@ -0,0 +1,29 @@ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#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]); | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
#ifndef VERIFY_H | |||
#define VERIFY_H | |||
#include <stdio.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); | |||
/* 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 |
@@ -0,0 +1,22 @@ | |||
name: ntru-hps4096821 | |||
type: kem | |||
claimed-nist-level: 5 | |||
length-public-key: 1230 | |||
length-secret-key: 1590 | |||
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 |
@@ -0,0 +1 @@ | |||
Public Domain |
@@ -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) |
@@ -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) |
@@ -0,0 +1,19 @@ | |||
#ifndef PQCLEAN_NTRUHPS4096821_CLEAN_API_H | |||
#define PQCLEAN_NTRUHPS4096821_CLEAN_API_H | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,50 @@ | |||
// XXX: Temporary placeholder for a faster sort. | |||
// Copied from supercop-20190110/crypto_sort/int32/portable3 | |||
#include <stdint.h> | |||
#include "crypto_sort.h" | |||
#define int32_MINMAX(a,b) \ | |||
do { \ | |||
int32_t ab = (b) ^ (a); \ | |||
int32_t c = (int32_t)((int64_t)(b) - (int64_t)(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; | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,6 @@ | |||
#ifndef CRYPTO_SORT | |||
#define CRYPTO_SORT | |||
void PQCLEAN_NTRUHPS4096821_CLEAN_crypto_sort(void *array, long long n); | |||
#endif |
@@ -0,0 +1,59 @@ | |||
#include <stdint.h> | |||
#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]; | |||
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++) { | |||
buf[NTRU_PRFKEYBYTES + i] = c[i]; | |||
} | |||
sha3_256(rm, buf, NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES); | |||
PQCLEAN_NTRUHPS4096821_CLEAN_cmov(k, rm, NTRU_SHAREDKEYBYTES, (unsigned char) fail); | |||
return 0; | |||
} |
@@ -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; | |||
} |
@@ -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 |
@@ -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; | |||
} | |||
@@ -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] = (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); | |||
} | |||
} | |||
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])); | |||
} |
@@ -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 |
@@ -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)<NTRU_N; (I)++) { \ | |||
(A).coeffs[(I)] ^= (B).coeffs[(I)] * (S); \ | |||
} | |||
static void cswappoly(poly *a, poly *b, int swap) { | |||
int i; | |||
uint16_t t; | |||
swap = -swap; | |||
for (i = 0; i < NTRU_N; i++) { | |||
t = (a->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]); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
#ifndef POLY_H | |||
#define POLY_H | |||
#include <stdint.h> | |||
#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 |
@@ -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) | |||
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) + ((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++) { | |||
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; | |||
} |
@@ -0,0 +1,16 @@ | |||
#ifndef SAMPLE_H | |||
#define SAMPLE_H | |||
#include <stdlib.h> | |||
#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 |
@@ -0,0 +1,29 @@ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#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]); | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
#ifndef VERIFY_H | |||
#define VERIFY_H | |||
#include <stdio.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); | |||
/* 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 |
@@ -0,0 +1,22 @@ | |||
name: ntru-hrss701 | |||
type: kem | |||
claimed-nist-level: 3 | |||
length-public-key: 1138 | |||
length-secret-key: 1450 | |||
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 |
@@ -0,0 +1 @@ | |||
Public Domain |
@@ -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) |
@@ -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) |
@@ -0,0 +1,19 @@ | |||
#ifndef PQCLEAN_NTRUHRSS701_CLEAN_API_H | |||
#define PQCLEAN_NTRUHRSS701_CLEAN_API_H | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,59 @@ | |||
#include <stdint.h> | |||
#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]; | |||
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++) { | |||
buf[NTRU_PRFKEYBYTES + i] = c[i]; | |||
} | |||
sha3_256(rm, buf, NTRU_PRFKEYBYTES + NTRU_CIPHERTEXTBYTES); | |||
PQCLEAN_NTRUHRSS701_CLEAN_cmov(k, rm, NTRU_SHAREDKEYBYTES, (unsigned char) fail); | |||
return 0; | |||
} |
@@ -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; | |||
} |
@@ -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 |
@@ -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; | |||
} | |||
@@ -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] = (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++) { | |||
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] = (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] = (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] = (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); | |||
} | |||
} | |||
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])); | |||
} |
@@ -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 |
@@ -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 <z*x^i, x-1> = 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] = <z, a>, b[1] = <z*x,a>, b[2] = <z*x^2,a> */ | |||
/* 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)<NTRU_N; (I)++) { \ | |||
(A).coeffs[(I)] ^= (B).coeffs[(I)] * (S); \ | |||
} | |||
static void cswappoly(poly *a, poly *b, int swap) { | |||
int i; | |||
uint16_t t; | |||
swap = -swap; | |||
for (i = 0; i < NTRU_N; i++) { | |||
t = (a->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]); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
#ifndef POLY_H | |||
#define POLY_H | |||
#include <stdint.h> | |||
#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 |
@@ -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 <x*r, r> >= 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 = <x*r, r>. (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)); | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
#ifndef SAMPLE_H | |||
#define SAMPLE_H | |||
#include <stdlib.h> | |||
#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 |
@@ -0,0 +1,29 @@ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#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]); | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
#ifndef VERIFY_H | |||
#define VERIFY_H | |||
#include <stdio.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); | |||
/* 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 |
@@ -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 |
@@ -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 |
@@ -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 |