diff --git a/crypto_kem/firesaber/META.yml b/crypto_kem/firesaber/META.yml index e58c7a7c..9e067250 100644 --- a/crypto_kem/firesaber/META.yml +++ b/crypto_kem/firesaber/META.yml @@ -14,4 +14,13 @@ principal-submitters: - Frederik Vercauteren implementations: - name: clean - version: https://github.com/KULeuven-COSIC/SABER/commit/14ede83f1ff3bcc41f0464543542366c68b55871 + version: https://github.com/KULeuven-COSIC/SABER/tree/509cc5ec3a7e12a751ccdd2ef5bd6e54e00bd350 via https://github.com/jschanck/package-pqclean/tree/1ae84c3c/saber + - name: avx2 + version: https://github.com/KULeuven-COSIC/SABER/tree/509cc5ec3a7e12a751ccdd2ef5bd6e54e00bd350 via https://github.com/jschanck/package-pqclean/tree/1ae84c3c/saber + supported_platforms: + - architecture: x86_64 + operating_systems: + - Linux + - Darwin + required_flags: + - avx2 diff --git a/crypto_kem/firesaber/avx2/LICENSE b/crypto_kem/firesaber/avx2/LICENSE new file mode 100644 index 00000000..d5d21fff --- /dev/null +++ b/crypto_kem/firesaber/avx2/LICENSE @@ -0,0 +1 @@ +Public Domain diff --git a/crypto_kem/firesaber/avx2/Makefile b/crypto_kem/firesaber/avx2/Makefile new file mode 100644 index 00000000..1ecd3c1a --- /dev/null +++ b/crypto_kem/firesaber/avx2/Makefile @@ -0,0 +1,22 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libfiresaber_avx2.a +HEADERS=api.h cbd.h pack_unpack.h poly.h SABER_indcpa.h SABER_params.h verify.h +OBJECTS=cbd.o kem.o pack_unpack.o poly.o poly_mul.o SABER_indcpa.o verify.o + +CFLAGS=-O3 -mavx2 -Wall -Wextra -Wpedantic -Wvla -Werror -Wredundant-decls -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.s $(HEADERS) + $(AS) -o $@ $< + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_kem/firesaber/avx2/SABER_indcpa.c b/crypto_kem/firesaber/avx2/SABER_indcpa.c new file mode 100644 index 00000000..285a6625 --- /dev/null +++ b/crypto_kem/firesaber/avx2/SABER_indcpa.c @@ -0,0 +1,125 @@ +#include "SABER_indcpa.h" +#include "SABER_params.h" +#include "fips202.h" +#include "pack_unpack.h" +#include "poly.h" +#include "randombytes.h" +#include +#include + +#define h1 (1 << (SABER_EQ - SABER_EP - 1)) +#define h2 ((1 << (SABER_EP - 2)) - (1 << (SABER_EP - SABER_ET - 1)) + (1 << (SABER_EQ - SABER_EP - 1))) + +void PQCLEAN_FIRESABER_AVX2_indcpa_kem_keypair(uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES], uint8_t sk[SABER_INDCPA_SECRETKEYBYTES]) { + size_t i, j; + + poly A[SABER_L][SABER_L]; + poly *skpv1 = A[0]; // use first row of A to hold sk temporarily + toom4_points skpv1_eval[SABER_L]; + poly res[SABER_L]; + + uint8_t rand[SABER_NOISESEEDBYTES]; + uint8_t *seed_A = pk + SABER_POLYVECCOMPRESSEDBYTES; + + randombytes(seed_A, SABER_SEEDBYTES); + shake128(seed_A, SABER_SEEDBYTES, seed_A, SABER_SEEDBYTES); // for not revealing system RNG state + + randombytes(rand, SABER_NOISESEEDBYTES); + PQCLEAN_FIRESABER_AVX2_GenSecret(skpv1, rand); + PQCLEAN_FIRESABER_AVX2_POLVECq2BS(sk, skpv1); // pack secret key + + for (j = 0; j < SABER_L; j++) { + PQCLEAN_FIRESABER_AVX2_toom4_eval(&skpv1_eval[j], &skpv1[j]); + } + + PQCLEAN_FIRESABER_AVX2_GenMatrix(A, seed_A); // sample matrix A + PQCLEAN_FIRESABER_AVX2_MatrixVectorMul(res, (const poly (*)[SABER_L])A, (const toom4_points *)skpv1_eval, 1); // Matrix in transposed order + + // rounding + for (i = 0; i < SABER_L; i++) { + for (j = 0; j < SABER_N; j++) { + res[i].coeffs[j] += h1; + res[i].coeffs[j] >>= SABER_EQ - SABER_EP; + res[i].coeffs[j] &= SABER_Q - 1; + } + } + + PQCLEAN_FIRESABER_AVX2_POLVECp2BS(pk, res); // pack public key +} + + +void PQCLEAN_FIRESABER_AVX2_indcpa_kem_enc(uint8_t ciphertext[SABER_BYTES_CCA_DEC], const uint8_t m[SABER_KEYBYTES], const uint8_t noiseseed[SABER_NOISESEEDBYTES], const uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES]) { + size_t i, j; + + poly A[SABER_L][SABER_L]; + poly res[SABER_L]; + toom4_points skpv1_eval[SABER_L]; + + poly *temp = A[0]; // re-use stack space + poly *vprime = &A[0][0]; + poly *message = &A[0][1]; + + const uint8_t *seed_A = pk + SABER_POLYVECCOMPRESSEDBYTES; + uint8_t *msk_c = ciphertext + SABER_POLYVECCOMPRESSEDBYTES; + + PQCLEAN_FIRESABER_AVX2_GenSecret(temp, noiseseed); + for (j = 0; j < SABER_L; j++) { + PQCLEAN_FIRESABER_AVX2_toom4_eval(&skpv1_eval[j], &temp[j]); + } + + PQCLEAN_FIRESABER_AVX2_GenMatrix(A, seed_A); + PQCLEAN_FIRESABER_AVX2_MatrixVectorMul(res, (const poly (*)[SABER_L])A, (const toom4_points *)skpv1_eval, 0); // 0 => not transposed + + // rounding + for (i = 0; i < SABER_L; i++) { //shift right EQ-EP bits + for (j = 0; j < SABER_N; j++) { + res[i].coeffs[j] += h1; + res[i].coeffs[j] >>= SABER_EQ - SABER_EP; + res[i].coeffs[j] &= SABER_Q - 1; + } + } + PQCLEAN_FIRESABER_AVX2_POLVECp2BS(ciphertext, res); + + // vector-vector scalar multiplication with mod p + PQCLEAN_FIRESABER_AVX2_BS2POLVECp(temp, pk); + PQCLEAN_FIRESABER_AVX2_InnerProd(vprime, temp, skpv1_eval); + PQCLEAN_FIRESABER_AVX2_BS2POLmsg(message, m); + + for (i = 0; i < SABER_N; i++) { + vprime->coeffs[i] += h1 - (message->coeffs[i] << (SABER_EP - 1)); + vprime->coeffs[i] &= SABER_P - 1; + vprime->coeffs[i] >>= SABER_EP - SABER_ET; + } + + PQCLEAN_FIRESABER_AVX2_POLT2BS(msk_c, vprime); +} + + +void PQCLEAN_FIRESABER_AVX2_indcpa_kem_dec(uint8_t m[SABER_KEYBYTES], const uint8_t sk[SABER_INDCPA_SECRETKEYBYTES], const uint8_t ciphertext[SABER_BYTES_CCA_DEC]) { + size_t i; + + poly temp[SABER_L]; + toom4_points sksv_eval[SABER_L]; + + const uint8_t *packed_cm = ciphertext + SABER_POLYVECCOMPRESSEDBYTES; + poly *v = &temp[0]; + poly *cm = &temp[1]; + + PQCLEAN_FIRESABER_AVX2_BS2POLVECq(temp, sk); + for (i = 0; i < SABER_L; i++) { + PQCLEAN_FIRESABER_AVX2_toom4_eval(&sksv_eval[i], &temp[i]); + } + + PQCLEAN_FIRESABER_AVX2_BS2POLVECp(temp, ciphertext); + PQCLEAN_FIRESABER_AVX2_InnerProd(v, temp, sksv_eval); + + PQCLEAN_FIRESABER_AVX2_BS2POLT(cm, packed_cm); + + for (i = 0; i < SABER_N; i++) { + v->coeffs[i] += h2 - (cm->coeffs[i] << (SABER_EP - SABER_ET)); + v->coeffs[i] &= SABER_P - 1; + v->coeffs[i] >>= SABER_EP - 1; + } + + PQCLEAN_FIRESABER_AVX2_POLmsg2BS(m, v); +} diff --git a/crypto_kem/firesaber/avx2/SABER_indcpa.h b/crypto_kem/firesaber/avx2/SABER_indcpa.h new file mode 100644 index 00000000..1b6c8311 --- /dev/null +++ b/crypto_kem/firesaber/avx2/SABER_indcpa.h @@ -0,0 +1,13 @@ +#ifndef INDCPA_H +#define INDCPA_H +#include "SABER_params.h" +#include + +void PQCLEAN_FIRESABER_AVX2_indcpa_kem_keypair(uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES], uint8_t sk[SABER_INDCPA_SECRETKEYBYTES]); + +void PQCLEAN_FIRESABER_AVX2_indcpa_kem_enc(uint8_t ciphertext[SABER_BYTES_CCA_DEC], const uint8_t m[SABER_KEYBYTES], const uint8_t noiseseed[SABER_NOISESEEDBYTES], const uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES]); + +void PQCLEAN_FIRESABER_AVX2_indcpa_kem_dec(uint8_t m[SABER_KEYBYTES], const uint8_t sk[SABER_INDCPA_SECRETKEYBYTES], const uint8_t ciphertext[SABER_BYTES_CCA_DEC]); + + +#endif diff --git a/crypto_kem/firesaber/avx2/SABER_params.h b/crypto_kem/firesaber/avx2/SABER_params.h new file mode 100644 index 00000000..6481efec --- /dev/null +++ b/crypto_kem/firesaber/avx2/SABER_params.h @@ -0,0 +1,41 @@ +#ifndef PARAMS_H +#define PARAMS_H + + +/* Don't change anything below this line */ +#define SABER_L 4 +#define SABER_MU 6 +#define SABER_ET 6 + +#define SABER_N 256 + +#define SABER_EP 10 +#define SABER_P (1 << SABER_EP) + +#define SABER_EQ 13 +#define SABER_Q (1 << SABER_EQ) + +#define SABER_SEEDBYTES 32 +#define SABER_NOISESEEDBYTES 32 +#define SABER_KEYBYTES 32 +#define SABER_HASHBYTES 32 + +#define SABER_POLYCOINBYTES (SABER_MU * SABER_N / 8) + +#define SABER_POLYBYTES (SABER_EQ * SABER_N / 8) +#define SABER_POLYVECBYTES (SABER_L * SABER_POLYBYTES) + +#define SABER_POLYCOMPRESSEDBYTES (SABER_EP * SABER_N / 8) +#define SABER_POLYVECCOMPRESSEDBYTES (SABER_L * SABER_POLYCOMPRESSEDBYTES) + +#define SABER_SCALEBYTES_KEM (SABER_ET * SABER_N / 8) + +#define SABER_INDCPA_PUBLICKEYBYTES (SABER_POLYVECCOMPRESSEDBYTES + SABER_SEEDBYTES) +#define SABER_INDCPA_SECRETKEYBYTES (SABER_POLYVECBYTES) + +#define SABER_PUBLICKEYBYTES (SABER_INDCPA_PUBLICKEYBYTES) +#define SABER_SECRETKEYBYTES (SABER_INDCPA_SECRETKEYBYTES + SABER_INDCPA_PUBLICKEYBYTES + SABER_HASHBYTES + SABER_KEYBYTES) + +#define SABER_BYTES_CCA_DEC (SABER_POLYVECCOMPRESSEDBYTES + SABER_SCALEBYTES_KEM) + +#endif diff --git a/crypto_kem/firesaber/avx2/api.h b/crypto_kem/firesaber/avx2/api.h new file mode 100644 index 00000000..cb5240dd --- /dev/null +++ b/crypto_kem/firesaber/avx2/api.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_FIRESABER_AVX2_API_H +#define PQCLEAN_FIRESABER_AVX2_API_H + + +#define PQCLEAN_FIRESABER_AVX2_CRYPTO_ALGNAME "FireSaber" +#define PQCLEAN_FIRESABER_AVX2_CRYPTO_BYTES 32 +#define PQCLEAN_FIRESABER_AVX2_CRYPTO_CIPHERTEXTBYTES 1472 +#define PQCLEAN_FIRESABER_AVX2_CRYPTO_PUBLICKEYBYTES 1312 +#define PQCLEAN_FIRESABER_AVX2_CRYPTO_SECRETKEYBYTES 3040 + +int PQCLEAN_FIRESABER_AVX2_crypto_kem_keypair(unsigned char *pk, unsigned char *sk); + +int PQCLEAN_FIRESABER_AVX2_crypto_kem_enc(unsigned char *ct, unsigned char *k, const unsigned char *pk); + +int PQCLEAN_FIRESABER_AVX2_crypto_kem_dec(unsigned char *k, const unsigned char *ct, const unsigned char *sk); + + +#endif /* PQCLEAN_FIRESABER_AVX2_API_H */ diff --git a/crypto_kem/firesaber/avx2/cbd.c b/crypto_kem/firesaber/avx2/cbd.c new file mode 100644 index 00000000..0da0876f --- /dev/null +++ b/crypto_kem/firesaber/avx2/cbd.c @@ -0,0 +1,48 @@ +#include "SABER_params.h" +#include "api.h" +#include "cbd.h" +#include +/*--------------------------------------------------------------------- +This file has been adapted from the implementation +(available at, Public Domain https://github.com/pq-crystals/kyber) +of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" +by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, +Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle +----------------------------------------------------------------------*/ + + +static uint64_t load_littleendian(const uint8_t *x, int bytes) { + int i; + uint64_t r = x[0]; + for (i = 1; i < bytes; i++) { + r |= (uint64_t)x[i] << (8 * i); + } + return r; +} + +void PQCLEAN_FIRESABER_AVX2_cbd(uint16_t s[SABER_N], const uint8_t buf[SABER_POLYCOINBYTES]) { + uint32_t t, d, a[4], b[4]; + int i, j; + + for (i = 0; i < SABER_N / 4; i++) { + t = (uint32_t) load_littleendian(buf + 3 * i, 3); + d = 0; + for (j = 0; j < 3; j++) { + d += (t >> j) & 0x249249; + } + + a[0] = d & 0x7; + b[0] = (d >> 3) & 0x7; + a[1] = (d >> 6) & 0x7; + b[1] = (d >> 9) & 0x7; + a[2] = (d >> 12) & 0x7; + b[2] = (d >> 15) & 0x7; + a[3] = (d >> 18) & 0x7; + b[3] = (d >> 21); + + s[4 * i + 0] = (uint16_t)(a[0] - b[0]); + s[4 * i + 1] = (uint16_t)(a[1] - b[1]); + s[4 * i + 2] = (uint16_t)(a[2] - b[2]); + s[4 * i + 3] = (uint16_t)(a[3] - b[3]); + } +} diff --git a/crypto_kem/firesaber/avx2/cbd.h b/crypto_kem/firesaber/avx2/cbd.h new file mode 100644 index 00000000..dba55d9d --- /dev/null +++ b/crypto_kem/firesaber/avx2/cbd.h @@ -0,0 +1,16 @@ +#ifndef CBD_H +#define CBD_H +/*--------------------------------------------------------------------- +This file has been adapted from the implementation +(available at, Public Domain https://github.com/pq-crystals/kyber) +of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" +by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, +Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle +----------------------------------------------------------------------*/ +#include "SABER_params.h" +#include + +void PQCLEAN_FIRESABER_AVX2_cbd(uint16_t s[SABER_N], const uint8_t buf[SABER_POLYCOINBYTES]); + + +#endif diff --git a/crypto_kem/firesaber/avx2/kem.c b/crypto_kem/firesaber/avx2/kem.c new file mode 100644 index 00000000..92c19a7d --- /dev/null +++ b/crypto_kem/firesaber/avx2/kem.c @@ -0,0 +1,77 @@ +#include "SABER_indcpa.h" +#include "SABER_params.h" +#include "api.h" +#include "fips202.h" +#include "randombytes.h" +#include "verify.h" +#include +#include + + +int PQCLEAN_FIRESABER_AVX2_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + size_t i; + + PQCLEAN_FIRESABER_AVX2_indcpa_kem_keypair(pk, sk); // sk[0:SABER_INDCPA_SECRETKEYBYTES-1] <-- sk + for (i = 0; i < SABER_INDCPA_PUBLICKEYBYTES; i++) { + sk[i + SABER_INDCPA_SECRETKEYBYTES] = pk[i]; // sk[SABER_INDCPA_SECRETKEYBYTES:SABER_INDCPA_SECRETKEYBYTES+SABER_INDCPA_SECRETKEYBYTES-1] <-- pk + } + + sha3_256(sk + SABER_SECRETKEYBYTES - 64, pk, SABER_INDCPA_PUBLICKEYBYTES); // Then hash(pk) is appended. + + randombytes(sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES); // Remaining part of sk contains a pseudo-random number. + // This is output when check in PQCLEAN_FIRESABER_AVX2_crypto_kem_dec() fails. + return (0); +} + +int PQCLEAN_FIRESABER_AVX2_crypto_kem_enc(uint8_t *c, uint8_t *k, const uint8_t *pk) { + + uint8_t kr[64]; // Will contain key, coins + uint8_t buf[64]; + + randombytes(buf, 32); + + sha3_256(buf, buf, 32); // BUF[0:31] <-- random message (will be used as the key for client) Note: hash doesnot release system RNG output + + sha3_256(buf + 32, pk, SABER_INDCPA_PUBLICKEYBYTES); // BUF[32:63] <-- Hash(public key); Multitarget countermeasure for coins + contributory KEM + + sha3_512(kr, buf, 64); // kr[0:63] <-- Hash(buf[0:63]); + // K^ <-- kr[0:31] + // noiseseed (r) <-- kr[32:63]; + PQCLEAN_FIRESABER_AVX2_indcpa_kem_enc(c, buf, kr + 32, pk); // buf[0:31] contains message; kr[32:63] contains randomness r; + + sha3_256(kr + 32, c, SABER_BYTES_CCA_DEC); + + sha3_256(k, kr, 64); // hash concatenation of pre-k and h(c) to k + + return (0); +} + +int PQCLEAN_FIRESABER_AVX2_crypto_kem_dec(uint8_t *k, const uint8_t *c, const uint8_t *sk) { + size_t i; + uint8_t fail; + uint8_t cmp[SABER_BYTES_CCA_DEC]; + uint8_t buf[64]; + uint8_t kr[64]; // Will contain key, coins + const uint8_t *pk = sk + SABER_INDCPA_SECRETKEYBYTES; + + PQCLEAN_FIRESABER_AVX2_indcpa_kem_dec(buf, sk, c); // buf[0:31] <-- message + + // Multitarget countermeasure for coins + contributory KEM + for (i = 0; i < 32; i++) { // Save hash by storing h(pk) in sk + buf[32 + i] = sk[SABER_SECRETKEYBYTES - 64 + i]; + } + + sha3_512(kr, buf, 64); + + PQCLEAN_FIRESABER_AVX2_indcpa_kem_enc(cmp, buf, kr + 32, pk); + + fail = PQCLEAN_FIRESABER_AVX2_verify(c, cmp, SABER_BYTES_CCA_DEC); + + sha3_256(kr + 32, c, SABER_BYTES_CCA_DEC); // overwrite coins in kr with h(c) + + PQCLEAN_FIRESABER_AVX2_cmov(kr, sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES, fail); + + sha3_256(k, kr, 64); // hash concatenation of pre-k and h(c) to k + + return (0); +} diff --git a/crypto_kem/firesaber/avx2/pack_unpack.c b/crypto_kem/firesaber/avx2/pack_unpack.c new file mode 100644 index 00000000..d5e6b9ba --- /dev/null +++ b/crypto_kem/firesaber/avx2/pack_unpack.c @@ -0,0 +1,151 @@ +#include "SABER_params.h" +#include "pack_unpack.h" +#include "poly.h" +#include + +void PQCLEAN_FIRESABER_AVX2_POLT2BS(uint8_t bytes[SABER_SCALEBYTES_KEM], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; + for (j = 0; j < SABER_N / 4; j++) { + out[0] = (uint8_t) ((in[0] & 0x3f) | (in[1] << 6)); + out[1] = (uint8_t) (((in[1] >> 2) & 0x0f) | (in[2] << 4)); + out[2] = (uint8_t) (((in[2] >> 4) & 0x03) | (in[3] << 2)); + in += 4; + out += 3; + } +} + +void PQCLEAN_FIRESABER_AVX2_BS2POLT(poly *data, const uint8_t bytes[SABER_SCALEBYTES_KEM]) { + /* This function does not reduce its output mod T */ + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; + for (j = 0; j < SABER_N / 4; j++) { + out[0] = in[0]; + out[1] = (in[0] >> 6) | (in[1] << 2); + out[2] = (in[1] >> 4) | (in[2] << 4); + out[3] = (in[2] >> 2); + in += 3; + out += 4; + } +} + +static void POLq2BS(uint8_t bytes[SABER_POLYBYTES], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; + for (j = 0; j < SABER_N / 8; j++) { + out[0] = (uint8_t) (in[0]); + out[1] = (uint8_t) (((in[0] >> 8) & 0x1f) | (in[1] << 5)); + out[2] = (uint8_t) (in[1] >> 3); + out[3] = (uint8_t) (((in[1] >> 11) & 0x03) | (in[2] << 2)); + out[4] = (uint8_t) (((in[2] >> 6) & 0x7f) | (in[3] << 7)); + out[5] = (uint8_t) (in[3] >> 1); + out[6] = (uint8_t) (((in[3] >> 9) & 0x0f) | (in[4] << 4)); + out[7] = (uint8_t) (in[4] >> 4); + out[8] = (uint8_t) (((in[4] >> 12) & 0x01) | (in[5] << 1)); + out[9] = (uint8_t) (((in[5] >> 7) & 0x3f) | (in[6] << 6)); + out[10] = (uint8_t) (in[6] >> 2); + out[11] = (uint8_t) (((in[6] >> 10) & 0x07) | (in[7] << 3)); + out[12] = (uint8_t) (in[7] >> 5); + in += 8; + out += 13; + } +} + +static void BS2POLq(poly *data, const uint8_t bytes[SABER_POLYBYTES]) { + /* This function does not reduce its output mod Q */ + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; + for (j = 0; j < SABER_N / 8; j++) { + out[0] = (in[0]) | (in[1] << 8); + out[1] = (in[1] >> 5) | (in[2] << 3) | (in[3] << 11); + out[2] = (in[3] >> 2) | (in[4] << 6); + out[3] = (in[4] >> 7) | (in[5] << 1) | (in[6] << 9); + out[4] = (in[6] >> 4) | (in[7] << 4) | (in[8] << 12); + out[5] = (in[8] >> 1) | (in[9] << 7); + out[6] = (in[9] >> 6) | (in[10] << 2) | (in[11] << 10); + out[7] = (in[11] >> 3) | (in[12] << 5); + in += 13; + out += 8; + } +} + +static void POLp2BS(uint8_t bytes[SABER_POLYCOMPRESSEDBYTES], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; + for (j = 0; j < SABER_N / 4; j++) { + out[0] = (uint8_t) (in[0]); + out[1] = (uint8_t) (((in[0] >> 8) & 0x03) | (in[1] << 2)); + out[2] = (uint8_t) (((in[1] >> 6) & 0x0f) | (in[2] << 4)); + out[3] = (uint8_t) (((in[2] >> 4) & 0x3f) | (in[3] << 6)); + out[4] = (uint8_t) (in[3] >> 2); + in += 4; + out += 5; + } +} + +static void BS2POLp(poly *data, const uint8_t bytes[SABER_POLYCOMPRESSEDBYTES]) { + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; + for (j = 0; j < SABER_N / 4; j++) { + out[0] = in[0] | (in[1] << 8); + out[1] = (in[1] >> 2) | (in[2] << 6); + out[2] = (in[2] >> 4) | (in[3] << 4); + out[3] = (in[3] >> 6) | (in[4] << 2); + in += 5; + out += 4; + } +} + +void PQCLEAN_FIRESABER_AVX2_POLVECq2BS(uint8_t bytes[SABER_POLYVECBYTES], const poly data[SABER_L]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + POLq2BS(bytes + i * SABER_POLYBYTES, &data[i]); + } +} + +void PQCLEAN_FIRESABER_AVX2_BS2POLVECq(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECBYTES]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + BS2POLq(&data[i], bytes + i * SABER_POLYBYTES); + } +} + +void PQCLEAN_FIRESABER_AVX2_POLVECp2BS(uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES], const poly data[SABER_L]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + POLp2BS(bytes + i * SABER_POLYCOMPRESSEDBYTES, &data[i]); + } +} + +void PQCLEAN_FIRESABER_AVX2_BS2POLVECp(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + BS2POLp(&data[i], bytes + i * SABER_POLYCOMPRESSEDBYTES); + } +} + +void PQCLEAN_FIRESABER_AVX2_BS2POLmsg(poly *data, const uint8_t bytes[SABER_KEYBYTES]) { + size_t i, j; + for (j = 0; j < SABER_KEYBYTES; j++) { + for (i = 0; i < 8; i++) { + data->coeffs[j * 8 + i] = ((bytes[j] >> i) & 0x01); + } + } +} + +void PQCLEAN_FIRESABER_AVX2_POLmsg2BS(uint8_t bytes[SABER_KEYBYTES], const poly *data) { + size_t i, j; + memset(bytes, 0, SABER_KEYBYTES); + + for (j = 0; j < SABER_KEYBYTES; j++) { + for (i = 0; i < 8; i++) { + bytes[j] = bytes[j] | ((data->coeffs[j * 8 + i] & 0x01) << i); + } + } +} diff --git a/crypto_kem/firesaber/avx2/pack_unpack.h b/crypto_kem/firesaber/avx2/pack_unpack.h new file mode 100644 index 00000000..eb6242be --- /dev/null +++ b/crypto_kem/firesaber/avx2/pack_unpack.h @@ -0,0 +1,28 @@ +#ifndef PACK_UNPACK_H +#define PACK_UNPACK_H +#include "SABER_params.h" +#include "poly.h" +#include +#include + +void PQCLEAN_FIRESABER_AVX2_POLT2BS(uint8_t bytes[SABER_SCALEBYTES_KEM], const poly *data); + +void PQCLEAN_FIRESABER_AVX2_BS2POLT(poly *data, const uint8_t bytes[SABER_SCALEBYTES_KEM]); + + +void PQCLEAN_FIRESABER_AVX2_POLVECq2BS(uint8_t bytes[SABER_POLYVECBYTES], const poly data[SABER_L]); + +void PQCLEAN_FIRESABER_AVX2_POLVECp2BS(uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES], const poly data[SABER_L]); + + +void PQCLEAN_FIRESABER_AVX2_BS2POLVECq(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECBYTES]); + +void PQCLEAN_FIRESABER_AVX2_BS2POLVECp(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES]); + + +void PQCLEAN_FIRESABER_AVX2_BS2POLmsg(poly *data, const uint8_t bytes[SABER_KEYBYTES]); + +void PQCLEAN_FIRESABER_AVX2_POLmsg2BS(uint8_t bytes[SABER_KEYBYTES], const poly *data); + + +#endif diff --git a/crypto_kem/firesaber/avx2/poly.c b/crypto_kem/firesaber/avx2/poly.c new file mode 100644 index 00000000..2a7fa836 --- /dev/null +++ b/crypto_kem/firesaber/avx2/poly.c @@ -0,0 +1,62 @@ +#include "cbd.h" +#include "fips202.h" +#include "pack_unpack.h" +#include "poly.h" + + +void PQCLEAN_FIRESABER_AVX2_MatrixVectorMul(poly c[SABER_L], const poly A[SABER_L][SABER_L], const toom4_points s_eval[SABER_L], int transpose) { + size_t i, j; + toom4_points_product c_eval; + + if (transpose) { + for (i = 0; i < SABER_L; i++) { + PQCLEAN_FIRESABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &A[0][i], &s_eval[0], 0); + for (j = 1; j < SABER_L; j++) { + PQCLEAN_FIRESABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &A[j][i], &s_eval[j], 1); + } + PQCLEAN_FIRESABER_AVX2_toom4_interp(&c[i], &c_eval); + } + } else { + for (i = 0; i < SABER_L; i++) { + PQCLEAN_FIRESABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &A[i][0], &s_eval[0], 0); + for (j = 1; j < SABER_L; j++) { + PQCLEAN_FIRESABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &A[i][j], &s_eval[j], 1); + } + PQCLEAN_FIRESABER_AVX2_toom4_interp(&c[i], &c_eval); + } + } +} + +void PQCLEAN_FIRESABER_AVX2_InnerProd(poly *c, const poly b[SABER_L], const toom4_points s_eval[SABER_L]) { + size_t i; + toom4_points_product c_eval; //Holds results for 9 Karatsuba at a time + + PQCLEAN_FIRESABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &b[0], &s_eval[0], 0); + for (i = 1; i < SABER_L; i++) { + PQCLEAN_FIRESABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &b[i], &s_eval[i], 1); + } + + PQCLEAN_FIRESABER_AVX2_toom4_interp(c, &c_eval); +} + +void PQCLEAN_FIRESABER_AVX2_GenMatrix(poly A[SABER_L][SABER_L], const uint8_t seed[SABER_SEEDBYTES]) { + size_t i; + uint8_t buf[SABER_L * SABER_POLYVECBYTES]; + + shake128(buf, sizeof(buf), seed, SABER_SEEDBYTES); + + for (i = 0; i < SABER_L; i++) { + PQCLEAN_FIRESABER_AVX2_BS2POLVECq(A[i], buf + i * SABER_POLYVECBYTES); + } +} + +void PQCLEAN_FIRESABER_AVX2_GenSecret(poly s[SABER_L], const uint8_t seed[SABER_NOISESEEDBYTES]) { + size_t i; + uint8_t buf[SABER_L * SABER_POLYCOINBYTES]; + + shake128(buf, sizeof(buf), seed, SABER_NOISESEEDBYTES); + + for (i = 0; i < SABER_L; i++) { + PQCLEAN_FIRESABER_AVX2_cbd(s[i].coeffs, buf + i * SABER_POLYCOINBYTES); + } +} diff --git a/crypto_kem/firesaber/avx2/poly.h b/crypto_kem/firesaber/avx2/poly.h new file mode 100644 index 00000000..859fb95e --- /dev/null +++ b/crypto_kem/firesaber/avx2/poly.h @@ -0,0 +1,38 @@ +#ifndef POLY_H +#define POLY_H +#include "SABER_params.h" +#include +#include + +typedef union { + uint16_t coeffs[SABER_N]; + __m256i dummy; +} poly; + +typedef union { + uint16_t coeffs[4 * SABER_N]; + __m256i dummy; +} toom4_points; + +typedef union { + uint16_t coeffs[8 * SABER_N]; + __m256i dummy; +} toom4_points_product; + +void PQCLEAN_FIRESABER_AVX2_MatrixVectorMul(poly c[SABER_L], const poly A[SABER_L][SABER_L], const toom4_points s_eval[SABER_L], int transpose); + +void PQCLEAN_FIRESABER_AVX2_InnerProd(poly *c, const poly b[SABER_L], const toom4_points s_eval[SABER_L]); + +void PQCLEAN_FIRESABER_AVX2_GenMatrix(poly a[SABER_L][SABER_L], const uint8_t seed[SABER_SEEDBYTES]); + +void PQCLEAN_FIRESABER_AVX2_GenSecret(poly s[SABER_L], const uint8_t seed[SABER_NOISESEEDBYTES]); + + +void PQCLEAN_FIRESABER_AVX2_toom4_interp(poly *res_avx, const toom4_points_product *c_eval); + +void PQCLEAN_FIRESABER_AVX2_toom4_eval(toom4_points *b_eval, const poly *b); + +void PQCLEAN_FIRESABER_AVX2_toom4_mul_A_by_B_eval(toom4_points_product *c_eval, const poly *a_avx, const toom4_points *b_eval, int accumulate); + + +#endif diff --git a/crypto_kem/firesaber/avx2/poly_mul.c b/crypto_kem/firesaber/avx2/poly_mul.c new file mode 100644 index 00000000..4d4ec959 --- /dev/null +++ b/crypto_kem/firesaber/avx2/poly_mul.c @@ -0,0 +1,1496 @@ +#include "SABER_params.h" +#include "poly.h" + + +#define L (SABER_N / 64) + +/* 16 word parallel multiply */ +#define mul(a, b) _mm256_mullo_epi16((a), (b)) +/* 16 word parallel multiply and accumulate */ +#define mac(a, b, c) _mm256_add_epi16(_mm256_mullo_epi16((a), (b)), (c)) + +static void schoolbook16x16(__m256i *c, const __m256i *a, const __m256i *b) { + __m256i a0, a1, a2, a3; + __m256i b0, b1, b2, b3; + __m256i t0; + a0 = a[0]; + a1 = a[1]; + a2 = a[2]; + a3 = a[3]; + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[0] = mul(a0, b0); + t0 = mul(a0, b1); + c[1] = mac(a1, b0, t0); + t0 = mul(a0, b2); + t0 = mac(a1, b1, t0); + c[2] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[3] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[4] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[5] = mac(a3, b2, t0); + c[6] = mul(a3, b3); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[4] = mac(a0, b0, c[4]); + t0 = mac(a0, b1, c[5]); + c[5] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[6]); + t0 = mac(a1, b1, t0); + c[6] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[7] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[8] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[9] = mac(a3, b2, t0); + c[10] = mul(a3, b3); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[13] = mac(a3, b2, t0); + c[14] = mul(a3, b3); + b0 = b[12]; + b1 = b[13]; + b2 = b[14]; + b3 = b[15]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[17] = mac(a3, b2, t0); + c[18] = mul(a3, b3); + a0 = a[4]; + a1 = a[5]; + a2 = a[6]; + a3 = a[7]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[21] = mac(a3, b2, t0); + c[22] = mul(a3, b3); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[4] = mac(a0, b0, c[4]); + t0 = mac(a0, b1, c[5]); + c[5] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[6]); + t0 = mac(a1, b1, t0); + c[6] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[7]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[7] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[8]); + t0 = mac(a2, b2, t0); + c[8] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[9]); + c[9] = mac(a3, b2, t0); + c[10] = mac(a3, b3, c[10]); + a0 = a[8]; + a1 = a[9]; + a2 = a[10]; + a3 = a[11]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[12]; + b1 = b[13]; + b2 = b[14]; + b3 = b[15]; + c[20] = mac(a0, b0, c[20]); + t0 = mac(a0, b1, c[21]); + c[21] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[22]); + t0 = mac(a1, b1, t0); + c[22] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[23] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[24] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[25] = mac(a3, b2, t0); + c[26] = mul(a3, b3); + a0 = a[12]; + a1 = a[13]; + a2 = a[14]; + a3 = a[15]; + c[24] = mac(a0, b0, c[24]); + t0 = mac(a0, b1, c[25]); + c[25] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[26]); + t0 = mac(a1, b1, t0); + c[26] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[27] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[28] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[29] = mac(a3, b2, t0); + c[30] = mul(a3, b3); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[20] = mac(a0, b0, c[20]); + t0 = mac(a0, b1, c[21]); + c[21] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[22]); + t0 = mac(a1, b1, t0); + c[22] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[23]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[23] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[24]); + t0 = mac(a2, b2, t0); + c[24] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[25]); + c[25] = mac(a3, b2, t0); + c[26] = mac(a3, b3, c[26]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + c[31] = _mm256_setzero_si256(); +} + +static void schoolbook16x16_acc(__m256i *c, const __m256i *a, const __m256i *b) { + __m256i a0, a1, a2, a3; + __m256i b0, b1, b2, b3; + __m256i t0; + a0 = a[0]; + a1 = a[1]; + a2 = a[2]; + a3 = a[3]; + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[0] = mac(a0, b0, c[0]); + t0 = mac(a0, b1, c[1]); + c[1] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[2]); + t0 = mac(a1, b1, t0); + c[2] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[3]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[3] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[4]); + t0 = mac(a2, b2, t0); + c[4] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[5]); + c[5] = mac(a3, b2, t0); + c[6] = mac(a3, b3, c[6]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[4] = mac(a0, b0, c[4]); + t0 = mac(a0, b1, c[5]); + c[5] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[6]); + t0 = mac(a1, b1, t0); + c[6] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[7]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[7] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[8]); + t0 = mac(a2, b2, t0); + c[8] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[9]); + c[9] = mac(a3, b2, t0); + c[10] = mac(a3, b3, c[10]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[12]; + b1 = b[13]; + b2 = b[14]; + b3 = b[15]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + a0 = a[4]; + a1 = a[5]; + a2 = a[6]; + a3 = a[7]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[4] = mac(a0, b0, c[4]); + t0 = mac(a0, b1, c[5]); + c[5] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[6]); + t0 = mac(a1, b1, t0); + c[6] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[7]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[7] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[8]); + t0 = mac(a2, b2, t0); + c[8] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[9]); + c[9] = mac(a3, b2, t0); + c[10] = mac(a3, b3, c[10]); + a0 = a[8]; + a1 = a[9]; + a2 = a[10]; + a3 = a[11]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[12]; + b1 = b[13]; + b2 = b[14]; + b3 = b[15]; + c[20] = mac(a0, b0, c[20]); + t0 = mac(a0, b1, c[21]); + c[21] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[22]); + t0 = mac(a1, b1, t0); + c[22] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[23]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[23] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[24]); + t0 = mac(a2, b2, t0); + c[24] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[25]); + c[25] = mac(a3, b2, t0); + c[26] = mac(a3, b3, c[26]); + a0 = a[12]; + a1 = a[13]; + a2 = a[14]; + a3 = a[15]; + c[24] = mac(a0, b0, c[24]); + t0 = mac(a0, b1, c[25]); + c[25] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[26]); + t0 = mac(a1, b1, t0); + c[26] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[27]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[27] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[28]); + t0 = mac(a2, b2, t0); + c[28] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[29]); + c[29] = mac(a3, b2, t0); + c[30] = mac(a3, b3, c[30]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[20] = mac(a0, b0, c[20]); + t0 = mac(a0, b1, c[21]); + c[21] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[22]); + t0 = mac(a1, b1, t0); + c[22] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[23]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[23] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[24]); + t0 = mac(a2, b2, t0); + c[24] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[25]); + c[25] = mac(a3, b2, t0); + c[26] = mac(a3, b3, c[26]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); +} + + +static void transpose(__m256i *M) { + __m256i r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11; + __m256i temp, temp0, temp1, temp2; + + r0 = _mm256_unpacklo_epi16(M[0], M[1]); + r1 = _mm256_unpacklo_epi16(M[2], M[3]); + r2 = _mm256_unpacklo_epi16(M[4], M[5]); + r3 = _mm256_unpacklo_epi16(M[6], M[7]); + r4 = _mm256_unpacklo_epi16(M[8], M[9]); + r5 = _mm256_unpacklo_epi16(M[10], M[11]); + r6 = _mm256_unpacklo_epi16(M[12], M[13]); + r7 = _mm256_unpacklo_epi16(M[14], M[15]); + + temp = _mm256_unpacklo_epi32(r0, r1); + temp0 = _mm256_unpacklo_epi32(r2, r3); + temp1 = _mm256_unpacklo_epi32(r4, r5); + temp2 = _mm256_unpacklo_epi32(r6, r7); + + r8 = _mm256_unpackhi_epi32(r0, r1); + r9 = _mm256_unpackhi_epi32(r2, r3); + r10 = _mm256_unpackhi_epi32(r4, r5); + r11 = _mm256_unpackhi_epi32(r6, r7); + + r0 = _mm256_unpacklo_epi64(temp, temp0); + r2 = _mm256_unpackhi_epi64(temp, temp0); + r1 = _mm256_unpacklo_epi64(temp1, temp2); + r3 = _mm256_unpackhi_epi64(temp1, temp2); + + temp = _mm256_unpackhi_epi16(M[0], M[1]); + temp0 = _mm256_unpackhi_epi16(M[2], M[3]); + temp1 = _mm256_unpackhi_epi16(M[4], M[5]); + temp2 = _mm256_unpackhi_epi16(M[6], M[7]); + + r4 = _mm256_unpackhi_epi16(M[8], M[9]); + M[0] = _mm256_permute2f128_si256(r0, r1, 0x20); + M[8] = _mm256_permute2f128_si256(r0, r1, 0x31); + M[1] = _mm256_permute2f128_si256(r2, r3, 0x20); + M[9] = _mm256_permute2f128_si256(r2, r3, 0x31); + r5 = _mm256_unpackhi_epi16(M[10], M[11]); + r6 = _mm256_unpackhi_epi16(M[12], M[13]); + r7 = _mm256_unpackhi_epi16(M[14], M[15]); + + r0 = _mm256_unpacklo_epi64(r8, r9); + r1 = _mm256_unpacklo_epi64(r10, r11); + r2 = _mm256_unpackhi_epi64(r8, r9); + r3 = _mm256_unpackhi_epi64(r10, r11); + + M[3] = _mm256_permute2f128_si256(r2, r3, 0x20); + M[11] = _mm256_permute2f128_si256(r2, r3, 0x31); + M[2] = _mm256_permute2f128_si256(r0, r1, 0x20); + M[10] = _mm256_permute2f128_si256(r0, r1, 0x31); + + r0 = _mm256_unpacklo_epi32(temp, temp0); + r1 = _mm256_unpacklo_epi32(temp1, temp2); + r2 = _mm256_unpacklo_epi32(r4, r5); + r3 = _mm256_unpacklo_epi32(r6, r7); + + r8 = _mm256_unpacklo_epi64(r0, r1); + r10 = _mm256_unpackhi_epi64(r0, r1); + r9 = _mm256_unpacklo_epi64(r2, r3); + r11 = _mm256_unpackhi_epi64(r2, r3); + + M[4] = _mm256_permute2f128_si256(r8, r9, 0x20); + M[12] = _mm256_permute2f128_si256(r8, r9, 0x31); + M[5] = _mm256_permute2f128_si256(r10, r11, 0x20); + M[13] = _mm256_permute2f128_si256(r10, r11, 0x31); + + r0 = _mm256_unpackhi_epi32(temp, temp0); + r1 = _mm256_unpackhi_epi32(temp1, temp2); + r2 = _mm256_unpackhi_epi32(r4, r5); + r3 = _mm256_unpackhi_epi32(r6, r7); + + r4 = _mm256_unpacklo_epi64(r0, r1); + r6 = _mm256_unpackhi_epi64(r0, r1); + r5 = _mm256_unpacklo_epi64(r2, r3); + r7 = _mm256_unpackhi_epi64(r2, r3); + + M[6] = _mm256_permute2f128_si256(r4, r5, 0x20); + M[14] = _mm256_permute2f128_si256(r4, r5, 0x31); + M[7] = _mm256_permute2f128_si256(r6, r7, 0x20); + M[15] = _mm256_permute2f128_si256(r6, r7, 0x31); +} + +static void batch_64coefficient_multiplications(toom4_points_product *c_eval, const __m256i *a, const toom4_points *b_eval, int accumulate) { + toom4_points a_eval;// Holds evaluation (a & b) for 7 Karatsuba at a time + __m256i r0_avx, r1_avx, r2_avx, r3_avx; + __m256i *va = (__m256i *)a_eval.coeffs; + __m256i *vb = (__m256i *)b_eval->coeffs; + __m256i *vc = (__m256i *)c_eval->coeffs; + + //------------------AVX evaluation for 1st poly----------------------- + r0_avx = a[0 * L + 0]; + r1_avx = a[0 * L + 1]; + r2_avx = a[0 * L + 2]; + r3_avx = a[0 * L + 3]; + + va[0] = r0_avx; + va[1] = r1_avx; + va[2] = r2_avx; + va[3] = r3_avx; + va[4] = _mm256_add_epi16(r0_avx, r1_avx); + va[5] = _mm256_add_epi16(r2_avx, r3_avx); + va[6] = _mm256_add_epi16(r0_avx, r2_avx); + va[7] = _mm256_add_epi16(r1_avx, r3_avx); + va[8] = _mm256_add_epi16(va[6], va[7]); + //------------------AVX evaluation for 1st poly ends------------------ + + //------------------AVX evaluation for 2nd poly----------------------- + r0_avx = a[1 * L + 0]; + r1_avx = a[1 * L + 1]; + r2_avx = a[1 * L + 2]; + r3_avx = a[1 * L + 3]; + + va[0 + 9] = r0_avx; + va[1 + 9] = r1_avx; + va[2 + 9] = r2_avx; + va[3 + 9] = r3_avx; + va[4 + 9] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 9] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 9] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 9] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 9] = _mm256_add_epi16(va[6 + 9], va[7 + 9]); + //------------------AVX evaluation for 2nd poly ends------------------ + + //------------------AVX evaluation for 3rd poly----------------------- + r0_avx = a[2 * L + 0]; + r1_avx = a[2 * L + 1]; + r2_avx = a[2 * L + 2]; + r3_avx = a[2 * L + 3]; + + va[0 + 18] = r0_avx; + va[1 + 18] = r1_avx; + va[2 + 18] = r2_avx; + va[3 + 18] = r3_avx; + va[4 + 18] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 18] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 18] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 18] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 18] = _mm256_add_epi16(va[6 + 18], va[7 + 18]); + //------------------AVX evaluation for 3rd poly ends------------------ + + //------------------AVX evaluation for 4th poly----------------------- + r0_avx = a[3 * L + 0]; + r1_avx = a[3 * L + 1]; + r2_avx = a[3 * L + 2]; + r3_avx = a[3 * L + 3]; + + va[0 + 27] = r0_avx; + va[1 + 27] = r1_avx; + va[2 + 27] = r2_avx; + va[3 + 27] = r3_avx; + va[4 + 27] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 27] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 27] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 27] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 27] = _mm256_add_epi16(va[6 + 27], va[7 + 27]); + //------------------AVX evaluation for 4th poly ends------------------ + + //------------------AVX evaluation for 5th poly----------------------- + r0_avx = a[4 * L + 0]; + r1_avx = a[4 * L + 1]; + r2_avx = a[4 * L + 2]; + r3_avx = a[4 * L + 3]; + + va[0 + 36] = r0_avx; + va[1 + 36] = r1_avx; + va[2 + 36] = r2_avx; + va[3 + 36] = r3_avx; + va[4 + 36] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 36] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 36] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 36] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 36] = _mm256_add_epi16(va[6 + 36], va[7 + 36]); + //------------------AVX evaluation for 5th poly ends------------------ + + //------------------AVX evaluation for 6th poly----------------------- + r0_avx = a[5 * L + 0]; + r1_avx = a[5 * L + 1]; + r2_avx = a[5 * L + 2]; + r3_avx = a[5 * L + 3]; + + va[0 + 45] = r0_avx; + va[1 + 45] = r1_avx; + va[2 + 45] = r2_avx; + va[3 + 45] = r3_avx; + va[4 + 45] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 45] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 45] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 45] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 45] = _mm256_add_epi16(va[6 + 45], va[7 + 45]); + //------------------AVX evaluation for 6th poly ends------------------ + + //------------------AVX evaluation for 7th poly----------------------- + r0_avx = a[6 * L + 0]; + r1_avx = a[6 * L + 1]; + r2_avx = a[6 * L + 2]; + r3_avx = a[6 * L + 3]; + + va[0 + 54] = r0_avx; + va[1 + 54] = r1_avx; + va[2 + 54] = r2_avx; + va[3 + 54] = r3_avx; + va[4 + 54] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 54] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 54] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 54] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 54] = _mm256_add_epi16(va[6 + 54], va[7 + 54]); + //------------------AVX evaluation for 7th poly ends------------------ + + //-----------------Forward transposes-------------------------------------- + transpose(va); + transpose(va + 16); + transpose(va + 32); + transpose(va + 48); + //-----------------Forward transposes ends--------------------------------- + + if (accumulate == 0) { + schoolbook16x16(vc, va, vb); + schoolbook16x16(vc + 32, va + 16, vb + 16); + schoolbook16x16(vc + 64, va + 32, vb + 32); + schoolbook16x16(vc + 96, va + 48, vb + 48); + } else { + schoolbook16x16_acc(vc, va, vb); + schoolbook16x16_acc(vc + 32, va + 16, vb + 16); + schoolbook16x16_acc(vc + 64, va + 32, vb + 32); + schoolbook16x16_acc(vc + 96, va + 48, vb + 48); + } +} + +static void karatsuba_eval(__m256i *b_eval, const __m256i *b) { + __m256i r0_avx, r1_avx, r2_avx, r3_avx; + + //-------1st poly---------------------------------------------------- + r0_avx = b[0 * L + 0]; + r1_avx = b[0 * L + 1]; + r2_avx = b[0 * L + 2]; + r3_avx = b[0 * L + 3]; + + b_eval[0] = r0_avx; + b_eval[1] = r1_avx; + b_eval[2] = r2_avx; + b_eval[3] = r3_avx; + b_eval[4] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8] = _mm256_add_epi16(b_eval[6], b_eval[7]); + + //-------2nd poly---------------------------------------------------- + r0_avx = b[1 * L + 0]; + r1_avx = b[1 * L + 1]; + r2_avx = b[1 * L + 2]; + r3_avx = b[1 * L + 3]; + + b_eval[0 + 9] = r0_avx; + b_eval[1 + 9] = r1_avx; + b_eval[2 + 9] = r2_avx; + b_eval[3 + 9] = r3_avx; + b_eval[4 + 9] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 9] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 9] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 9] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 9] = _mm256_add_epi16(b_eval[6 + 9], b_eval[7 + 9]); + + //-------3rd poly---------------------------------------------------- + r0_avx = b[2 * L + 0]; + r1_avx = b[2 * L + 1]; + r2_avx = b[2 * L + 2]; + r3_avx = b[2 * L + 3]; + + b_eval[0 + 18] = r0_avx; + b_eval[1 + 18] = r1_avx; + b_eval[2 + 18] = r2_avx; + b_eval[3 + 18] = r3_avx; + b_eval[4 + 18] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 18] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 18] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 18] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 18] = _mm256_add_epi16(b_eval[6 + 18], b_eval[7 + 18]); + + //-------4th poly---------------------------------------------------- + r0_avx = b[3 * L + 0]; + r1_avx = b[3 * L + 1]; + r2_avx = b[3 * L + 2]; + r3_avx = b[3 * L + 3]; + + b_eval[0 + 27] = r0_avx; + b_eval[1 + 27] = r1_avx; + b_eval[2 + 27] = r2_avx; + b_eval[3 + 27] = r3_avx; + b_eval[4 + 27] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 27] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 27] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 27] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 27] = _mm256_add_epi16(b_eval[6 + 27], b_eval[7 + 27]); + + //-------5th poly---------------------------------------------------- + r0_avx = b[4 * L + 0]; + r1_avx = b[4 * L + 1]; + r2_avx = b[4 * L + 2]; + r3_avx = b[4 * L + 3]; + + b_eval[0 + 36] = r0_avx; + b_eval[1 + 36] = r1_avx; + b_eval[2 + 36] = r2_avx; + b_eval[3 + 36] = r3_avx; + b_eval[4 + 36] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 36] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 36] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 36] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 36] = _mm256_add_epi16(b_eval[6 + 36], b_eval[7 + 36]); + + //-------6th poly---------------------------------------------------- + r0_avx = b[5 * L + 0]; + r1_avx = b[5 * L + 1]; + r2_avx = b[5 * L + 2]; + r3_avx = b[5 * L + 3]; + + b_eval[0 + 45] = r0_avx; + b_eval[1 + 45] = r1_avx; + b_eval[2 + 45] = r2_avx; + b_eval[3 + 45] = r3_avx; + b_eval[4 + 45] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 45] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 45] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 45] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 45] = _mm256_add_epi16(b_eval[6 + 45], b_eval[7 + 45]); + + //-------7th poly---------------------------------------------------- + r0_avx = b[6 * L + 0]; + r1_avx = b[6 * L + 1]; + r2_avx = b[6 * L + 2]; + r3_avx = b[6 * L + 3]; + + b_eval[0 + 54] = r0_avx; + b_eval[1 + 54] = r1_avx; + b_eval[2 + 54] = r2_avx; + b_eval[3 + 54] = r3_avx; + b_eval[4 + 54] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 54] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 54] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 54] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 54] = _mm256_add_epi16(b_eval[6 + 54], b_eval[7 + 54]); + + //--------------Evaluating B poly ends------------------------------- + transpose(b_eval); + transpose(b_eval + 16); + transpose(b_eval + 32); + transpose(b_eval + 48); +} + +static void karatsuba_interp(__m256i *result_final0, __m256i *result_final1, __m256i *result_final2, __m256i *result_final3, __m256i *result_final4, __m256i *result_final5, __m256i *result_final6, const __m256i *c_eval) { + __m256i res_avx0, res_avx1, res_avx2, res_avx3, res_avx4, res_avx5, res_avx6, res_avx7; // to hold each 64X64 poly mul results + __m256i temp, c6_avx, c7_avx, c8_avx, c20_avx, c21_avx, c22_avx, c23_avx, c24_avx; + + //------------------------AVX interpolation for 1st poly external------------------- + res_avx0 = c_eval[0]; + res_avx2 = c_eval[1]; + res_avx4 = c_eval[2]; + res_avx6 = c_eval[3]; + c6_avx = c_eval[6]; + c7_avx = c_eval[7]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[8], c6_avx), c7_avx); + + res_avx1 = c_eval[16]; + res_avx3 = c_eval[17]; + res_avx5 = c_eval[18]; + res_avx7 = c_eval[19]; + c22_avx = c_eval[22]; + c23_avx = c_eval[23]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[21], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[24], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[20], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[5], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[4], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final0[0] = res_avx0; + result_final0[1] = res_avx1; + result_final0[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final0[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final0[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final0[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final0[6] = res_avx6; + result_final0[7] = res_avx7; + //------------------------AVX interpolation for 1st poly ends-------------- + + + //------------------------AVX interpolation for 2nd poly external------------------- + res_avx0 = c_eval[9]; //c_eval0 + res_avx2 = c_eval[10]; //c_eval1 + res_avx4 = c_eval[11]; //c_eval2 + res_avx6 = c_eval[12]; //c_eval3 + c6_avx = c_eval[15]; //c_eval6 + c7_avx = c_eval[32]; //c_eval7 + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[33], c6_avx), c7_avx); + + res_avx1 = c_eval[25]; //c_eval0 + res_avx3 = c_eval[26]; //c_eval1 + res_avx5 = c_eval[27]; //c_eval2 + res_avx7 = c_eval[28]; //c_eval3 + c22_avx = c_eval[31]; + c23_avx = c_eval[48]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[30], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[49], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[29], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[14], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[13], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final1[0] = res_avx0; + result_final1[1] = res_avx1; + result_final1[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final1[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final1[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final1[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final1[6] = res_avx6; + result_final1[7] = res_avx7; + //------------------------AVX interpolation for 2nd poly ends-------------- + + //------------------------AVX interpolation for 3rd poly external------------------- + res_avx0 = c_eval[34]; //c_eval0 + res_avx2 = c_eval[35]; //c_eval1 + res_avx4 = c_eval[36]; + res_avx6 = c_eval[37]; + c6_avx = c_eval[40]; + c7_avx = c_eval[41]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[42], c6_avx), c7_avx); + + res_avx1 = c_eval[50]; //c_eval0 + res_avx3 = c_eval[51]; //c_eval1 + res_avx5 = c_eval[52]; + res_avx7 = c_eval[53]; + c22_avx = c_eval[56]; + c23_avx = c_eval[57]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[55], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[58], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[54], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[39], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[38], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final2[0] = res_avx0; + result_final2[1] = res_avx1; + result_final2[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final2[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final2[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final2[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final2[6] = res_avx6; + result_final2[7] = res_avx7; + //------------------------AVX interpolation for 3rd poly ends-------------- + + //------------------------AVX interpolation for 4th poly external------------------- + res_avx0 = c_eval[43]; + res_avx2 = c_eval[44]; + res_avx4 = c_eval[45]; + res_avx6 = c_eval[46]; + c6_avx = c_eval[65]; + c7_avx = c_eval[66]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[67], c6_avx), c7_avx); + + res_avx1 = c_eval[59]; + res_avx3 = c_eval[60]; + res_avx5 = c_eval[61]; + res_avx7 = c_eval[62]; + c22_avx = c_eval[81]; + c23_avx = c_eval[82]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[80], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[83], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[63], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[64], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[47], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final3[0] = res_avx0; + result_final3[1] = res_avx1; + result_final3[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final3[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final3[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final3[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final3[6] = res_avx6; + result_final3[7] = res_avx7; + //------------------------AVX interpolation for 4th poly ends-------------- + + //------------------------AVX interpolation for 5th poly external------------------- + res_avx0 = c_eval[68]; + res_avx2 = c_eval[69]; + res_avx4 = c_eval[70]; + res_avx6 = c_eval[71]; + c6_avx = c_eval[74]; + c7_avx = c_eval[75]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[76], c6_avx), c7_avx); + + res_avx1 = c_eval[84]; + res_avx3 = c_eval[85]; + res_avx5 = c_eval[86]; + res_avx7 = c_eval[87]; + c22_avx = c_eval[90]; + c23_avx = c_eval[91]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[89], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[92], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[88], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[73], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[72], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final4[0] = res_avx0; + result_final4[1] = res_avx1; + result_final4[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final4[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final4[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final4[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final4[6] = res_avx6; + result_final4[7] = res_avx7; + //------------------------AVX interpolation for 5th poly ends-------------- + + //------------------------AVX interpolation for 6th poly external------------------- + res_avx0 = c_eval[77]; + res_avx2 = c_eval[78]; + res_avx4 = c_eval[79]; + res_avx6 = c_eval[96]; + c6_avx = c_eval[99]; + c7_avx = c_eval[100]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[101], c6_avx), c7_avx); + + res_avx1 = c_eval[93]; + res_avx3 = c_eval[94]; + res_avx5 = c_eval[95]; + res_avx7 = c_eval[112]; + c22_avx = c_eval[115]; + c23_avx = c_eval[116]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[114], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[117], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[113], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[98], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[97], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final5[0] = res_avx0; + result_final5[1] = res_avx1; + result_final5[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final5[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final5[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final5[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final5[6] = res_avx6; + result_final5[7] = res_avx7; + //------------------------AVX interpolation for 6th poly ends-------------- + + //------------------------AVX interpolation for 7th poly external------------------- + res_avx0 = c_eval[102]; + res_avx2 = c_eval[103]; + res_avx4 = c_eval[104]; + res_avx6 = c_eval[105]; + c6_avx = c_eval[108]; + c7_avx = c_eval[109]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[110], c6_avx), c7_avx); + + res_avx1 = c_eval[118]; + res_avx3 = c_eval[119]; + res_avx5 = c_eval[120]; + res_avx7 = c_eval[121]; + c22_avx = c_eval[124]; + c23_avx = c_eval[125]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[123], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[126], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[122], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[107], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[106], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final6[0] = res_avx0; + result_final6[1] = res_avx1; + result_final6[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final6[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final6[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final6[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final6[6] = res_avx6; + result_final6[7] = res_avx7; + //------------------------AVX interpolation for 7th poly ends-------------- +} + +void PQCLEAN_FIRESABER_AVX2_toom4_mul_A_by_B_eval(toom4_points_product *c_eval, const poly *a, const toom4_points *b_eval, int accumulate) { + size_t i; + __m256i r0_avx, r1_avx, r2_avx, r3_avx, r4_avx, r5_avx, r6_avx; + __m256i aw_avx[7 * L]; + __m256i *va = (__m256i *)a->coeffs; + + for (i = 0; i < L; i++) { + r0_avx = va[0 * L + i]; + r1_avx = va[1 * L + i]; + r2_avx = va[2 * L + i]; + r3_avx = va[3 * L + i]; + r4_avx = _mm256_add_epi16(r0_avx, r2_avx); + r5_avx = _mm256_add_epi16(r1_avx, r3_avx); + aw_avx[2 * L + i] = _mm256_add_epi16(r4_avx, r5_avx); + aw_avx[3 * L + i] = _mm256_sub_epi16(r4_avx, r5_avx); + r4_avx = _mm256_slli_epi16(r0_avx, 2); + r4_avx = _mm256_add_epi16(r4_avx, r2_avx); + r4_avx = _mm256_slli_epi16(r4_avx, 1); + r5_avx = _mm256_slli_epi16(r1_avx, 2); + r5_avx = _mm256_add_epi16(r5_avx, r3_avx); + aw_avx[4 * L + i] = _mm256_add_epi16(r4_avx, r5_avx); + aw_avx[5 * L + i] = _mm256_sub_epi16(r4_avx, r5_avx); + r4_avx = _mm256_slli_epi16(r3_avx, 3); + r6_avx = _mm256_slli_epi16(r2_avx, 2); + r4_avx = _mm256_add_epi16(r4_avx, r6_avx); + r6_avx = _mm256_slli_epi16(r1_avx, 1); + r4_avx = _mm256_add_epi16(r4_avx, r6_avx); + aw_avx[1 * L + i] = _mm256_add_epi16(r4_avx, r0_avx); + aw_avx[6 * L + i] = r0_avx; + aw_avx[0 * L + i] = r3_avx; + } + + batch_64coefficient_multiplications(c_eval, aw_avx, b_eval, accumulate); +} + +void PQCLEAN_FIRESABER_AVX2_toom4_eval(toom4_points *b_eval, const poly *b) { + size_t i; + __m256i bw_avx[7 * L]; + __m256i r0_avx, r1_avx, r2_avx, r3_avx, r4_avx, r5_avx, r6_avx; + __m256i *vb = (__m256i *)b->coeffs; + __m256i *vb_eval = (__m256i *)b_eval->coeffs; + + for (i = 0; i < L; i++) { + r0_avx = vb[0 * L + i]; + r1_avx = vb[1 * L + i]; + r2_avx = vb[2 * L + i]; + r3_avx = vb[3 * L + i]; + r4_avx = _mm256_add_epi16(r0_avx, r2_avx); + r5_avx = _mm256_add_epi16(r1_avx, r3_avx); + bw_avx[2 * L + i] = _mm256_add_epi16(r4_avx, r5_avx); + bw_avx[3 * L + i] = _mm256_sub_epi16(r4_avx, r5_avx); + r4_avx = _mm256_slli_epi16(r0_avx, 2); + r4_avx = _mm256_add_epi16(r4_avx, r2_avx); + r4_avx = _mm256_slli_epi16(r4_avx, 1); + r5_avx = _mm256_slli_epi16(r1_avx, 2); + r5_avx = _mm256_add_epi16(r5_avx, r3_avx); + bw_avx[4 * L + i] = _mm256_add_epi16(r4_avx, r5_avx); + bw_avx[5 * L + i] = _mm256_sub_epi16(r4_avx, r5_avx); + r4_avx = _mm256_slli_epi16(r3_avx, 3); + r6_avx = _mm256_slli_epi16(r2_avx, 2); + r4_avx = _mm256_add_epi16(r4_avx, r6_avx); + r6_avx = _mm256_slli_epi16(r1_avx, 1); + r4_avx = _mm256_add_epi16(r4_avx, r6_avx); + bw_avx[1 * L + i] = _mm256_add_epi16(r4_avx, r0_avx); + bw_avx[6 * L + i] = r0_avx; + bw_avx[0 * L + i] = r3_avx; + } + + karatsuba_eval(vb_eval, bw_avx); +} + + +void PQCLEAN_FIRESABER_AVX2_toom4_interp(poly *res, const toom4_points_product *c_eval) { + size_t i; + __m256i r0_avx, r1_avx, r2_avx, r3_avx, r4_avx, r5_avx, r6_avx, temp_avx; + __m256i w1_avx[2 * L], w2_avx[2 * L], w3_avx[2 * L], w4_avx[2 * L], w5_avx[2 * L], w6_avx[2 * L], w7_avx[2 * L]; + __m256i res_full[32]; + __m256i *vc = (__m256i *)c_eval->coeffs; + __m256i *vres = (__m256i *)res->coeffs; + + transpose(vc); + transpose(vc + 16); + transpose(vc + 32); + transpose(vc + 48); + transpose(vc + 64); + transpose(vc + 80); + transpose(vc + 96); + transpose(vc + 112); + + karatsuba_interp(w1_avx, w2_avx, w3_avx, w4_avx, w5_avx, w6_avx, w7_avx, vc); + + for (i = 0; i < 2 * L; i++) { + r0_avx = w1_avx[i]; + r1_avx = w2_avx[i]; + r2_avx = w3_avx[i]; + r3_avx = w4_avx[i]; + r4_avx = w5_avx[i]; + r5_avx = w6_avx[i]; + r6_avx = w7_avx[i]; + + r1_avx = _mm256_add_epi16(r1_avx, r4_avx); + r5_avx = _mm256_sub_epi16(r5_avx, r4_avx); + r3_avx = _mm256_sub_epi16(r3_avx, r2_avx); + r3_avx = _mm256_srli_epi16(r3_avx, 1); + r4_avx = _mm256_sub_epi16(r4_avx, r0_avx); + temp_avx = _mm256_slli_epi16(r6_avx, 6); + + r4_avx = _mm256_sub_epi16(r4_avx, temp_avx); + r4_avx = _mm256_slli_epi16(r4_avx, 1); + r4_avx = _mm256_add_epi16(r4_avx, r5_avx); + r2_avx = _mm256_add_epi16(r2_avx, r3_avx); + temp_avx = _mm256_slli_epi16(r2_avx, 6); + + r1_avx = _mm256_sub_epi16(r1_avx, temp_avx); + r1_avx = _mm256_sub_epi16(r1_avx, r2_avx); + r2_avx = _mm256_sub_epi16(r2_avx, r6_avx); + r2_avx = _mm256_sub_epi16(r2_avx, r0_avx); + temp_avx = _mm256_mullo_epi16(r2_avx, _mm256_set1_epi16(45)); + + r1_avx = _mm256_add_epi16(r1_avx, temp_avx); + temp_avx = _mm256_slli_epi16(r2_avx, 3); + + r4_avx = _mm256_sub_epi16(r4_avx, temp_avx); + r4_avx = _mm256_mullo_epi16(r4_avx, _mm256_set1_epi16(-21845)); // -21845 = 1/3 (mod 2^16) + r4_avx = _mm256_srli_epi16(r4_avx, 3); + r5_avx = _mm256_add_epi16(r5_avx, r1_avx); + temp_avx = _mm256_slli_epi16(r3_avx, 4); + + r1_avx = _mm256_add_epi16(r1_avx, temp_avx); + r1_avx = _mm256_mullo_epi16(r1_avx, _mm256_set1_epi16(-29127)); // -29127 = 1/9 (mod 2^16) + r1_avx = _mm256_srli_epi16(r1_avx, 1); + r3_avx = _mm256_add_epi16(r1_avx, r3_avx); + r3_avx = _mm256_sub_epi16(_mm256_set1_epi16(0), r3_avx); + temp_avx = _mm256_mullo_epi16(r1_avx, _mm256_set1_epi16(30)); + temp_avx = _mm256_sub_epi16(temp_avx, r5_avx); + temp_avx = _mm256_mullo_epi16(temp_avx, _mm256_set1_epi16(-4369)); // -4369 = 1/15 (mod 2^16) + + r5_avx = _mm256_srli_epi16(temp_avx, 2); + r2_avx = _mm256_sub_epi16(r2_avx, r4_avx); + r1_avx = _mm256_sub_epi16(r1_avx, r5_avx); + + if (i < L) { + res_full[0 * L + i] = r6_avx; + res_full[1 * L + i] = r5_avx; + res_full[2 * L + i] = r4_avx; + res_full[3 * L + i] = r3_avx; + res_full[4 * L + i] = r2_avx; + res_full[5 * L + i] = r1_avx; + res_full[6 * L + i] = r0_avx; + } else { + res_full[0 * L + i] = _mm256_add_epi16(res_full[0 * L + i], r6_avx); + res_full[1 * L + i] = _mm256_add_epi16(res_full[1 * L + i], r5_avx); + res_full[2 * L + i] = _mm256_add_epi16(res_full[2 * L + i], r4_avx); + res_full[3 * L + i] = _mm256_add_epi16(res_full[3 * L + i], r3_avx); + res_full[4 * L + i] = _mm256_add_epi16(res_full[4 * L + i], r2_avx); + res_full[5 * L + i] = _mm256_add_epi16(res_full[5 * L + i], r1_avx); + res_full[6 * L + i] = r0_avx; + } + } + + // Reduction by X^256 + 1 + for (i = 0; i < 16; i++) { + vres[i] = _mm256_sub_epi16(res_full[i], res_full[i + 16]); + } +} diff --git a/crypto_kem/firesaber/avx2/verify.c b/crypto_kem/firesaber/avx2/verify.c new file mode 100644 index 00000000..d78e12e0 --- /dev/null +++ b/crypto_kem/firesaber/avx2/verify.c @@ -0,0 +1,35 @@ +#include "verify.h" + +/*------------------------------------------------- +This file has been adapted from the implementation +(available at https://github.com/pq-crystals/kyber) of +"CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" + by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, +Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle +----------------------------------------------------*/ + + +/* returns 0 for equal strings, 1 for non-equal strings */ +uint8_t PQCLEAN_FIRESABER_AVX2_verify(const uint8_t *a, const uint8_t *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 (uint8_t) r; +} + +/* b = 1 means mov, b = 0 means don't mov*/ +void PQCLEAN_FIRESABER_AVX2_cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b) { + size_t i; + + b = -b; + for (i = 0; i < len; i++) { + r[i] ^= b & (x[i] ^ r[i]); + } +} diff --git a/crypto_kem/firesaber/avx2/verify.h b/crypto_kem/firesaber/avx2/verify.h new file mode 100644 index 00000000..2ec50370 --- /dev/null +++ b/crypto_kem/firesaber/avx2/verify.h @@ -0,0 +1,22 @@ +#ifndef VERIFY_H +#define VERIFY_H +/*------------------------------------------------- +This file has been adapted from the implementation +(available at https://github.com/pq-crystals/kyber) of +"CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" + by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, +Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle +----------------------------------------------------*/ + +#include +#include + +/* returns 0 for equal strings, 1 for non-equal strings */ +uint8_t PQCLEAN_FIRESABER_AVX2_verify(const uint8_t *a, const uint8_t *b, size_t len); + + +/* b = 1 means mov, b = 0 means don't mov*/ +void PQCLEAN_FIRESABER_AVX2_cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b); + + +#endif diff --git a/crypto_kem/firesaber/clean/LICENSE b/crypto_kem/firesaber/clean/LICENSE index 08c799e3..d5d21fff 100644 --- a/crypto_kem/firesaber/clean/LICENSE +++ b/crypto_kem/firesaber/clean/LICENSE @@ -1,8 +1 @@ ----------------------------------------------------------------------------------------- -SABER_v1.1 - -Public domain - -Authors: Jan-Pieter D'Anvers, Angshuman Karmakar, Sujoy Sinha Roy, -Frederik Vercauteren ----------------------------------------------------------------------------------------- +Public Domain diff --git a/crypto_kem/firesaber/clean/Makefile b/crypto_kem/firesaber/clean/Makefile index e00112e8..8f8dd8f7 100644 --- a/crypto_kem/firesaber/clean/Makefile +++ b/crypto_kem/firesaber/clean/Makefile @@ -1,10 +1,10 @@ # This Makefile can be used with GNU Make or BSD Make LIB=libfiresaber_clean.a -HEADERS=api.h cbd.h poly.h poly_mul.h SABER_indcpa.h SABER_params.h verify.h pack_unpack.h +HEADERS=api.h cbd.h pack_unpack.h poly.h poly_mul.h SABER_indcpa.h SABER_params.h verify.h OBJECTS=cbd.o kem.o pack_unpack.o poly.o poly_mul.o SABER_indcpa.o verify.o -CFLAGS=-O3 -Wall -Wextra -Wpedantic -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) +CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wredundant-decls -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) all: $(LIB) diff --git a/crypto_kem/firesaber/clean/SABER_indcpa.c b/crypto_kem/firesaber/clean/SABER_indcpa.c index da8aa685..a9e7c141 100644 --- a/crypto_kem/firesaber/clean/SABER_indcpa.c +++ b/crypto_kem/firesaber/clean/SABER_indcpa.c @@ -3,296 +3,111 @@ #include "fips202.h" #include "pack_unpack.h" #include "poly.h" -#include "poly_mul.h" #include "randombytes.h" #include #include +#define h1 (1 << (SABER_EQ - SABER_EP - 1)) +#define h2 ((1 << (SABER_EP - 2)) - (1 << (SABER_EP - SABER_ET - 1)) + (1 << (SABER_EQ - SABER_EP - 1))) + +void PQCLEAN_FIRESABER_CLEAN_indcpa_kem_keypair(uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES], uint8_t sk[SABER_INDCPA_SECRETKEYBYTES]) { + size_t i, j; + + poly A[SABER_L][SABER_L]; + poly s[SABER_L]; + poly res[SABER_L]; + + uint8_t rand[SABER_NOISESEEDBYTES]; + uint8_t *seed_A = pk + SABER_POLYVECCOMPRESSEDBYTES; + + randombytes(seed_A, SABER_SEEDBYTES); + shake128(seed_A, SABER_SEEDBYTES, seed_A, SABER_SEEDBYTES); // for not revealing system RNG state + + randombytes(rand, SABER_NOISESEEDBYTES); + PQCLEAN_FIRESABER_CLEAN_GenSecret(s, rand); + PQCLEAN_FIRESABER_CLEAN_POLVECq2BS(sk, s); + + PQCLEAN_FIRESABER_CLEAN_GenMatrix(A, seed_A); // sample matrix A + PQCLEAN_FIRESABER_CLEAN_MatrixVectorMul(res, (const poly (*)[SABER_L])A, (const poly *)s, 1); // Matrix in transposed order -/*----------------------------------------------------------------------------------- - This routine generates a=[Matrix K x K] of 256-coefficient polynomials --------------------------------------------------------------------------------------*/ - -#define h1 4 //2^(EQ-EP-1) - -#define h2 ( (1<<(SABER_EP-2)) - (1<<(SABER_EP-SABER_ET-1)) + (1<<(SABER_EQ-SABER_EP-1)) ) - -static void InnerProd(uint16_t pkcl[SABER_K][SABER_N], uint16_t skpv[SABER_K][SABER_N], uint16_t mod, uint16_t res[SABER_N]); -static void MatrixVectorMul(polyvec *a, uint16_t skpv[SABER_K][SABER_N], uint16_t res[SABER_K][SABER_N], uint16_t mod, int16_t transpose); - -static void POL2MSG(const uint16_t *message_dec_unpacked, unsigned char *message_dec); - -static void GenMatrix(polyvec *a, const unsigned char *seed) { - unsigned char buf[SABER_K * SABER_K * (13 * SABER_N / 8)]; - - uint16_t temp_ar[SABER_N]; - - int i, j, k; - uint16_t mod = (SABER_Q - 1); - - shake128(buf, sizeof(buf), seed, SABER_SEEDBYTES); - - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_K; j++) { - PQCLEAN_FIRESABER_CLEAN_BS2POL(buf + (i * SABER_K + j) * (13 * SABER_N / 8), temp_ar); - for (k = 0; k < SABER_N; k++) { - a[i].vec[j].coeffs[k] = (temp_ar[k])& mod ; - } + // rounding + for (i = 0; i < SABER_L; i++) { + for (j = 0; j < SABER_N; j++) { + res[i].coeffs[j] += h1; + res[i].coeffs[j] >>= SABER_EQ - SABER_EP; + res[i].coeffs[j] &= SABER_Q - 1; } } + + PQCLEAN_FIRESABER_CLEAN_POLVECp2BS(pk, res); // pack public key } -void PQCLEAN_FIRESABER_CLEAN_indcpa_kem_keypair(unsigned char *pk, unsigned char *sk) { - polyvec a[SABER_K]; +void PQCLEAN_FIRESABER_CLEAN_indcpa_kem_enc(uint8_t ciphertext[SABER_BYTES_CCA_DEC], const uint8_t m[SABER_KEYBYTES], const uint8_t noiseseed[SABER_NOISESEEDBYTES], const uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES]) { + size_t i, j; - uint16_t skpv[SABER_K][SABER_N]; + poly A[SABER_L][SABER_L]; + poly res[SABER_L]; + poly s[SABER_L]; + poly *temp = A[0]; // re-use stack space + poly *vprime = &A[0][0]; + poly *message = &A[0][1]; - unsigned char seed[SABER_SEEDBYTES]; - unsigned char noiseseed[SABER_COINBYTES]; - int32_t i, j; - uint16_t mod_q = SABER_Q - 1; + const uint8_t *seed_A = pk + SABER_POLYVECCOMPRESSEDBYTES; + uint8_t *msk_c = ciphertext + SABER_POLYVECCOMPRESSEDBYTES; + + PQCLEAN_FIRESABER_CLEAN_GenSecret(s, noiseseed); + PQCLEAN_FIRESABER_CLEAN_GenMatrix(A, seed_A); + PQCLEAN_FIRESABER_CLEAN_MatrixVectorMul(res, (const poly (*)[SABER_L])A, (const poly *)s, 0); // 0 => not transposed - uint16_t res[SABER_K][SABER_N]; - - randombytes(seed, SABER_SEEDBYTES); - - // for not revealing system RNG state - shake128(seed, SABER_SEEDBYTES, seed, SABER_SEEDBYTES); - randombytes(noiseseed, SABER_COINBYTES); - - GenMatrix(a, seed); //sample matrix A - - // generate secret from constant-time binomial distribution - PQCLEAN_FIRESABER_CLEAN_GenSecret(skpv, noiseseed); - - // do the matrix vector multiplication and rounding - for (i = 0; i < SABER_K; i++) { + // rounding + for (i = 0; i < SABER_L; i++) { //shift right EQ-EP bits for (j = 0; j < SABER_N; j++) { - res[i][j] = 0; - } - } - MatrixVectorMul(a, skpv, res, SABER_Q - 1, 1); - - // now rounding - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - // shift right 3 bits - res[i][j] = (res[i][j] + h1) & (mod_q); - res[i][j] = (res[i][j] >> (SABER_EQ - SABER_EP)); - } - } - - // unload and pack sk=3 x (256 coefficients of 14 bits) - PQCLEAN_FIRESABER_CLEAN_POLVEC2BS(sk, skpv, SABER_Q); - - // unload and pack pk=256 bits seed and 3 x (256 coefficients of 11 bits) - // load the public-key coefficients - PQCLEAN_FIRESABER_CLEAN_POLVEC2BS(pk, res, SABER_P); - - - // now load the seedbytes in PK. Easy since seed bytes are kept in byte format. - for (i = 0; i < SABER_SEEDBYTES; i++) { - pk[SABER_POLYVECCOMPRESSEDBYTES + i] = seed[i]; - } - -} - - -void PQCLEAN_FIRESABER_CLEAN_indcpa_kem_enc(const unsigned char *message_received, unsigned char *noiseseed, const unsigned char *pk, unsigned char *ciphertext) { - uint32_t i, j, k; - polyvec a[SABER_K]; - unsigned char seed[SABER_SEEDBYTES]; - // public key of received by the client - uint16_t pkcl[SABER_K][SABER_N]; - uint16_t skpv1[SABER_K][SABER_N]; - uint16_t message[SABER_KEYBYTES * 8]; - uint16_t res[SABER_K][SABER_N]; - uint16_t mod_p = SABER_P - 1; - uint16_t mod_q = SABER_Q - 1; - uint16_t vprime[SABER_N]; - unsigned char msk_c[SABER_SCALEBYTES_KEM]; - - // extract the seedbytes from Public Key. - for (i = 0; i < SABER_SEEDBYTES; i++) { - seed[i] = pk[ SABER_POLYVECCOMPRESSEDBYTES + i]; - } - - GenMatrix(a, seed); - - // generate secret from constant-time binomial distribution - PQCLEAN_FIRESABER_CLEAN_GenSecret(skpv1, noiseseed); - - // matrix-vector multiplication and rounding - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - res[i][j] = 0; - } - } - MatrixVectorMul(a, skpv1, res, SABER_Q - 1, 0); - - // now rounding - //shift right 3 bits - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - res[i][j] = ( res[i][j] + h1 ) & mod_q; - res[i][j] = (res[i][j] >> (SABER_EQ - SABER_EP) ); - } - } - - PQCLEAN_FIRESABER_CLEAN_POLVEC2BS(ciphertext, res, SABER_P); - - // ************client matrix-vector multiplication ends************ - - // now calculate the v' - // unpack the public_key - // pkcl is the b in the protocol - PQCLEAN_FIRESABER_CLEAN_BS2POLVEC(pk, pkcl, SABER_P); - for (i = 0; i < SABER_N; i++) { - vprime[i] = 0; - } - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - skpv1[i][j] = skpv1[i][j] & (mod_p); + res[i].coeffs[j] += h1; + res[i].coeffs[j] >>= SABER_EQ - SABER_EP; + res[i].coeffs[j] &= SABER_Q - 1; } } + PQCLEAN_FIRESABER_CLEAN_POLVECp2BS(ciphertext, res); // vector-vector scalar multiplication with mod p - InnerProd(pkcl, skpv1, mod_p, vprime); + PQCLEAN_FIRESABER_CLEAN_BS2POLVECp(temp, pk); + PQCLEAN_FIRESABER_CLEAN_InnerProd(vprime, temp, s); + PQCLEAN_FIRESABER_CLEAN_BS2POLmsg(message, m); - // addition of h1 to vprime for (i = 0; i < SABER_N; i++) { - vprime[i] = vprime[i] + h1; + vprime->coeffs[i] += h1 - (message->coeffs[i] << (SABER_EP - 1)); + vprime->coeffs[i] &= SABER_P - 1; + vprime->coeffs[i] >>= SABER_EP - SABER_ET; } - // unpack message_received; - for (j = 0; j < SABER_KEYBYTES; j++) { - for (i = 0; i < 8; i++) { - message[8 * j + i] = ((message_received[j] >> i) & 0x01); - } - } + PQCLEAN_FIRESABER_CLEAN_POLT2BS(msk_c, vprime); +} + + +void PQCLEAN_FIRESABER_CLEAN_indcpa_kem_dec(uint8_t m[SABER_KEYBYTES], const uint8_t sk[SABER_INDCPA_SECRETKEYBYTES], const uint8_t ciphertext[SABER_BYTES_CCA_DEC]) { + size_t i; + + poly temp[SABER_L]; + poly s[SABER_L]; + + const uint8_t *packed_cm = ciphertext + SABER_POLYVECCOMPRESSEDBYTES; + poly *v = &temp[0]; + poly *cm = &temp[1]; + + PQCLEAN_FIRESABER_CLEAN_BS2POLVECq(s, sk); + PQCLEAN_FIRESABER_CLEAN_BS2POLVECp(temp, ciphertext); + PQCLEAN_FIRESABER_CLEAN_InnerProd(&temp[0], temp, s); + + PQCLEAN_FIRESABER_CLEAN_BS2POLT(cm, packed_cm); - // message encoding for (i = 0; i < SABER_N; i++) { - message[i] = (message[i] << (SABER_EP - 1)); + v->coeffs[i] += h2 - (cm->coeffs[i] << (SABER_EP - SABER_ET)); + v->coeffs[i] &= SABER_P - 1; + v->coeffs[i] >>= SABER_EP - 1; } - for (k = 0; k < SABER_N; k++) { - vprime[k] = ( (vprime[k] - message[k]) & (mod_p) ) >> (SABER_EP - SABER_ET); - } - - - PQCLEAN_FIRESABER_CLEAN_pack_6bit(msk_c, vprime); - - for (j = 0; j < SABER_SCALEBYTES_KEM; j++) { - ciphertext[SABER_POLYVECCOMPRESSEDBYTES + j] = msk_c[j]; - } -} - - -void PQCLEAN_FIRESABER_CLEAN_indcpa_kem_dec(const unsigned char *sk, const unsigned char *ciphertext, unsigned char message_dec[]) { - uint32_t i, j; - // secret key of the server - uint16_t sksv[SABER_K][SABER_N]; - uint16_t pksv[SABER_K][SABER_N]; - uint8_t scale_ar[SABER_SCALEBYTES_KEM]; - uint16_t mod_p = SABER_P - 1; - uint16_t v[SABER_N]; - uint16_t op[SABER_N]; - - // sksv is the secret-key - PQCLEAN_FIRESABER_CLEAN_BS2POLVEC(sk, sksv, SABER_Q); - // pksv is the ciphertext - PQCLEAN_FIRESABER_CLEAN_BS2POLVEC(ciphertext, pksv, SABER_P); - - // vector-vector scalar multiplication with mod p - for (i = 0; i < SABER_N; i++) { - v[i] = 0; - } - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - sksv[i][j] = sksv[i][j] & (mod_p); - } - } - InnerProd(pksv, sksv, mod_p, v); - - //Extraction - for (i = 0; i < SABER_SCALEBYTES_KEM; i++) { - scale_ar[i] = ciphertext[SABER_POLYVECCOMPRESSEDBYTES + i]; - } - - PQCLEAN_FIRESABER_CLEAN_un_pack6bit(scale_ar, op); - - //addition of h1 - for (i = 0; i < SABER_N; i++) { - v[i] = ( ( v[i] + h2 - (op[i] << (SABER_EP - SABER_ET)) ) & (mod_p) ) >> (SABER_EP - 1); - } - - // pack decrypted message - POL2MSG(v, message_dec); -} -static void MatrixVectorMul(polyvec *a, uint16_t skpv[SABER_K][SABER_N], uint16_t res[SABER_K][SABER_N], uint16_t mod, int16_t transpose) { - uint16_t acc[SABER_N]; - int32_t i, j, k; - - if (transpose == 1) { - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_K; j++) { - PQCLEAN_FIRESABER_CLEAN_pol_mul((uint16_t *)&a[j].vec[i], skpv[j], acc, SABER_Q, SABER_N); - - for (k = 0; k < SABER_N; k++) { - res[i][k] = res[i][k] + acc[k]; - //reduction mod p - res[i][k] = (res[i][k] & mod); - //clear the accumulator - acc[k] = 0; - } - } - } - } else { - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_K; j++) { - PQCLEAN_FIRESABER_CLEAN_pol_mul((uint16_t *)&a[i].vec[j], skpv[j], acc, SABER_Q, SABER_N); - for (k = 0; k < SABER_N; k++) { - res[i][k] = res[i][k] + acc[k]; - // reduction - res[i][k] = res[i][k] & mod; - // clear the accumulator - acc[k] = 0; - } - } - } - } -} - -static void POL2MSG(const uint16_t *message_dec_unpacked, unsigned char *message_dec) { - int32_t i, j; - - for (j = 0; j < SABER_KEYBYTES; j++) { - message_dec[j] = 0; - for (i = 0; i < 8; i++) { - message_dec[j] = message_dec[j] | (uint8_t) (message_dec_unpacked[j * 8 + i] << i); - } - } -} - - -static void InnerProd(uint16_t pkcl[SABER_K][SABER_N], uint16_t skpv[SABER_K][SABER_N], uint16_t mod, uint16_t res[SABER_N]) { - uint32_t j, k; - uint16_t acc[SABER_N]; - - // vector-vector scalar multiplication with mod p - for (j = 0; j < SABER_K; j++) { - PQCLEAN_FIRESABER_CLEAN_pol_mul(pkcl[j], skpv[j], acc, SABER_P, SABER_N); - - for (k = 0; k < SABER_N; k++) { - res[k] = res[k] + acc[k]; - // reduction - res[k] = res[k] & mod; - // clear the accumulator - acc[k] = 0; - } - } + PQCLEAN_FIRESABER_CLEAN_POLmsg2BS(m, v); } diff --git a/crypto_kem/firesaber/clean/SABER_indcpa.h b/crypto_kem/firesaber/clean/SABER_indcpa.h index 6007352d..cc009afe 100644 --- a/crypto_kem/firesaber/clean/SABER_indcpa.h +++ b/crypto_kem/firesaber/clean/SABER_indcpa.h @@ -1,9 +1,13 @@ #ifndef INDCPA_H #define INDCPA_H +#include "SABER_params.h" +#include + +void PQCLEAN_FIRESABER_CLEAN_indcpa_kem_keypair(uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES], uint8_t sk[SABER_INDCPA_SECRETKEYBYTES]); + +void PQCLEAN_FIRESABER_CLEAN_indcpa_kem_enc(uint8_t ciphertext[SABER_BYTES_CCA_DEC], const uint8_t m[SABER_KEYBYTES], const uint8_t noiseseed[SABER_NOISESEEDBYTES], const uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES]); + +void PQCLEAN_FIRESABER_CLEAN_indcpa_kem_dec(uint8_t m[SABER_KEYBYTES], const uint8_t sk[SABER_INDCPA_SECRETKEYBYTES], const uint8_t ciphertext[SABER_BYTES_CCA_DEC]); -void PQCLEAN_FIRESABER_CLEAN_indcpa_kem_keypair(unsigned char *pk, unsigned char *sk); -void PQCLEAN_FIRESABER_CLEAN_indcpa_kem_enc(const unsigned char *message, unsigned char *noiseseed, const unsigned char *pk, unsigned char *ciphertext); -void PQCLEAN_FIRESABER_CLEAN_indcpa_kem_dec(const unsigned char *sk, const unsigned char *ciphertext, unsigned char *message_dec); #endif - diff --git a/crypto_kem/firesaber/clean/SABER_params.h b/crypto_kem/firesaber/clean/SABER_params.h index b0d517f8..6481efec 100644 --- a/crypto_kem/firesaber/clean/SABER_params.h +++ b/crypto_kem/firesaber/clean/SABER_params.h @@ -1,49 +1,41 @@ #ifndef PARAMS_H #define PARAMS_H -#include "api.h" -#define SABER_K 4 +/* Don't change anything below this line */ +#define SABER_L 4 #define SABER_MU 6 #define SABER_ET 6 -#define SABER_EQ 13 -#define SABER_EP 10 - #define SABER_N 256 -#define SABER_Q 8192 -#define SABER_P 1024 -#define SABER_SEEDBYTES 32 -#define SABER_NOISESEEDBYTES 32 -#define SABER_COINBYTES 32 -#define SABER_KEYBYTES 32 +#define SABER_EP 10 +#define SABER_P (1 << SABER_EP) -#define SABER_HASHBYTES 32 +#define SABER_EQ 13 +#define SABER_Q (1 << SABER_EQ) -#define SABER_POLYBYTES 416 //13*256/8 +#define SABER_SEEDBYTES 32 +#define SABER_NOISESEEDBYTES 32 +#define SABER_KEYBYTES 32 +#define SABER_HASHBYTES 32 -#define SABER_POLYVECBYTES (SABER_K * SABER_POLYBYTES) +#define SABER_POLYCOINBYTES (SABER_MU * SABER_N / 8) -#define SABER_POLYVECCOMPRESSEDBYTES (SABER_K * 320) //10*256/8 NOTE : changed till here due to parameter adaptation +#define SABER_POLYBYTES (SABER_EQ * SABER_N / 8) +#define SABER_POLYVECBYTES (SABER_L * SABER_POLYBYTES) -#define SABER_CIPHERTEXTBYTES (SABER_POLYVECCOMPRESSEDBYTES) +#define SABER_POLYCOMPRESSEDBYTES (SABER_EP * SABER_N / 8) +#define SABER_POLYVECCOMPRESSEDBYTES (SABER_L * SABER_POLYCOMPRESSEDBYTES) -#define SABER_SCALEBYTES (SABER_DELTA*SABER_N/8) - -#define SABER_SCALEBYTES_KEM ((SABER_ET)*SABER_N/8) +#define SABER_SCALEBYTES_KEM (SABER_ET * SABER_N / 8) #define SABER_INDCPA_PUBLICKEYBYTES (SABER_POLYVECCOMPRESSEDBYTES + SABER_SEEDBYTES) #define SABER_INDCPA_SECRETKEYBYTES (SABER_POLYVECBYTES) #define SABER_PUBLICKEYBYTES (SABER_INDCPA_PUBLICKEYBYTES) +#define SABER_SECRETKEYBYTES (SABER_INDCPA_SECRETKEYBYTES + SABER_INDCPA_PUBLICKEYBYTES + SABER_HASHBYTES + SABER_KEYBYTES) -#define SABER_SECRETKEYBYTES (SABER_INDCPA_SECRETKEYBYTES + SABER_INDCPA_PUBLICKEYBYTES + SABER_HASHBYTES + SABER_KEYBYTES) - -#define SABER_BYTES_CCA_DEC (SABER_POLYVECCOMPRESSEDBYTES + SABER_SCALEBYTES_KEM) /* Second part is for Targhi-Unruh */ - - - +#define SABER_BYTES_CCA_DEC (SABER_POLYVECCOMPRESSEDBYTES + SABER_SCALEBYTES_KEM) #endif - diff --git a/crypto_kem/firesaber/clean/api.h b/crypto_kem/firesaber/clean/api.h index 56d17038..fdff18fa 100644 --- a/crypto_kem/firesaber/clean/api.h +++ b/crypto_kem/firesaber/clean/api.h @@ -1,14 +1,18 @@ #ifndef PQCLEAN_FIRESABER_CLEAN_API_H #define PQCLEAN_FIRESABER_CLEAN_API_H + #define PQCLEAN_FIRESABER_CLEAN_CRYPTO_ALGNAME "FireSaber" -#define PQCLEAN_FIRESABER_CLEAN_CRYPTO_SECRETKEYBYTES 3040 -#define PQCLEAN_FIRESABER_CLEAN_CRYPTO_PUBLICKEYBYTES (4*320+32) #define PQCLEAN_FIRESABER_CLEAN_CRYPTO_BYTES 32 #define PQCLEAN_FIRESABER_CLEAN_CRYPTO_CIPHERTEXTBYTES 1472 +#define PQCLEAN_FIRESABER_CLEAN_CRYPTO_PUBLICKEYBYTES 1312 +#define PQCLEAN_FIRESABER_CLEAN_CRYPTO_SECRETKEYBYTES 3040 int PQCLEAN_FIRESABER_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned char *sk); -int PQCLEAN_FIRESABER_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk); -int PQCLEAN_FIRESABER_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk); -#endif /* api_h */ +int PQCLEAN_FIRESABER_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *k, const unsigned char *pk); + +int PQCLEAN_FIRESABER_CLEAN_crypto_kem_dec(unsigned char *k, const unsigned char *ct, const unsigned char *sk); + + +#endif /* PQCLEAN_FIRESABER_CLEAN_API_H */ diff --git a/crypto_kem/firesaber/clean/cbd.c b/crypto_kem/firesaber/clean/cbd.c index cca885a1..28fbc61c 100644 --- a/crypto_kem/firesaber/clean/cbd.c +++ b/crypto_kem/firesaber/clean/cbd.c @@ -1,3 +1,7 @@ +#include "SABER_params.h" +#include "api.h" +#include "cbd.h" +#include /*--------------------------------------------------------------------- This file has been adapted from the implementation (available at, Public Domain https://github.com/pq-crystals/kyber) @@ -6,12 +10,8 @@ by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle ----------------------------------------------------------------------*/ -#include "SABER_params.h" -#include "api.h" -#include "cbd.h" -#include -static uint64_t load_littleendian(const unsigned char *x, int bytes) { +static uint64_t load_littleendian(const uint8_t *x, int bytes) { int i; uint64_t r = x[0]; for (i = 1; i < bytes; i++) { @@ -20,10 +20,7 @@ static uint64_t load_littleendian(const unsigned char *x, int bytes) { return r; } - -void PQCLEAN_FIRESABER_CLEAN_cbd(uint16_t *r, const unsigned char *buf) { - uint16_t Qmod_minus1 = SABER_Q - 1; - +void PQCLEAN_FIRESABER_CLEAN_cbd(uint16_t s[SABER_N], const uint8_t buf[SABER_POLYCOINBYTES]) { uint32_t t, d, a[4], b[4]; int i, j; @@ -34,19 +31,18 @@ void PQCLEAN_FIRESABER_CLEAN_cbd(uint16_t *r, const unsigned char *buf) { d += (t >> j) & 0x249249; } - a[0] = d & 0x7; - b[0] = (d >> 3) & 0x7; - a[1] = (d >> 6) & 0x7; - b[1] = (d >> 9) & 0x7; + a[0] = d & 0x7; + b[0] = (d >> 3) & 0x7; + a[1] = (d >> 6) & 0x7; + b[1] = (d >> 9) & 0x7; a[2] = (d >> 12) & 0x7; b[2] = (d >> 15) & 0x7; a[3] = (d >> 18) & 0x7; b[3] = (d >> 21); - r[4 * i + 0] = (uint16_t)(a[0] - b[0]) & Qmod_minus1; - r[4 * i + 1] = (uint16_t)(a[1] - b[1]) & Qmod_minus1; - r[4 * i + 2] = (uint16_t)(a[2] - b[2]) & Qmod_minus1; - r[4 * i + 3] = (uint16_t)(a[3] - b[3]) & Qmod_minus1; - + s[4 * i + 0] = (uint16_t)(a[0] - b[0]); + s[4 * i + 1] = (uint16_t)(a[1] - b[1]); + s[4 * i + 2] = (uint16_t)(a[2] - b[2]); + s[4 * i + 3] = (uint16_t)(a[3] - b[3]); } } diff --git a/crypto_kem/firesaber/clean/cbd.h b/crypto_kem/firesaber/clean/cbd.h index b10e5202..0fa18b02 100644 --- a/crypto_kem/firesaber/clean/cbd.h +++ b/crypto_kem/firesaber/clean/cbd.h @@ -1,6 +1,5 @@ #ifndef CBD_H #define CBD_H - /*--------------------------------------------------------------------- This file has been adapted from the implementation (available at, Public Domain https://github.com/pq-crystals/kyber) @@ -8,10 +7,10 @@ of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle ----------------------------------------------------------------------*/ - -#include "poly.h" +#include "SABER_params.h" #include -void PQCLEAN_FIRESABER_CLEAN_cbd(uint16_t *r, const unsigned char *buf); +void PQCLEAN_FIRESABER_CLEAN_cbd(uint16_t s[SABER_N], const uint8_t buf[SABER_POLYCOINBYTES]); + #endif diff --git a/crypto_kem/firesaber/clean/kem.c b/crypto_kem/firesaber/clean/kem.c index c66cfed9..2ffe4e75 100644 --- a/crypto_kem/firesaber/clean/kem.c +++ b/crypto_kem/firesaber/clean/kem.c @@ -1,96 +1,77 @@ #include "SABER_indcpa.h" #include "SABER_params.h" +#include "api.h" #include "fips202.h" #include "randombytes.h" #include "verify.h" +#include #include -#include -#include -int PQCLEAN_FIRESABER_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned char *sk) { - int i; - // sk[0:SABER_INDCPA_SECRETKEYBYTES-1] <-- sk - PQCLEAN_FIRESABER_CLEAN_indcpa_kem_keypair(pk, sk); +int PQCLEAN_FIRESABER_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + size_t i; - // sk[SABER_INDCPA_SECRETKEYBYTES:SABER_INDCPA_SECRETKEYBYTES+SABER_INDCPA_SECRETKEYBYTES-1] <-- pk + PQCLEAN_FIRESABER_CLEAN_indcpa_kem_keypair(pk, sk); // sk[0:SABER_INDCPA_SECRETKEYBYTES-1] <-- sk for (i = 0; i < SABER_INDCPA_PUBLICKEYBYTES; i++) { - sk[i + SABER_INDCPA_SECRETKEYBYTES] = pk[i]; + sk[i + SABER_INDCPA_SECRETKEYBYTES] = pk[i]; // sk[SABER_INDCPA_SECRETKEYBYTES:SABER_INDCPA_SECRETKEYBYTES+SABER_INDCPA_SECRETKEYBYTES-1] <-- pk } - // Then hash(pk) is appended. - sha3_256(sk + SABER_SECRETKEYBYTES - 64, pk, SABER_INDCPA_PUBLICKEYBYTES); + sha3_256(sk + SABER_SECRETKEYBYTES - 64, pk, SABER_INDCPA_PUBLICKEYBYTES); // Then hash(pk) is appended. - // Remaining part of sk contains a pseudo-random number. - // This is output when check in crypto_kem_dec() fails. - randombytes(sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES ); + randombytes(sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES); // Remaining part of sk contains a pseudo-random number. + // This is output when check in PQCLEAN_FIRESABER_CLEAN_crypto_kem_dec() fails. return (0); } -int PQCLEAN_FIRESABER_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk) { - // Will contain key, coins - unsigned char kr[64]; - unsigned char buf[64]; +int PQCLEAN_FIRESABER_CLEAN_crypto_kem_enc(uint8_t *c, uint8_t *k, const uint8_t *pk) { + + uint8_t kr[64]; // Will contain key, coins + uint8_t buf[64]; randombytes(buf, 32); - // BUF[0:31] <-- random message (will be used as the key for client) Note: hash doesnot release system RNG output - sha3_256(buf, buf, 32); + sha3_256(buf, buf, 32); // BUF[0:31] <-- random message (will be used as the key for client) Note: hash doesnot release system RNG output - // BUF[32:63] <-- Hash(public key); Multitarget countermeasure for coins + contributory KEM - sha3_256(buf + 32, pk, SABER_INDCPA_PUBLICKEYBYTES); - - // kr[0:63] <-- Hash(buf[0:63]); - sha3_512(kr, buf, 64); + sha3_256(buf + 32, pk, SABER_INDCPA_PUBLICKEYBYTES); // BUF[32:63] <-- Hash(public key); Multitarget countermeasure for coins + contributory KEM + sha3_512(kr, buf, 64); // kr[0:63] <-- Hash(buf[0:63]); // K^ <-- kr[0:31] // noiseseed (r) <-- kr[32:63]; - // buf[0:31] contains message; kr[32:63] contains randomness r; - PQCLEAN_FIRESABER_CLEAN_indcpa_kem_enc(buf, kr + 32, pk, ct); + PQCLEAN_FIRESABER_CLEAN_indcpa_kem_enc(c, buf, kr + 32, pk); // buf[0:31] contains message; kr[32:63] contains randomness r; - sha3_256(kr + 32, ct, SABER_BYTES_CCA_DEC); + sha3_256(kr + 32, c, SABER_BYTES_CCA_DEC); - // hash concatenation of pre-k and h(c) to k - sha3_256(ss, kr, 64); + sha3_256(k, kr, 64); // hash concatenation of pre-k and h(c) to k return (0); } +int PQCLEAN_FIRESABER_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, const uint8_t *sk) { + size_t i; + uint8_t fail; + uint8_t cmp[SABER_BYTES_CCA_DEC]; + uint8_t buf[64]; + uint8_t kr[64]; // Will contain key, coins + const uint8_t *pk = sk + SABER_INDCPA_SECRETKEYBYTES; -int PQCLEAN_FIRESABER_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk) { - int i; - unsigned char fail; - unsigned char cmp[SABER_BYTES_CCA_DEC]; - unsigned char buf[64]; - - // Will contain key, coins - unsigned char kr[64]; - const unsigned char *pk = sk + SABER_INDCPA_SECRETKEYBYTES; - - // buf[0:31] <-- message - PQCLEAN_FIRESABER_CLEAN_indcpa_kem_dec(sk, ct, buf); - + PQCLEAN_FIRESABER_CLEAN_indcpa_kem_dec(buf, sk, c); // buf[0:31] <-- message // Multitarget countermeasure for coins + contributory KEM - // Save hash by storing h(pk) in sk - for (i = 0; i < 32; i++) { + for (i = 0; i < 32; i++) { // Save hash by storing h(pk) in sk buf[32 + i] = sk[SABER_SECRETKEYBYTES - 64 + i]; } sha3_512(kr, buf, 64); - PQCLEAN_FIRESABER_CLEAN_indcpa_kem_enc(buf, kr + 32, pk, cmp); + PQCLEAN_FIRESABER_CLEAN_indcpa_kem_enc(cmp, buf, kr + 32, pk); + fail = PQCLEAN_FIRESABER_CLEAN_verify(c, cmp, SABER_BYTES_CCA_DEC); - fail = PQCLEAN_FIRESABER_CLEAN_verify(ct, cmp, SABER_BYTES_CCA_DEC); - - // overwrite coins in kr with h(c) - sha3_256(kr + 32, ct, SABER_BYTES_CCA_DEC); + sha3_256(kr + 32, c, SABER_BYTES_CCA_DEC); // overwrite coins in kr with h(c) PQCLEAN_FIRESABER_CLEAN_cmov(kr, sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES, fail); - // hash concatenation of pre-k and h(c) to k - sha3_256(ss, kr, 64); + sha3_256(k, kr, 64); // hash concatenation of pre-k and h(c) to k return (0); } diff --git a/crypto_kem/firesaber/clean/pack_unpack.c b/crypto_kem/firesaber/clean/pack_unpack.c index 9e68ffc1..2d1538ae 100644 --- a/crypto_kem/firesaber/clean/pack_unpack.c +++ b/crypto_kem/firesaber/clean/pack_unpack.c @@ -1,254 +1,151 @@ +#include "SABER_params.h" #include "pack_unpack.h" +#include "poly.h" +#include -void PQCLEAN_FIRESABER_CLEAN_pack_3bit(uint8_t *bytes, const uint16_t *data) { - uint32_t j; - uint32_t offset_data, offset_byte; - - for (j = 0; j < SABER_N / 8; j++) { - offset_byte = 3 * j; - offset_data = 8 * j; - bytes[offset_byte + 0] = (data[offset_data + 0] & 0x7) | - ((data[offset_data + 1] & 0x7) << 3) | - ((data[offset_data + 2] & 0x3) << 6); - bytes[offset_byte + 1] = ((data[offset_data + 2] >> 2 ) & 0x01) | - ((data[offset_data + 3] & 0x7) << 1) | - ((data[offset_data + 4] & 0x7) << 4) | - (((data[offset_data + 5]) & 0x01) << 7); - bytes[offset_byte + 2] = ((data[offset_data + 5] >> 1 ) & 0x03) | - ((data[offset_data + 6] & 0x7) << 2) | - ((data[offset_data + 7] & 0x7) << 5); - } -} - -void PQCLEAN_FIRESABER_CLEAN_un_pack3bit(const uint8_t *bytes, uint16_t *data) { - uint32_t j; - uint32_t offset_data, offset_byte; - - for (j = 0; j < SABER_N / 8; j++) { - offset_byte = 3 * j; - offset_data = 8 * j; - data[offset_data + 0] = (bytes[offset_byte + 0]) & 0x07; - data[offset_data + 1] = ((bytes[offset_byte + 0]) >> 3 ) & 0x07; - data[offset_data + 2] = (((bytes[offset_byte + 0]) >> 6 ) & 0x03) | - (((bytes[offset_byte + 1]) & 0x01) << 2); - data[offset_data + 3] = ((bytes[offset_byte + 1]) >> 1 ) & 0x07; - data[offset_data + 4] = ((bytes[offset_byte + 1]) >> 4 ) & 0x07; - data[offset_data + 5] = (((bytes[offset_byte + 1]) >> 7 ) & 0x01) | - (((bytes[offset_byte + 2]) & 0x03) << 1); - data[offset_data + 6] = ((bytes[offset_byte + 2] >> 2) & 0x07); - data[offset_data + 7] = ((bytes[offset_byte + 2] >> 5) & 0x07); - } -} - -void PQCLEAN_FIRESABER_CLEAN_pack_4bit(uint8_t *bytes, const uint16_t *data) { - uint32_t j; - uint32_t offset_data; - - for (j = 0; j < SABER_N / 2; j++) { - offset_data = 2 * j; - bytes[j] = (data[offset_data] & 0x0f) | - ((data[offset_data + 1] & 0x0f) << 4); - } -} - -void PQCLEAN_FIRESABER_CLEAN_un_pack4bit(const unsigned char *bytes, uint16_t *ar) { - uint32_t j; - uint32_t offset_data; - - for (j = 0; j < SABER_N / 2; j++) { - offset_data = 2 * j; - ar[offset_data] = bytes[j] & 0x0f; - ar[offset_data + 1] = (bytes[j] >> 4) & 0x0f; - } -} - -void PQCLEAN_FIRESABER_CLEAN_pack_6bit(uint8_t *bytes, const uint16_t *data) { - uint32_t j; - uint32_t offset_data, offset_byte; - +void PQCLEAN_FIRESABER_CLEAN_POLT2BS(uint8_t bytes[SABER_SCALEBYTES_KEM], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; for (j = 0; j < SABER_N / 4; j++) { - offset_byte = 3 * j; - offset_data = 4 * j; - bytes[offset_byte + 0] = (data[offset_data + 0] & 0x3f) | - ((data[offset_data + 1] & 0x03) << 6); - bytes[offset_byte + 1] = ((data[offset_data + 1] >> 2) & 0x0f) | - ((data[offset_data + 2] & 0x0f) << 4); - bytes[offset_byte + 2] = ((data[offset_data + 2] >> 4) & 0x03) | - ((data[offset_data + 3] & 0x3f) << 2); + out[0] = (uint8_t) ((in[0] & 0x3f) | (in[1] << 6)); + out[1] = (uint8_t) (((in[1] >> 2) & 0x0f) | (in[2] << 4)); + out[2] = (uint8_t) (((in[2] >> 4) & 0x03) | (in[3] << 2)); + in += 4; + out += 3; } } - -void PQCLEAN_FIRESABER_CLEAN_un_pack6bit(const unsigned char *bytes, uint16_t *data) { - uint32_t j; - uint32_t offset_data, offset_byte; - +void PQCLEAN_FIRESABER_CLEAN_BS2POLT(poly *data, const uint8_t bytes[SABER_SCALEBYTES_KEM]) { + /* This function does not reduce its output mod T */ + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; for (j = 0; j < SABER_N / 4; j++) { - offset_byte = 3 * j; - offset_data = 4 * j; - data[offset_data + 0] = bytes[offset_byte + 0] & 0x3f; - data[offset_data + 1] = ((bytes[offset_byte + 0] >> 6) & 0x03) | - ((bytes[offset_byte + 1] & 0x0f) << 2); - data[offset_data + 2] = ((bytes[offset_byte + 1] & 0xff) >> 4) | - ((bytes[offset_byte + 2] & 0x03) << 4); - data[offset_data + 3] = ((bytes[offset_byte + 2] & 0xff) >> 2); + out[0] = in[0]; + out[1] = (in[0] >> 6) | (in[1] << 2); + out[2] = (in[1] >> 4) | (in[2] << 4); + out[3] = (in[2] >> 2); + in += 3; + out += 4; } } - -static void POLVECp2BS(uint8_t *bytes, uint16_t data[SABER_K][SABER_N]) { - uint32_t i, j; - uint32_t offset_data, offset_byte, offset_byte1; - - for (i = 0; i < SABER_K; i++) { - offset_byte1 = i * (SABER_N * 10) / 8; - for (j = 0; j < SABER_N / 4; j++) { - offset_byte = offset_byte1 + 5 * j; - offset_data = 4 * j; - bytes[offset_byte + 0] = (data[i][offset_data + 0] & (0xff)); - bytes[offset_byte + 1] = ((data[i][offset_data + 0] >> 8) & 0x03) | - ((data[i][offset_data + 1] & 0x3f) << 2); - bytes[offset_byte + 2] = ((data[i][offset_data + 1] >> 6) & 0x0f) | - ((data[i][offset_data + 2] & 0x0f) << 4); - bytes[offset_byte + 3] = ((data[i][offset_data + 2] >> 4) & 0x3f) | - ((data[i][offset_data + 3] & 0x03) << 6); - bytes[offset_byte + 4] = ((data[i][offset_data + 3] >> 2) & 0xff); - } - } -} - -static void BS2POLVECp(const unsigned char *bytes, uint16_t data[SABER_K][SABER_N]) { - uint32_t i, j; - uint32_t offset_data, offset_byte, offset_byte1; - - for (i = 0; i < SABER_K; i++) { - offset_byte1 = i * (SABER_N * 10) / 8; - for (j = 0; j < SABER_N / 4; j++) { - offset_byte = offset_byte1 + 5 * j; - offset_data = 4 * j; - data[i][offset_data + 0] = (bytes[offset_byte + 0] & (0xff)) | - ((bytes[offset_byte + 1] & 0x03) << 8); - data[i][offset_data + 1] = ((bytes[offset_byte + 1] >> 2) & (0x3f)) | - ((bytes[offset_byte + 2] & 0x0f) << 6); - data[i][offset_data + 2] = ((bytes[offset_byte + 2] >> 4) & (0x0f)) | - ((bytes[offset_byte + 3] & 0x3f) << 4); - data[i][offset_data + 3] = ((bytes[offset_byte + 3] >> 6) & (0x03)) | - ((bytes[offset_byte + 4] & 0xff) << 2); - } - } -} - - - -static void POLVECq2BS(uint8_t *bytes, uint16_t data[SABER_K][SABER_N]) { - uint32_t i, j; - uint32_t offset_data, offset_byte, offset_byte1; - - for (i = 0; i < SABER_K; i++) { - offset_byte1 = i * (SABER_N * 13) / 8; - for (j = 0; j < SABER_N / 8; j++) { - offset_byte = offset_byte1 + 13 * j; - offset_data = 8 * j; - bytes[offset_byte + 0] = (data[i][offset_data + 0] & (0xff)); - bytes[offset_byte + 1] = ((data[i][offset_data + 0] >> 8) & 0x1f) | - ((data[i][offset_data + 1] & 0x07) << 5); - bytes[offset_byte + 2] = ((data[i][offset_data + 1] >> 3) & 0xff); - bytes[offset_byte + 3] = ((data[i][offset_data + 1] >> 11) & 0x03) | - ((data[i][offset_data + 2] & 0x3f) << 2); - bytes[offset_byte + 4] = ((data[i][offset_data + 2] >> 6) & 0x7f) | - ((data[i][offset_data + 3] & 0x01) << 7); - bytes[offset_byte + 5] = ((data[i][offset_data + 3] >> 1) & 0xff); - bytes[offset_byte + 6] = ((data[i][offset_data + 3] >> 9) & 0x0f) | - ((data[i][offset_data + 4] & 0x0f) << 4); - bytes[offset_byte + 7] = ((data[i][offset_data + 4] >> 4) & 0xff); - bytes[offset_byte + 8] = ((data[i][offset_data + 4] >> 12) & 0x01) | - ((data[i][offset_data + 5] & 0x7f) << 1); - bytes[offset_byte + 9] = ((data[i][offset_data + 5] >> 7) & 0x3f) | - ((data[i][offset_data + 6] & 0x03) << 6); - bytes[offset_byte + 10] = ((data[i][offset_data + 6] >> 2) & 0xff); - bytes[offset_byte + 11] = ((data[i][offset_data + 6] >> 10) & 0x07) | - ((data[i][offset_data + 7] & 0x1f) << 3); - bytes[offset_byte + 12] = ((data[i][offset_data + 7] >> 5) & 0xff); - } - } -} - -static void BS2POLVECq(const unsigned char *bytes, uint16_t data[SABER_K][SABER_N]) { - uint32_t i, j; - uint32_t offset_data, offset_byte, offset_byte1; - - for (i = 0; i < SABER_K; i++) { - offset_byte1 = i * (SABER_N * 13) / 8; - for (j = 0; j < SABER_N / 8; j++) { - offset_byte = offset_byte1 + 13 * j; - offset_data = 8 * j; - data[i][offset_data + 0] = (bytes[offset_byte + 0] & (0xff)) | - ((bytes[offset_byte + 1] & 0x1f) << 8); - data[i][offset_data + 1] = (bytes[offset_byte + 1] >> 5 & (0x07)) | - ((bytes[offset_byte + 2] & 0xff) << 3) | - ((bytes[offset_byte + 3] & 0x03) << 11); - data[i][offset_data + 2] = (bytes[offset_byte + 3] >> 2 & (0x3f)) | - ((bytes[offset_byte + 4] & 0x7f) << 6); - data[i][offset_data + 3] = (bytes[offset_byte + 4] >> 7 & (0x01)) | - ((bytes[offset_byte + 5] & 0xff) << 1) | - ((bytes[offset_byte + 6] & 0x0f) << 9); - data[i][offset_data + 4] = (bytes[offset_byte + 6] >> 4 & (0x0f)) | - ((bytes[offset_byte + 7] & 0xff) << 4) | - ((bytes[offset_byte + 8] & 0x01) << 12); - data[i][offset_data + 5] = (bytes[offset_byte + 8] >> 1 & (0x7f)) | - ((bytes[offset_byte + 9] & 0x3f) << 7); - data[i][offset_data + 6] = (bytes[offset_byte + 9] >> 6 & (0x03)) | - ((bytes[offset_byte + 10] & 0xff) << 2) | - ((bytes[offset_byte + 11] & 0x07) << 10); - data[i][offset_data + 7] = (bytes[offset_byte + 11] >> 3 & (0x1f)) | - ((bytes[offset_byte + 12] & 0xff) << 5); - } - } -} - -//only BS2POLq no BS2POLp -void PQCLEAN_FIRESABER_CLEAN_BS2POL(const unsigned char *bytes, uint16_t data[SABER_N]) { - uint32_t j; - uint32_t offset_data, offset_byte; - +static void POLq2BS(uint8_t bytes[SABER_POLYBYTES], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; for (j = 0; j < SABER_N / 8; j++) { - offset_byte = 13 * j; - offset_data = 8 * j; - data[offset_data + 0] = (bytes[offset_byte + 0] & (0xff)) | - ((bytes[offset_byte + 1] & 0x1f) << 8); - data[offset_data + 1] = (bytes[offset_byte + 1] >> 5 & (0x07)) | - ((bytes[offset_byte + 2] & 0xff) << 3) | - ((bytes[offset_byte + 3] & 0x03) << 11); - data[offset_data + 2] = (bytes[offset_byte + 3] >> 2 & (0x3f)) | - ((bytes[offset_byte + 4] & 0x7f) << 6); - data[offset_data + 3] = (bytes[offset_byte + 4] >> 7 & (0x01)) | - ((bytes[offset_byte + 5] & 0xff) << 1) | - ((bytes[offset_byte + 6] & 0x0f) << 9); - data[offset_data + 4] = (bytes[offset_byte + 6] >> 4 & (0x0f)) | - ((bytes[offset_byte + 7] & 0xff) << 4) | - ((bytes[offset_byte + 8] & 0x01) << 12); - data[offset_data + 5] = (bytes[offset_byte + 8] >> 1 & (0x7f)) | - ((bytes[offset_byte + 9] & 0x3f) << 7); - data[offset_data + 6] = (bytes[offset_byte + 9] >> 6 & (0x03)) | - ((bytes[offset_byte + 10] & 0xff) << 2) | - ((bytes[offset_byte + 11] & 0x07) << 10); - data[offset_data + 7] = (bytes[offset_byte + 11] >> 3 & (0x1f)) | - ((bytes[offset_byte + 12] & 0xff) << 5); + out[0] = (uint8_t) (in[0]); + out[1] = (uint8_t) (((in[0] >> 8) & 0x1f) | (in[1] << 5)); + out[2] = (uint8_t) (in[1] >> 3); + out[3] = (uint8_t) (((in[1] >> 11) & 0x03) | (in[2] << 2)); + out[4] = (uint8_t) (((in[2] >> 6) & 0x7f) | (in[3] << 7)); + out[5] = (uint8_t) (in[3] >> 1); + out[6] = (uint8_t) (((in[3] >> 9) & 0x0f) | (in[4] << 4)); + out[7] = (uint8_t) (in[4] >> 4); + out[8] = (uint8_t) (((in[4] >> 12) & 0x01) | (in[5] << 1)); + out[9] = (uint8_t) (((in[5] >> 7) & 0x3f) | (in[6] << 6)); + out[10] = (uint8_t) (in[6] >> 2); + out[11] = (uint8_t) (((in[6] >> 10) & 0x07) | (in[7] << 3)); + out[12] = (uint8_t) (in[7] >> 5); + in += 8; + out += 13; } } -void PQCLEAN_FIRESABER_CLEAN_POLVEC2BS(uint8_t *bytes, uint16_t data[SABER_K][SABER_N], uint16_t modulus) { - if (modulus == 1024) { - POLVECp2BS(bytes, data); - } else if (modulus == 8192) { - POLVECq2BS(bytes, data); +static void BS2POLq(poly *data, const uint8_t bytes[SABER_POLYBYTES]) { + /* This function does not reduce its output mod Q */ + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; + for (j = 0; j < SABER_N / 8; j++) { + out[0] = (in[0]) | (in[1] << 8); + out[1] = (in[1] >> 5) | (in[2] << 3) | (in[3] << 11); + out[2] = (in[3] >> 2) | (in[4] << 6); + out[3] = (in[4] >> 7) | (in[5] << 1) | (in[6] << 9); + out[4] = (in[6] >> 4) | (in[7] << 4) | (in[8] << 12); + out[5] = (in[8] >> 1) | (in[9] << 7); + out[6] = (in[9] >> 6) | (in[10] << 2) | (in[11] << 10); + out[7] = (in[11] >> 3) | (in[12] << 5); + in += 13; + out += 8; } } -void PQCLEAN_FIRESABER_CLEAN_BS2POLVEC(const unsigned char *bytes, uint16_t data[SABER_K][SABER_N], uint16_t modulus) { - if (modulus == 1024) { - BS2POLVECp(bytes, data); - } else if (modulus == 8192) { - BS2POLVECq(bytes, data); +static void POLp2BS(uint8_t bytes[SABER_POLYCOMPRESSEDBYTES], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; + for (j = 0; j < SABER_N / 4; j++) { + out[0] = (uint8_t) (in[0]); + out[1] = (uint8_t) (((in[0] >> 8) & 0x03) | (in[1] << 2)); + out[2] = (uint8_t) (((in[1] >> 6) & 0x0f) | (in[2] << 4)); + out[3] = (uint8_t) (((in[2] >> 4) & 0x3f) | (in[3] << 6)); + out[4] = (uint8_t) (in[3] >> 2); + in += 4; + out += 5; + } +} + +static void BS2POLp(poly *data, const uint8_t bytes[SABER_POLYCOMPRESSEDBYTES]) { + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; + for (j = 0; j < SABER_N / 4; j++) { + out[0] = in[0] | (in[1] << 8); + out[1] = (in[1] >> 2) | (in[2] << 6); + out[2] = (in[2] >> 4) | (in[3] << 4); + out[3] = (in[3] >> 6) | (in[4] << 2); + in += 5; + out += 4; + } +} + +void PQCLEAN_FIRESABER_CLEAN_POLVECq2BS(uint8_t bytes[SABER_POLYVECBYTES], const poly data[SABER_L]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + POLq2BS(bytes + i * SABER_POLYBYTES, &data[i]); + } +} + +void PQCLEAN_FIRESABER_CLEAN_BS2POLVECq(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECBYTES]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + BS2POLq(&data[i], bytes + i * SABER_POLYBYTES); + } +} + +void PQCLEAN_FIRESABER_CLEAN_POLVECp2BS(uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES], const poly data[SABER_L]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + POLp2BS(bytes + i * SABER_POLYCOMPRESSEDBYTES, &data[i]); + } +} + +void PQCLEAN_FIRESABER_CLEAN_BS2POLVECp(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + BS2POLp(&data[i], bytes + i * SABER_POLYCOMPRESSEDBYTES); + } +} + +void PQCLEAN_FIRESABER_CLEAN_BS2POLmsg(poly *data, const uint8_t bytes[SABER_KEYBYTES]) { + size_t i, j; + for (j = 0; j < SABER_KEYBYTES; j++) { + for (i = 0; i < 8; i++) { + data->coeffs[j * 8 + i] = ((bytes[j] >> i) & 0x01); + } + } +} + +void PQCLEAN_FIRESABER_CLEAN_POLmsg2BS(uint8_t bytes[SABER_KEYBYTES], const poly *data) { + size_t i, j; + memset(bytes, 0, SABER_KEYBYTES); + + for (j = 0; j < SABER_KEYBYTES; j++) { + for (i = 0; i < 8; i++) { + bytes[j] = bytes[j] | ((data->coeffs[j * 8 + i] & 0x01) << i); + } } } diff --git a/crypto_kem/firesaber/clean/pack_unpack.h b/crypto_kem/firesaber/clean/pack_unpack.h index 6509f107..698cecb1 100644 --- a/crypto_kem/firesaber/clean/pack_unpack.h +++ b/crypto_kem/firesaber/clean/pack_unpack.h @@ -1,28 +1,28 @@ #ifndef PACK_UNPACK_H #define PACK_UNPACK_H - #include "SABER_params.h" +#include "poly.h" #include #include +void PQCLEAN_FIRESABER_CLEAN_POLT2BS(uint8_t bytes[SABER_SCALEBYTES_KEM], const poly *data); -void PQCLEAN_FIRESABER_CLEAN_pack_3bit(uint8_t *bytes, const uint16_t *data); - -void PQCLEAN_FIRESABER_CLEAN_un_pack3bit(const uint8_t *bytes, uint16_t *data); - -void PQCLEAN_FIRESABER_CLEAN_pack_4bit(uint8_t *bytes, const uint16_t *data); - -void PQCLEAN_FIRESABER_CLEAN_un_pack4bit(const unsigned char *bytes, uint16_t *ar); - -void PQCLEAN_FIRESABER_CLEAN_pack_6bit(uint8_t *bytes, const uint16_t *data); - -void PQCLEAN_FIRESABER_CLEAN_un_pack6bit(const unsigned char *bytes, uint16_t *data); +void PQCLEAN_FIRESABER_CLEAN_BS2POLT(poly *data, const uint8_t bytes[SABER_SCALEBYTES_KEM]); -void PQCLEAN_FIRESABER_CLEAN_BS2POL(const unsigned char *bytes, uint16_t data[SABER_N]); +void PQCLEAN_FIRESABER_CLEAN_POLVECq2BS(uint8_t bytes[SABER_POLYVECBYTES], const poly data[SABER_L]); -void PQCLEAN_FIRESABER_CLEAN_POLVEC2BS(uint8_t *bytes, uint16_t data[SABER_K][SABER_N], uint16_t modulus); +void PQCLEAN_FIRESABER_CLEAN_POLVECp2BS(uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES], const poly data[SABER_L]); + + +void PQCLEAN_FIRESABER_CLEAN_BS2POLVECq(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECBYTES]); + +void PQCLEAN_FIRESABER_CLEAN_BS2POLVECp(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES]); + + +void PQCLEAN_FIRESABER_CLEAN_BS2POLmsg(poly *data, const uint8_t bytes[SABER_KEYBYTES]); + +void PQCLEAN_FIRESABER_CLEAN_POLmsg2BS(uint8_t bytes[SABER_KEYBYTES], const poly *data); -void PQCLEAN_FIRESABER_CLEAN_BS2POLVEC(const unsigned char *bytes, uint16_t data[SABER_K][SABER_N], uint16_t modulus); #endif diff --git a/crypto_kem/firesaber/clean/poly.c b/crypto_kem/firesaber/clean/poly.c index 6fef45d5..2ce0e871 100644 --- a/crypto_kem/firesaber/clean/poly.c +++ b/crypto_kem/firesaber/clean/poly.c @@ -1,21 +1,57 @@ -/*--------------------------------------------------------------------- -This file has been adapted from the implementation -(available at, Public Domain https://github.com/pq-crystals/kyber) -of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" -by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, -Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle -----------------------------------------------------------------------*/ -#include "SABER_params.h" +#include "api.h" #include "cbd.h" #include "fips202.h" +#include "pack_unpack.h" #include "poly.h" +#include -void PQCLEAN_FIRESABER_CLEAN_GenSecret(uint16_t r[SABER_K][SABER_N], const unsigned char *seed) { - uint8_t buf[SABER_MU * SABER_N * SABER_K / 8]; +void PQCLEAN_FIRESABER_CLEAN_MatrixVectorMul(poly c[SABER_L], const poly A[SABER_L][SABER_L], const poly s[SABER_L], int16_t transpose) { + size_t i, j; + + if (transpose) { + for (i = 0; i < SABER_L; i++) { + PQCLEAN_FIRESABER_CLEAN_poly_mul(&c[i], &A[0][i], &s[0], 0); + for (j = 1; j < SABER_L; j++) { + PQCLEAN_FIRESABER_CLEAN_poly_mul(&c[i], &A[j][i], &s[j], 1); + } + } + } else { + for (i = 0; i < SABER_L; i++) { + PQCLEAN_FIRESABER_CLEAN_poly_mul(&c[i], &A[i][0], &s[0], 0); + for (j = 1; j < SABER_L; j++) { + PQCLEAN_FIRESABER_CLEAN_poly_mul(&c[i], &A[i][j], &s[j], 1); + } + } + } +} + +void PQCLEAN_FIRESABER_CLEAN_InnerProd(poly *c, const poly b[SABER_L], const poly s[SABER_L]) { + size_t i; + + PQCLEAN_FIRESABER_CLEAN_poly_mul(c, &b[0], &s[0], 0); + for (i = 1; i < SABER_L; i++) { + PQCLEAN_FIRESABER_CLEAN_poly_mul(c, &b[i], &s[i], 1); + } +} + +void PQCLEAN_FIRESABER_CLEAN_GenMatrix(poly A[SABER_L][SABER_L], const uint8_t seed[SABER_SEEDBYTES]) { + size_t i; + uint8_t buf[SABER_L * SABER_POLYVECBYTES]; + + shake128(buf, sizeof(buf), seed, SABER_SEEDBYTES); + + for (i = 0; i < SABER_L; i++) { + PQCLEAN_FIRESABER_CLEAN_BS2POLVECq(A[i], buf + i * SABER_POLYVECBYTES); + } +} + +void PQCLEAN_FIRESABER_CLEAN_GenSecret(poly s[SABER_L], const uint8_t seed[SABER_NOISESEEDBYTES]) { + size_t i; + uint8_t buf[SABER_L * SABER_POLYCOINBYTES]; shake128(buf, sizeof(buf), seed, SABER_NOISESEEDBYTES); - for (size_t i = 0; i < SABER_K; i++) { - PQCLEAN_FIRESABER_CLEAN_cbd(r[i], buf + i * SABER_MU * SABER_N / 8); + for (i = 0; i < SABER_L; i++) { + PQCLEAN_FIRESABER_CLEAN_cbd(s[i].coeffs, buf + i * SABER_POLYCOINBYTES); } } diff --git a/crypto_kem/firesaber/clean/poly.h b/crypto_kem/firesaber/clean/poly.h index 4f69a068..fdbbfa1f 100644 --- a/crypto_kem/firesaber/clean/poly.h +++ b/crypto_kem/firesaber/clean/poly.h @@ -1,26 +1,23 @@ #ifndef POLY_H #define POLY_H - -/*--------------------------------------------------------------------- -This file has been adapted from the implementation -(available at, Public Domain https://github.com/pq-crystals/kyber) -of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" -by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, -Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle -----------------------------------------------------------------------*/ - - #include "SABER_params.h" #include -typedef struct { +typedef union { uint16_t coeffs[SABER_N]; } poly; -typedef struct { - poly vec[SABER_K]; -} polyvec; -void PQCLEAN_FIRESABER_CLEAN_GenSecret(uint16_t r[SABER_K][SABER_N], const unsigned char *seed); +void PQCLEAN_FIRESABER_CLEAN_MatrixVectorMul(poly c[SABER_L], const poly A[SABER_L][SABER_L], const poly s[SABER_L], int16_t transpose); + +void PQCLEAN_FIRESABER_CLEAN_InnerProd(poly *c, const poly b[SABER_L], const poly s[SABER_L]); + +void PQCLEAN_FIRESABER_CLEAN_GenMatrix(poly A[SABER_L][SABER_L], const uint8_t seed[SABER_SEEDBYTES]); + +void PQCLEAN_FIRESABER_CLEAN_GenSecret(poly s[SABER_L], const uint8_t seed[SABER_NOISESEEDBYTES]); + + +void PQCLEAN_FIRESABER_CLEAN_poly_mul(poly *c, const poly *a, const poly *b, int accumulate); + #endif diff --git a/crypto_kem/firesaber/clean/poly_mul.c b/crypto_kem/firesaber/clean/poly_mul.c index 20f1d4ad..b57e04fb 100644 --- a/crypto_kem/firesaber/clean/poly_mul.c +++ b/crypto_kem/firesaber/clean/poly_mul.c @@ -1,4 +1,4 @@ -#include "poly_mul.h" +#include "poly.h" #include #include @@ -11,13 +11,13 @@ #define OVERFLOWING_MUL(X, Y) ((uint16_t)((uint32_t)(X) * (uint32_t)(Y))) #define KARATSUBA_N 64 -static void karatsuba_simple(const uint16_t *a_1, const uint16_t *b_1, uint16_t *result_final) { +static void karatsuba_simple(uint16_t *result_final, const uint16_t *a_1, const uint16_t *b_1) { uint16_t d01[KARATSUBA_N / 2 - 1]; uint16_t d0123[KARATSUBA_N / 2 - 1]; uint16_t d23[KARATSUBA_N / 2 - 1]; uint16_t result_d01[KARATSUBA_N - 1]; - int32_t i, j; + size_t i, j; memset(result_d01, 0, (KARATSUBA_N - 1)*sizeof(uint16_t)); memset(d01, 0, (KARATSUBA_N / 2 - 1)*sizeof(uint16_t)); @@ -110,7 +110,7 @@ static void karatsuba_simple(const uint16_t *a_1, const uint16_t *b_1, uint16_t -static void toom_cook_4way (const uint16_t *a1, const uint16_t *b1, uint16_t *result) { +static void toom_cook_4way (uint16_t *result, const uint16_t *a1, const uint16_t *b1) { uint16_t inv3 = 43691, inv9 = 36409, inv15 = 61167; uint16_t aw1[N_SB], aw2[N_SB], aw3[N_SB], aw4[N_SB], aw5[N_SB], aw6[N_SB], aw7[N_SB]; @@ -181,13 +181,13 @@ static void toom_cook_4way (const uint16_t *a1, const uint16_t *b1, uint16_t *re // MULTIPLICATION - karatsuba_simple(aw1, bw1, w1); - karatsuba_simple(aw2, bw2, w2); - karatsuba_simple(aw3, bw3, w3); - karatsuba_simple(aw4, bw4, w4); - karatsuba_simple(aw5, bw5, w5); - karatsuba_simple(aw6, bw6, w6); - karatsuba_simple(aw7, bw7, w7); + karatsuba_simple(w1, aw1, bw1); + karatsuba_simple(w2, aw2, bw2); + karatsuba_simple(w3, aw3, bw3); + karatsuba_simple(w4, aw4, bw4); + karatsuba_simple(w5, aw5, bw5); + karatsuba_simple(w6, aw6, bw6); + karatsuba_simple(w7, aw7, bw7); // INTERPOLATION for (i = 0; i < N_SB_RES; ++i) { @@ -228,19 +228,21 @@ static void toom_cook_4way (const uint16_t *a1, const uint16_t *b1, uint16_t *re } } -void PQCLEAN_FIRESABER_CLEAN_pol_mul(uint16_t *a, uint16_t *b, uint16_t *res, uint16_t p, uint32_t n) { - uint32_t i; - // normal multiplication - uint16_t c[512]; +/* res += a*b */ +void PQCLEAN_FIRESABER_CLEAN_poly_mul(poly *c, const poly *a, const poly *b, const int accumulate) { + uint16_t C[2 * SABER_N] = {0}; + size_t i; - for (i = 0; i < 512; i++) { - c[i] = 0; - } + toom_cook_4way(C, a->coeffs, b->coeffs); - toom_cook_4way(a, b, c); - - // reduction - for (i = n; i < 2 * n; i++) { - res[i - n] = (c[i - n] - c[i]) & (p - 1); + /* reduction */ + if (accumulate == 0) { + for (i = SABER_N; i < 2 * SABER_N; i++) { + c->coeffs[i - SABER_N] = (C[i - SABER_N] - C[i]); + } + } else { + for (i = SABER_N; i < 2 * SABER_N; i++) { + c->coeffs[i - SABER_N] += (C[i - SABER_N] - C[i]); + } } } diff --git a/crypto_kem/firesaber/clean/poly_mul.h b/crypto_kem/firesaber/clean/poly_mul.h index 4d960042..b28b04f6 100644 --- a/crypto_kem/firesaber/clean/poly_mul.h +++ b/crypto_kem/firesaber/clean/poly_mul.h @@ -1,9 +1,3 @@ -#ifndef POLYMUL_H -#define POLYMUL_H -#include "SABER_params.h" -#include -void PQCLEAN_FIRESABER_CLEAN_pol_mul(uint16_t *a, uint16_t *b, uint16_t *res, uint16_t p, uint32_t n); -#endif diff --git a/crypto_kem/firesaber/clean/verify.c b/crypto_kem/firesaber/clean/verify.c index 3c571e92..97a302a9 100644 --- a/crypto_kem/firesaber/clean/verify.c +++ b/crypto_kem/firesaber/clean/verify.c @@ -1,3 +1,5 @@ +#include "verify.h" + /*------------------------------------------------- This file has been adapted from the implementation (available at https://github.com/pq-crystals/kyber) of @@ -5,26 +7,25 @@ This file has been adapted from the implementation by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle ----------------------------------------------------*/ -#include "verify.h" -#include + /* returns 0 for equal strings, 1 for non-equal strings */ -unsigned char PQCLEAN_FIRESABER_CLEAN_verify(const unsigned char *a, const unsigned char *b, size_t len) { +uint8_t PQCLEAN_FIRESABER_CLEAN_verify(const uint8_t *a, const uint8_t *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; + return (uint8_t) r; } /* b = 1 means mov, b = 0 means don't mov*/ -void PQCLEAN_FIRESABER_CLEAN_cmov(unsigned char *r, const unsigned char *x, size_t len, unsigned char b) { +void PQCLEAN_FIRESABER_CLEAN_cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b) { size_t i; b = -b; diff --git a/crypto_kem/firesaber/clean/verify.h b/crypto_kem/firesaber/clean/verify.h index 1b69b071..1d5e4cb9 100644 --- a/crypto_kem/firesaber/clean/verify.h +++ b/crypto_kem/firesaber/clean/verify.h @@ -1,6 +1,5 @@ #ifndef VERIFY_H #define VERIFY_H - /*------------------------------------------------- This file has been adapted from the implementation (available at https://github.com/pq-crystals/kyber) of @@ -13,9 +12,11 @@ Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle #include /* returns 0 for equal strings, 1 for non-equal strings */ -unsigned char PQCLEAN_FIRESABER_CLEAN_verify(const unsigned char *a, const unsigned char *b, size_t len); +uint8_t PQCLEAN_FIRESABER_CLEAN_verify(const uint8_t *a, const uint8_t *b, size_t len); + /* b = 1 means mov, b = 0 means don't mov*/ -void PQCLEAN_FIRESABER_CLEAN_cmov(unsigned char *r, const unsigned char *x, size_t len, unsigned char b); +void PQCLEAN_FIRESABER_CLEAN_cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b); + #endif diff --git a/crypto_kem/lightsaber/META.yml b/crypto_kem/lightsaber/META.yml index 1b7912f6..ec0f7517 100644 --- a/crypto_kem/lightsaber/META.yml +++ b/crypto_kem/lightsaber/META.yml @@ -14,4 +14,13 @@ principal-submitters: - Frederik Vercauteren implementations: - name: clean - version: https://github.com/KULeuven-COSIC/SABER/commit/14ede83f1ff3bcc41f0464543542366c68b55871 + version: https://github.com/KULeuven-COSIC/SABER/tree/509cc5ec3a7e12a751ccdd2ef5bd6e54e00bd350 via https://github.com/jschanck/package-pqclean/tree/1ae84c3c/saber + - name: avx2 + version: https://github.com/KULeuven-COSIC/SABER/tree/509cc5ec3a7e12a751ccdd2ef5bd6e54e00bd350 via https://github.com/jschanck/package-pqclean/tree/1ae84c3c/saber + supported_platforms: + - architecture: x86_64 + operating_systems: + - Linux + - Darwin + required_flags: + - avx2 diff --git a/crypto_kem/lightsaber/avx2/LICENSE b/crypto_kem/lightsaber/avx2/LICENSE new file mode 100644 index 00000000..d5d21fff --- /dev/null +++ b/crypto_kem/lightsaber/avx2/LICENSE @@ -0,0 +1 @@ +Public Domain diff --git a/crypto_kem/lightsaber/avx2/Makefile b/crypto_kem/lightsaber/avx2/Makefile new file mode 100644 index 00000000..ff4f4367 --- /dev/null +++ b/crypto_kem/lightsaber/avx2/Makefile @@ -0,0 +1,22 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=liblightsaber_avx2.a +HEADERS=api.h cbd.h pack_unpack.h poly.h SABER_indcpa.h SABER_params.h verify.h +OBJECTS=cbd.o kem.o pack_unpack.o poly.o poly_mul.o SABER_indcpa.o verify.o + +CFLAGS=-O3 -mavx2 -Wall -Wextra -Wpedantic -Wvla -Werror -Wredundant-decls -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.s $(HEADERS) + $(AS) -o $@ $< + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_kem/lightsaber/avx2/SABER_indcpa.c b/crypto_kem/lightsaber/avx2/SABER_indcpa.c new file mode 100644 index 00000000..50f57221 --- /dev/null +++ b/crypto_kem/lightsaber/avx2/SABER_indcpa.c @@ -0,0 +1,125 @@ +#include "SABER_indcpa.h" +#include "SABER_params.h" +#include "fips202.h" +#include "pack_unpack.h" +#include "poly.h" +#include "randombytes.h" +#include +#include + +#define h1 (1 << (SABER_EQ - SABER_EP - 1)) +#define h2 ((1 << (SABER_EP - 2)) - (1 << (SABER_EP - SABER_ET - 1)) + (1 << (SABER_EQ - SABER_EP - 1))) + +void PQCLEAN_LIGHTSABER_AVX2_indcpa_kem_keypair(uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES], uint8_t sk[SABER_INDCPA_SECRETKEYBYTES]) { + size_t i, j; + + poly A[SABER_L][SABER_L]; + poly *skpv1 = A[0]; // use first row of A to hold sk temporarily + toom4_points skpv1_eval[SABER_L]; + poly res[SABER_L]; + + uint8_t rand[SABER_NOISESEEDBYTES]; + uint8_t *seed_A = pk + SABER_POLYVECCOMPRESSEDBYTES; + + randombytes(seed_A, SABER_SEEDBYTES); + shake128(seed_A, SABER_SEEDBYTES, seed_A, SABER_SEEDBYTES); // for not revealing system RNG state + + randombytes(rand, SABER_NOISESEEDBYTES); + PQCLEAN_LIGHTSABER_AVX2_GenSecret(skpv1, rand); + PQCLEAN_LIGHTSABER_AVX2_POLVECq2BS(sk, skpv1); // pack secret key + + for (j = 0; j < SABER_L; j++) { + PQCLEAN_LIGHTSABER_AVX2_toom4_eval(&skpv1_eval[j], &skpv1[j]); + } + + PQCLEAN_LIGHTSABER_AVX2_GenMatrix(A, seed_A); // sample matrix A + PQCLEAN_LIGHTSABER_AVX2_MatrixVectorMul(res, (const poly (*)[SABER_L])A, (const toom4_points *)skpv1_eval, 1); // Matrix in transposed order + + // rounding + for (i = 0; i < SABER_L; i++) { + for (j = 0; j < SABER_N; j++) { + res[i].coeffs[j] += h1; + res[i].coeffs[j] >>= SABER_EQ - SABER_EP; + res[i].coeffs[j] &= SABER_Q - 1; + } + } + + PQCLEAN_LIGHTSABER_AVX2_POLVECp2BS(pk, res); // pack public key +} + + +void PQCLEAN_LIGHTSABER_AVX2_indcpa_kem_enc(uint8_t ciphertext[SABER_BYTES_CCA_DEC], const uint8_t m[SABER_KEYBYTES], const uint8_t noiseseed[SABER_NOISESEEDBYTES], const uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES]) { + size_t i, j; + + poly A[SABER_L][SABER_L]; + poly res[SABER_L]; + toom4_points skpv1_eval[SABER_L]; + + poly *temp = A[0]; // re-use stack space + poly *vprime = &A[0][0]; + poly *message = &A[0][1]; + + const uint8_t *seed_A = pk + SABER_POLYVECCOMPRESSEDBYTES; + uint8_t *msk_c = ciphertext + SABER_POLYVECCOMPRESSEDBYTES; + + PQCLEAN_LIGHTSABER_AVX2_GenSecret(temp, noiseseed); + for (j = 0; j < SABER_L; j++) { + PQCLEAN_LIGHTSABER_AVX2_toom4_eval(&skpv1_eval[j], &temp[j]); + } + + PQCLEAN_LIGHTSABER_AVX2_GenMatrix(A, seed_A); + PQCLEAN_LIGHTSABER_AVX2_MatrixVectorMul(res, (const poly (*)[SABER_L])A, (const toom4_points *)skpv1_eval, 0); // 0 => not transposed + + // rounding + for (i = 0; i < SABER_L; i++) { //shift right EQ-EP bits + for (j = 0; j < SABER_N; j++) { + res[i].coeffs[j] += h1; + res[i].coeffs[j] >>= SABER_EQ - SABER_EP; + res[i].coeffs[j] &= SABER_Q - 1; + } + } + PQCLEAN_LIGHTSABER_AVX2_POLVECp2BS(ciphertext, res); + + // vector-vector scalar multiplication with mod p + PQCLEAN_LIGHTSABER_AVX2_BS2POLVECp(temp, pk); + PQCLEAN_LIGHTSABER_AVX2_InnerProd(vprime, temp, skpv1_eval); + PQCLEAN_LIGHTSABER_AVX2_BS2POLmsg(message, m); + + for (i = 0; i < SABER_N; i++) { + vprime->coeffs[i] += h1 - (message->coeffs[i] << (SABER_EP - 1)); + vprime->coeffs[i] &= SABER_P - 1; + vprime->coeffs[i] >>= SABER_EP - SABER_ET; + } + + PQCLEAN_LIGHTSABER_AVX2_POLT2BS(msk_c, vprime); +} + + +void PQCLEAN_LIGHTSABER_AVX2_indcpa_kem_dec(uint8_t m[SABER_KEYBYTES], const uint8_t sk[SABER_INDCPA_SECRETKEYBYTES], const uint8_t ciphertext[SABER_BYTES_CCA_DEC]) { + size_t i; + + poly temp[SABER_L]; + toom4_points sksv_eval[SABER_L]; + + const uint8_t *packed_cm = ciphertext + SABER_POLYVECCOMPRESSEDBYTES; + poly *v = &temp[0]; + poly *cm = &temp[1]; + + PQCLEAN_LIGHTSABER_AVX2_BS2POLVECq(temp, sk); + for (i = 0; i < SABER_L; i++) { + PQCLEAN_LIGHTSABER_AVX2_toom4_eval(&sksv_eval[i], &temp[i]); + } + + PQCLEAN_LIGHTSABER_AVX2_BS2POLVECp(temp, ciphertext); + PQCLEAN_LIGHTSABER_AVX2_InnerProd(v, temp, sksv_eval); + + PQCLEAN_LIGHTSABER_AVX2_BS2POLT(cm, packed_cm); + + for (i = 0; i < SABER_N; i++) { + v->coeffs[i] += h2 - (cm->coeffs[i] << (SABER_EP - SABER_ET)); + v->coeffs[i] &= SABER_P - 1; + v->coeffs[i] >>= SABER_EP - 1; + } + + PQCLEAN_LIGHTSABER_AVX2_POLmsg2BS(m, v); +} diff --git a/crypto_kem/lightsaber/avx2/SABER_indcpa.h b/crypto_kem/lightsaber/avx2/SABER_indcpa.h new file mode 100644 index 00000000..61ee77ba --- /dev/null +++ b/crypto_kem/lightsaber/avx2/SABER_indcpa.h @@ -0,0 +1,13 @@ +#ifndef INDCPA_H +#define INDCPA_H +#include "SABER_params.h" +#include + +void PQCLEAN_LIGHTSABER_AVX2_indcpa_kem_keypair(uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES], uint8_t sk[SABER_INDCPA_SECRETKEYBYTES]); + +void PQCLEAN_LIGHTSABER_AVX2_indcpa_kem_enc(uint8_t ciphertext[SABER_BYTES_CCA_DEC], const uint8_t m[SABER_KEYBYTES], const uint8_t noiseseed[SABER_NOISESEEDBYTES], const uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES]); + +void PQCLEAN_LIGHTSABER_AVX2_indcpa_kem_dec(uint8_t m[SABER_KEYBYTES], const uint8_t sk[SABER_INDCPA_SECRETKEYBYTES], const uint8_t ciphertext[SABER_BYTES_CCA_DEC]); + + +#endif diff --git a/crypto_kem/lightsaber/avx2/SABER_params.h b/crypto_kem/lightsaber/avx2/SABER_params.h new file mode 100644 index 00000000..8da6ec34 --- /dev/null +++ b/crypto_kem/lightsaber/avx2/SABER_params.h @@ -0,0 +1,41 @@ +#ifndef PARAMS_H +#define PARAMS_H + + +/* Don't change anything below this line */ +#define SABER_L 2 +#define SABER_MU 10 +#define SABER_ET 3 + +#define SABER_N 256 + +#define SABER_EP 10 +#define SABER_P (1 << SABER_EP) + +#define SABER_EQ 13 +#define SABER_Q (1 << SABER_EQ) + +#define SABER_SEEDBYTES 32 +#define SABER_NOISESEEDBYTES 32 +#define SABER_KEYBYTES 32 +#define SABER_HASHBYTES 32 + +#define SABER_POLYCOINBYTES (SABER_MU * SABER_N / 8) + +#define SABER_POLYBYTES (SABER_EQ * SABER_N / 8) +#define SABER_POLYVECBYTES (SABER_L * SABER_POLYBYTES) + +#define SABER_POLYCOMPRESSEDBYTES (SABER_EP * SABER_N / 8) +#define SABER_POLYVECCOMPRESSEDBYTES (SABER_L * SABER_POLYCOMPRESSEDBYTES) + +#define SABER_SCALEBYTES_KEM (SABER_ET * SABER_N / 8) + +#define SABER_INDCPA_PUBLICKEYBYTES (SABER_POLYVECCOMPRESSEDBYTES + SABER_SEEDBYTES) +#define SABER_INDCPA_SECRETKEYBYTES (SABER_POLYVECBYTES) + +#define SABER_PUBLICKEYBYTES (SABER_INDCPA_PUBLICKEYBYTES) +#define SABER_SECRETKEYBYTES (SABER_INDCPA_SECRETKEYBYTES + SABER_INDCPA_PUBLICKEYBYTES + SABER_HASHBYTES + SABER_KEYBYTES) + +#define SABER_BYTES_CCA_DEC (SABER_POLYVECCOMPRESSEDBYTES + SABER_SCALEBYTES_KEM) + +#endif diff --git a/crypto_kem/lightsaber/avx2/api.h b/crypto_kem/lightsaber/avx2/api.h new file mode 100644 index 00000000..d1e2105b --- /dev/null +++ b/crypto_kem/lightsaber/avx2/api.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_LIGHTSABER_AVX2_API_H +#define PQCLEAN_LIGHTSABER_AVX2_API_H + + +#define PQCLEAN_LIGHTSABER_AVX2_CRYPTO_ALGNAME "LightSaber" +#define PQCLEAN_LIGHTSABER_AVX2_CRYPTO_BYTES 32 +#define PQCLEAN_LIGHTSABER_AVX2_CRYPTO_CIPHERTEXTBYTES 736 +#define PQCLEAN_LIGHTSABER_AVX2_CRYPTO_PUBLICKEYBYTES 672 +#define PQCLEAN_LIGHTSABER_AVX2_CRYPTO_SECRETKEYBYTES 1568 + +int PQCLEAN_LIGHTSABER_AVX2_crypto_kem_keypair(unsigned char *pk, unsigned char *sk); + +int PQCLEAN_LIGHTSABER_AVX2_crypto_kem_enc(unsigned char *ct, unsigned char *k, const unsigned char *pk); + +int PQCLEAN_LIGHTSABER_AVX2_crypto_kem_dec(unsigned char *k, const unsigned char *ct, const unsigned char *sk); + + +#endif /* PQCLEAN_LIGHTSABER_AVX2_API_H */ diff --git a/crypto_kem/lightsaber/avx2/cbd.c b/crypto_kem/lightsaber/avx2/cbd.c new file mode 100644 index 00000000..5a61236f --- /dev/null +++ b/crypto_kem/lightsaber/avx2/cbd.c @@ -0,0 +1,48 @@ +#include "SABER_params.h" +#include "api.h" +#include "cbd.h" +#include +/*--------------------------------------------------------------------- +This file has been adapted from the implementation +(available at, Public Domain https://github.com/pq-crystals/kyber) +of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" +by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, +Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle +----------------------------------------------------------------------*/ + + +static uint64_t load_littleendian(const uint8_t *x, int bytes) { + int i; + uint64_t r = x[0]; + for (i = 1; i < bytes; i++) { + r |= (uint64_t)x[i] << (8 * i); + } + return r; +} + +void PQCLEAN_LIGHTSABER_AVX2_cbd(uint16_t s[SABER_N], const uint8_t buf[SABER_POLYCOINBYTES]) { + uint64_t t, d, a[4], b[4]; + int i, j; + + for (i = 0; i < SABER_N / 4; i++) { + t = load_littleendian(buf + 5 * i, 5); + d = 0; + for (j = 0; j < 5; j++) { + d += (t >> j) & 0x0842108421UL; + } + + a[0] = d & 0x1f; + b[0] = (d >> 5) & 0x1f; + a[1] = (d >> 10) & 0x1f; + b[1] = (d >> 15) & 0x1f; + a[2] = (d >> 20) & 0x1f; + b[2] = (d >> 25) & 0x1f; + a[3] = (d >> 30) & 0x1f; + b[3] = (d >> 35); + + s[4 * i + 0] = (uint16_t)(a[0] - b[0]); + s[4 * i + 1] = (uint16_t)(a[1] - b[1]); + s[4 * i + 2] = (uint16_t)(a[2] - b[2]); + s[4 * i + 3] = (uint16_t)(a[3] - b[3]); + } +} diff --git a/crypto_kem/lightsaber/avx2/cbd.h b/crypto_kem/lightsaber/avx2/cbd.h new file mode 100644 index 00000000..5be3a405 --- /dev/null +++ b/crypto_kem/lightsaber/avx2/cbd.h @@ -0,0 +1,16 @@ +#ifndef CBD_H +#define CBD_H +/*--------------------------------------------------------------------- +This file has been adapted from the implementation +(available at, Public Domain https://github.com/pq-crystals/kyber) +of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" +by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, +Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle +----------------------------------------------------------------------*/ +#include "SABER_params.h" +#include + +void PQCLEAN_LIGHTSABER_AVX2_cbd(uint16_t s[SABER_N], const uint8_t buf[SABER_POLYCOINBYTES]); + + +#endif diff --git a/crypto_kem/lightsaber/avx2/kem.c b/crypto_kem/lightsaber/avx2/kem.c new file mode 100644 index 00000000..e60a2d51 --- /dev/null +++ b/crypto_kem/lightsaber/avx2/kem.c @@ -0,0 +1,77 @@ +#include "SABER_indcpa.h" +#include "SABER_params.h" +#include "api.h" +#include "fips202.h" +#include "randombytes.h" +#include "verify.h" +#include +#include + + +int PQCLEAN_LIGHTSABER_AVX2_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + size_t i; + + PQCLEAN_LIGHTSABER_AVX2_indcpa_kem_keypair(pk, sk); // sk[0:SABER_INDCPA_SECRETKEYBYTES-1] <-- sk + for (i = 0; i < SABER_INDCPA_PUBLICKEYBYTES; i++) { + sk[i + SABER_INDCPA_SECRETKEYBYTES] = pk[i]; // sk[SABER_INDCPA_SECRETKEYBYTES:SABER_INDCPA_SECRETKEYBYTES+SABER_INDCPA_SECRETKEYBYTES-1] <-- pk + } + + sha3_256(sk + SABER_SECRETKEYBYTES - 64, pk, SABER_INDCPA_PUBLICKEYBYTES); // Then hash(pk) is appended. + + randombytes(sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES); // Remaining part of sk contains a pseudo-random number. + // This is output when check in PQCLEAN_LIGHTSABER_AVX2_crypto_kem_dec() fails. + return (0); +} + +int PQCLEAN_LIGHTSABER_AVX2_crypto_kem_enc(uint8_t *c, uint8_t *k, const uint8_t *pk) { + + uint8_t kr[64]; // Will contain key, coins + uint8_t buf[64]; + + randombytes(buf, 32); + + sha3_256(buf, buf, 32); // BUF[0:31] <-- random message (will be used as the key for client) Note: hash doesnot release system RNG output + + sha3_256(buf + 32, pk, SABER_INDCPA_PUBLICKEYBYTES); // BUF[32:63] <-- Hash(public key); Multitarget countermeasure for coins + contributory KEM + + sha3_512(kr, buf, 64); // kr[0:63] <-- Hash(buf[0:63]); + // K^ <-- kr[0:31] + // noiseseed (r) <-- kr[32:63]; + PQCLEAN_LIGHTSABER_AVX2_indcpa_kem_enc(c, buf, kr + 32, pk); // buf[0:31] contains message; kr[32:63] contains randomness r; + + sha3_256(kr + 32, c, SABER_BYTES_CCA_DEC); + + sha3_256(k, kr, 64); // hash concatenation of pre-k and h(c) to k + + return (0); +} + +int PQCLEAN_LIGHTSABER_AVX2_crypto_kem_dec(uint8_t *k, const uint8_t *c, const uint8_t *sk) { + size_t i; + uint8_t fail; + uint8_t cmp[SABER_BYTES_CCA_DEC]; + uint8_t buf[64]; + uint8_t kr[64]; // Will contain key, coins + const uint8_t *pk = sk + SABER_INDCPA_SECRETKEYBYTES; + + PQCLEAN_LIGHTSABER_AVX2_indcpa_kem_dec(buf, sk, c); // buf[0:31] <-- message + + // Multitarget countermeasure for coins + contributory KEM + for (i = 0; i < 32; i++) { // Save hash by storing h(pk) in sk + buf[32 + i] = sk[SABER_SECRETKEYBYTES - 64 + i]; + } + + sha3_512(kr, buf, 64); + + PQCLEAN_LIGHTSABER_AVX2_indcpa_kem_enc(cmp, buf, kr + 32, pk); + + fail = PQCLEAN_LIGHTSABER_AVX2_verify(c, cmp, SABER_BYTES_CCA_DEC); + + sha3_256(kr + 32, c, SABER_BYTES_CCA_DEC); // overwrite coins in kr with h(c) + + PQCLEAN_LIGHTSABER_AVX2_cmov(kr, sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES, fail); + + sha3_256(k, kr, 64); // hash concatenation of pre-k and h(c) to k + + return (0); +} diff --git a/crypto_kem/lightsaber/avx2/pack_unpack.c b/crypto_kem/lightsaber/avx2/pack_unpack.c new file mode 100644 index 00000000..08f7a9d9 --- /dev/null +++ b/crypto_kem/lightsaber/avx2/pack_unpack.c @@ -0,0 +1,155 @@ +#include "SABER_params.h" +#include "pack_unpack.h" +#include "poly.h" +#include + +void PQCLEAN_LIGHTSABER_AVX2_POLT2BS(uint8_t bytes[SABER_SCALEBYTES_KEM], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; + for (j = 0; j < SABER_N / 8; j++) { + out[0] = (uint8_t) ((in[0] & 0x7) | ((in[1] & 0x7) << 3) | (in[2] << 6)); + out[1] = (uint8_t) (((in[2] >> 2) & 0x01) | ((in[3] & 0x7) << 1) | ((in[4] & 0x7) << 4) | (in[5] << 7)); + out[2] = (uint8_t) (((in[5] >> 1) & 0x03) | ((in[6] & 0x7) << 2) | (in[7] << 5)); + in += 8; + out += 3; + } +} + +void PQCLEAN_LIGHTSABER_AVX2_BS2POLT(poly *data, const uint8_t bytes[SABER_SCALEBYTES_KEM]) { + /* This function does not reduce its output mod T */ + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; + for (j = 0; j < SABER_N / 8; j++) { + out[0] = in[0]; + out[1] = in[0] >> 3; + out[2] = (in[0] >> 6) | (in[1] << 2); + out[3] = in[1] >> 1; + out[4] = in[1] >> 4; + out[5] = (in[1] >> 7) | (in[2] << 1); + out[6] = in[2] >> 2; + out[7] = in[2] >> 5; + in += 3; + out += 8; + } +} + +static void POLq2BS(uint8_t bytes[SABER_POLYBYTES], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; + for (j = 0; j < SABER_N / 8; j++) { + out[0] = (uint8_t) (in[0]); + out[1] = (uint8_t) (((in[0] >> 8) & 0x1f) | (in[1] << 5)); + out[2] = (uint8_t) (in[1] >> 3); + out[3] = (uint8_t) (((in[1] >> 11) & 0x03) | (in[2] << 2)); + out[4] = (uint8_t) (((in[2] >> 6) & 0x7f) | (in[3] << 7)); + out[5] = (uint8_t) (in[3] >> 1); + out[6] = (uint8_t) (((in[3] >> 9) & 0x0f) | (in[4] << 4)); + out[7] = (uint8_t) (in[4] >> 4); + out[8] = (uint8_t) (((in[4] >> 12) & 0x01) | (in[5] << 1)); + out[9] = (uint8_t) (((in[5] >> 7) & 0x3f) | (in[6] << 6)); + out[10] = (uint8_t) (in[6] >> 2); + out[11] = (uint8_t) (((in[6] >> 10) & 0x07) | (in[7] << 3)); + out[12] = (uint8_t) (in[7] >> 5); + in += 8; + out += 13; + } +} + +static void BS2POLq(poly *data, const uint8_t bytes[SABER_POLYBYTES]) { + /* This function does not reduce its output mod Q */ + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; + for (j = 0; j < SABER_N / 8; j++) { + out[0] = (in[0]) | (in[1] << 8); + out[1] = (in[1] >> 5) | (in[2] << 3) | (in[3] << 11); + out[2] = (in[3] >> 2) | (in[4] << 6); + out[3] = (in[4] >> 7) | (in[5] << 1) | (in[6] << 9); + out[4] = (in[6] >> 4) | (in[7] << 4) | (in[8] << 12); + out[5] = (in[8] >> 1) | (in[9] << 7); + out[6] = (in[9] >> 6) | (in[10] << 2) | (in[11] << 10); + out[7] = (in[11] >> 3) | (in[12] << 5); + in += 13; + out += 8; + } +} + +static void POLp2BS(uint8_t bytes[SABER_POLYCOMPRESSEDBYTES], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; + for (j = 0; j < SABER_N / 4; j++) { + out[0] = (uint8_t) (in[0]); + out[1] = (uint8_t) (((in[0] >> 8) & 0x03) | (in[1] << 2)); + out[2] = (uint8_t) (((in[1] >> 6) & 0x0f) | (in[2] << 4)); + out[3] = (uint8_t) (((in[2] >> 4) & 0x3f) | (in[3] << 6)); + out[4] = (uint8_t) (in[3] >> 2); + in += 4; + out += 5; + } +} + +static void BS2POLp(poly *data, const uint8_t bytes[SABER_POLYCOMPRESSEDBYTES]) { + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; + for (j = 0; j < SABER_N / 4; j++) { + out[0] = in[0] | (in[1] << 8); + out[1] = (in[1] >> 2) | (in[2] << 6); + out[2] = (in[2] >> 4) | (in[3] << 4); + out[3] = (in[3] >> 6) | (in[4] << 2); + in += 5; + out += 4; + } +} + +void PQCLEAN_LIGHTSABER_AVX2_POLVECq2BS(uint8_t bytes[SABER_POLYVECBYTES], const poly data[SABER_L]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + POLq2BS(bytes + i * SABER_POLYBYTES, &data[i]); + } +} + +void PQCLEAN_LIGHTSABER_AVX2_BS2POLVECq(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECBYTES]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + BS2POLq(&data[i], bytes + i * SABER_POLYBYTES); + } +} + +void PQCLEAN_LIGHTSABER_AVX2_POLVECp2BS(uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES], const poly data[SABER_L]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + POLp2BS(bytes + i * SABER_POLYCOMPRESSEDBYTES, &data[i]); + } +} + +void PQCLEAN_LIGHTSABER_AVX2_BS2POLVECp(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + BS2POLp(&data[i], bytes + i * SABER_POLYCOMPRESSEDBYTES); + } +} + +void PQCLEAN_LIGHTSABER_AVX2_BS2POLmsg(poly *data, const uint8_t bytes[SABER_KEYBYTES]) { + size_t i, j; + for (j = 0; j < SABER_KEYBYTES; j++) { + for (i = 0; i < 8; i++) { + data->coeffs[j * 8 + i] = ((bytes[j] >> i) & 0x01); + } + } +} + +void PQCLEAN_LIGHTSABER_AVX2_POLmsg2BS(uint8_t bytes[SABER_KEYBYTES], const poly *data) { + size_t i, j; + memset(bytes, 0, SABER_KEYBYTES); + + for (j = 0; j < SABER_KEYBYTES; j++) { + for (i = 0; i < 8; i++) { + bytes[j] = bytes[j] | ((data->coeffs[j * 8 + i] & 0x01) << i); + } + } +} diff --git a/crypto_kem/lightsaber/avx2/pack_unpack.h b/crypto_kem/lightsaber/avx2/pack_unpack.h new file mode 100644 index 00000000..2ba7822b --- /dev/null +++ b/crypto_kem/lightsaber/avx2/pack_unpack.h @@ -0,0 +1,28 @@ +#ifndef PACK_UNPACK_H +#define PACK_UNPACK_H +#include "SABER_params.h" +#include "poly.h" +#include +#include + +void PQCLEAN_LIGHTSABER_AVX2_POLT2BS(uint8_t bytes[SABER_SCALEBYTES_KEM], const poly *data); + +void PQCLEAN_LIGHTSABER_AVX2_BS2POLT(poly *data, const uint8_t bytes[SABER_SCALEBYTES_KEM]); + + +void PQCLEAN_LIGHTSABER_AVX2_POLVECq2BS(uint8_t bytes[SABER_POLYVECBYTES], const poly data[SABER_L]); + +void PQCLEAN_LIGHTSABER_AVX2_POLVECp2BS(uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES], const poly data[SABER_L]); + + +void PQCLEAN_LIGHTSABER_AVX2_BS2POLVECq(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECBYTES]); + +void PQCLEAN_LIGHTSABER_AVX2_BS2POLVECp(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES]); + + +void PQCLEAN_LIGHTSABER_AVX2_BS2POLmsg(poly *data, const uint8_t bytes[SABER_KEYBYTES]); + +void PQCLEAN_LIGHTSABER_AVX2_POLmsg2BS(uint8_t bytes[SABER_KEYBYTES], const poly *data); + + +#endif diff --git a/crypto_kem/lightsaber/avx2/poly.c b/crypto_kem/lightsaber/avx2/poly.c new file mode 100644 index 00000000..56227f6f --- /dev/null +++ b/crypto_kem/lightsaber/avx2/poly.c @@ -0,0 +1,62 @@ +#include "cbd.h" +#include "fips202.h" +#include "pack_unpack.h" +#include "poly.h" + + +void PQCLEAN_LIGHTSABER_AVX2_MatrixVectorMul(poly c[SABER_L], const poly A[SABER_L][SABER_L], const toom4_points s_eval[SABER_L], int transpose) { + size_t i, j; + toom4_points_product c_eval; + + if (transpose) { + for (i = 0; i < SABER_L; i++) { + PQCLEAN_LIGHTSABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &A[0][i], &s_eval[0], 0); + for (j = 1; j < SABER_L; j++) { + PQCLEAN_LIGHTSABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &A[j][i], &s_eval[j], 1); + } + PQCLEAN_LIGHTSABER_AVX2_toom4_interp(&c[i], &c_eval); + } + } else { + for (i = 0; i < SABER_L; i++) { + PQCLEAN_LIGHTSABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &A[i][0], &s_eval[0], 0); + for (j = 1; j < SABER_L; j++) { + PQCLEAN_LIGHTSABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &A[i][j], &s_eval[j], 1); + } + PQCLEAN_LIGHTSABER_AVX2_toom4_interp(&c[i], &c_eval); + } + } +} + +void PQCLEAN_LIGHTSABER_AVX2_InnerProd(poly *c, const poly b[SABER_L], const toom4_points s_eval[SABER_L]) { + size_t i; + toom4_points_product c_eval; //Holds results for 9 Karatsuba at a time + + PQCLEAN_LIGHTSABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &b[0], &s_eval[0], 0); + for (i = 1; i < SABER_L; i++) { + PQCLEAN_LIGHTSABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &b[i], &s_eval[i], 1); + } + + PQCLEAN_LIGHTSABER_AVX2_toom4_interp(c, &c_eval); +} + +void PQCLEAN_LIGHTSABER_AVX2_GenMatrix(poly A[SABER_L][SABER_L], const uint8_t seed[SABER_SEEDBYTES]) { + size_t i; + uint8_t buf[SABER_L * SABER_POLYVECBYTES]; + + shake128(buf, sizeof(buf), seed, SABER_SEEDBYTES); + + for (i = 0; i < SABER_L; i++) { + PQCLEAN_LIGHTSABER_AVX2_BS2POLVECq(A[i], buf + i * SABER_POLYVECBYTES); + } +} + +void PQCLEAN_LIGHTSABER_AVX2_GenSecret(poly s[SABER_L], const uint8_t seed[SABER_NOISESEEDBYTES]) { + size_t i; + uint8_t buf[SABER_L * SABER_POLYCOINBYTES]; + + shake128(buf, sizeof(buf), seed, SABER_NOISESEEDBYTES); + + for (i = 0; i < SABER_L; i++) { + PQCLEAN_LIGHTSABER_AVX2_cbd(s[i].coeffs, buf + i * SABER_POLYCOINBYTES); + } +} diff --git a/crypto_kem/lightsaber/avx2/poly.h b/crypto_kem/lightsaber/avx2/poly.h new file mode 100644 index 00000000..2e7b2a11 --- /dev/null +++ b/crypto_kem/lightsaber/avx2/poly.h @@ -0,0 +1,38 @@ +#ifndef POLY_H +#define POLY_H +#include "SABER_params.h" +#include +#include + +typedef union { + uint16_t coeffs[SABER_N]; + __m256i dummy; +} poly; + +typedef union { + uint16_t coeffs[4 * SABER_N]; + __m256i dummy; +} toom4_points; + +typedef union { + uint16_t coeffs[8 * SABER_N]; + __m256i dummy; +} toom4_points_product; + +void PQCLEAN_LIGHTSABER_AVX2_MatrixVectorMul(poly c[SABER_L], const poly A[SABER_L][SABER_L], const toom4_points s_eval[SABER_L], int transpose); + +void PQCLEAN_LIGHTSABER_AVX2_InnerProd(poly *c, const poly b[SABER_L], const toom4_points s_eval[SABER_L]); + +void PQCLEAN_LIGHTSABER_AVX2_GenMatrix(poly a[SABER_L][SABER_L], const uint8_t seed[SABER_SEEDBYTES]); + +void PQCLEAN_LIGHTSABER_AVX2_GenSecret(poly s[SABER_L], const uint8_t seed[SABER_NOISESEEDBYTES]); + + +void PQCLEAN_LIGHTSABER_AVX2_toom4_interp(poly *res_avx, const toom4_points_product *c_eval); + +void PQCLEAN_LIGHTSABER_AVX2_toom4_eval(toom4_points *b_eval, const poly *b); + +void PQCLEAN_LIGHTSABER_AVX2_toom4_mul_A_by_B_eval(toom4_points_product *c_eval, const poly *a_avx, const toom4_points *b_eval, int accumulate); + + +#endif diff --git a/crypto_kem/lightsaber/avx2/poly_mul.c b/crypto_kem/lightsaber/avx2/poly_mul.c new file mode 100644 index 00000000..51504491 --- /dev/null +++ b/crypto_kem/lightsaber/avx2/poly_mul.c @@ -0,0 +1,1496 @@ +#include "SABER_params.h" +#include "poly.h" + + +#define L (SABER_N / 64) + +/* 16 word parallel multiply */ +#define mul(a, b) _mm256_mullo_epi16((a), (b)) +/* 16 word parallel multiply and accumulate */ +#define mac(a, b, c) _mm256_add_epi16(_mm256_mullo_epi16((a), (b)), (c)) + +static void schoolbook16x16(__m256i *c, const __m256i *a, const __m256i *b) { + __m256i a0, a1, a2, a3; + __m256i b0, b1, b2, b3; + __m256i t0; + a0 = a[0]; + a1 = a[1]; + a2 = a[2]; + a3 = a[3]; + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[0] = mul(a0, b0); + t0 = mul(a0, b1); + c[1] = mac(a1, b0, t0); + t0 = mul(a0, b2); + t0 = mac(a1, b1, t0); + c[2] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[3] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[4] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[5] = mac(a3, b2, t0); + c[6] = mul(a3, b3); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[4] = mac(a0, b0, c[4]); + t0 = mac(a0, b1, c[5]); + c[5] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[6]); + t0 = mac(a1, b1, t0); + c[6] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[7] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[8] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[9] = mac(a3, b2, t0); + c[10] = mul(a3, b3); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[13] = mac(a3, b2, t0); + c[14] = mul(a3, b3); + b0 = b[12]; + b1 = b[13]; + b2 = b[14]; + b3 = b[15]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[17] = mac(a3, b2, t0); + c[18] = mul(a3, b3); + a0 = a[4]; + a1 = a[5]; + a2 = a[6]; + a3 = a[7]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[21] = mac(a3, b2, t0); + c[22] = mul(a3, b3); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[4] = mac(a0, b0, c[4]); + t0 = mac(a0, b1, c[5]); + c[5] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[6]); + t0 = mac(a1, b1, t0); + c[6] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[7]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[7] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[8]); + t0 = mac(a2, b2, t0); + c[8] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[9]); + c[9] = mac(a3, b2, t0); + c[10] = mac(a3, b3, c[10]); + a0 = a[8]; + a1 = a[9]; + a2 = a[10]; + a3 = a[11]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[12]; + b1 = b[13]; + b2 = b[14]; + b3 = b[15]; + c[20] = mac(a0, b0, c[20]); + t0 = mac(a0, b1, c[21]); + c[21] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[22]); + t0 = mac(a1, b1, t0); + c[22] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[23] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[24] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[25] = mac(a3, b2, t0); + c[26] = mul(a3, b3); + a0 = a[12]; + a1 = a[13]; + a2 = a[14]; + a3 = a[15]; + c[24] = mac(a0, b0, c[24]); + t0 = mac(a0, b1, c[25]); + c[25] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[26]); + t0 = mac(a1, b1, t0); + c[26] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[27] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[28] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[29] = mac(a3, b2, t0); + c[30] = mul(a3, b3); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[20] = mac(a0, b0, c[20]); + t0 = mac(a0, b1, c[21]); + c[21] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[22]); + t0 = mac(a1, b1, t0); + c[22] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[23]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[23] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[24]); + t0 = mac(a2, b2, t0); + c[24] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[25]); + c[25] = mac(a3, b2, t0); + c[26] = mac(a3, b3, c[26]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + c[31] = _mm256_setzero_si256(); +} + +static void schoolbook16x16_acc(__m256i *c, const __m256i *a, const __m256i *b) { + __m256i a0, a1, a2, a3; + __m256i b0, b1, b2, b3; + __m256i t0; + a0 = a[0]; + a1 = a[1]; + a2 = a[2]; + a3 = a[3]; + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[0] = mac(a0, b0, c[0]); + t0 = mac(a0, b1, c[1]); + c[1] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[2]); + t0 = mac(a1, b1, t0); + c[2] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[3]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[3] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[4]); + t0 = mac(a2, b2, t0); + c[4] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[5]); + c[5] = mac(a3, b2, t0); + c[6] = mac(a3, b3, c[6]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[4] = mac(a0, b0, c[4]); + t0 = mac(a0, b1, c[5]); + c[5] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[6]); + t0 = mac(a1, b1, t0); + c[6] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[7]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[7] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[8]); + t0 = mac(a2, b2, t0); + c[8] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[9]); + c[9] = mac(a3, b2, t0); + c[10] = mac(a3, b3, c[10]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[12]; + b1 = b[13]; + b2 = b[14]; + b3 = b[15]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + a0 = a[4]; + a1 = a[5]; + a2 = a[6]; + a3 = a[7]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[4] = mac(a0, b0, c[4]); + t0 = mac(a0, b1, c[5]); + c[5] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[6]); + t0 = mac(a1, b1, t0); + c[6] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[7]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[7] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[8]); + t0 = mac(a2, b2, t0); + c[8] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[9]); + c[9] = mac(a3, b2, t0); + c[10] = mac(a3, b3, c[10]); + a0 = a[8]; + a1 = a[9]; + a2 = a[10]; + a3 = a[11]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[12]; + b1 = b[13]; + b2 = b[14]; + b3 = b[15]; + c[20] = mac(a0, b0, c[20]); + t0 = mac(a0, b1, c[21]); + c[21] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[22]); + t0 = mac(a1, b1, t0); + c[22] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[23]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[23] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[24]); + t0 = mac(a2, b2, t0); + c[24] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[25]); + c[25] = mac(a3, b2, t0); + c[26] = mac(a3, b3, c[26]); + a0 = a[12]; + a1 = a[13]; + a2 = a[14]; + a3 = a[15]; + c[24] = mac(a0, b0, c[24]); + t0 = mac(a0, b1, c[25]); + c[25] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[26]); + t0 = mac(a1, b1, t0); + c[26] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[27]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[27] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[28]); + t0 = mac(a2, b2, t0); + c[28] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[29]); + c[29] = mac(a3, b2, t0); + c[30] = mac(a3, b3, c[30]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[20] = mac(a0, b0, c[20]); + t0 = mac(a0, b1, c[21]); + c[21] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[22]); + t0 = mac(a1, b1, t0); + c[22] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[23]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[23] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[24]); + t0 = mac(a2, b2, t0); + c[24] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[25]); + c[25] = mac(a3, b2, t0); + c[26] = mac(a3, b3, c[26]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); +} + + +static void transpose(__m256i *M) { + __m256i r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11; + __m256i temp, temp0, temp1, temp2; + + r0 = _mm256_unpacklo_epi16(M[0], M[1]); + r1 = _mm256_unpacklo_epi16(M[2], M[3]); + r2 = _mm256_unpacklo_epi16(M[4], M[5]); + r3 = _mm256_unpacklo_epi16(M[6], M[7]); + r4 = _mm256_unpacklo_epi16(M[8], M[9]); + r5 = _mm256_unpacklo_epi16(M[10], M[11]); + r6 = _mm256_unpacklo_epi16(M[12], M[13]); + r7 = _mm256_unpacklo_epi16(M[14], M[15]); + + temp = _mm256_unpacklo_epi32(r0, r1); + temp0 = _mm256_unpacklo_epi32(r2, r3); + temp1 = _mm256_unpacklo_epi32(r4, r5); + temp2 = _mm256_unpacklo_epi32(r6, r7); + + r8 = _mm256_unpackhi_epi32(r0, r1); + r9 = _mm256_unpackhi_epi32(r2, r3); + r10 = _mm256_unpackhi_epi32(r4, r5); + r11 = _mm256_unpackhi_epi32(r6, r7); + + r0 = _mm256_unpacklo_epi64(temp, temp0); + r2 = _mm256_unpackhi_epi64(temp, temp0); + r1 = _mm256_unpacklo_epi64(temp1, temp2); + r3 = _mm256_unpackhi_epi64(temp1, temp2); + + temp = _mm256_unpackhi_epi16(M[0], M[1]); + temp0 = _mm256_unpackhi_epi16(M[2], M[3]); + temp1 = _mm256_unpackhi_epi16(M[4], M[5]); + temp2 = _mm256_unpackhi_epi16(M[6], M[7]); + + r4 = _mm256_unpackhi_epi16(M[8], M[9]); + M[0] = _mm256_permute2f128_si256(r0, r1, 0x20); + M[8] = _mm256_permute2f128_si256(r0, r1, 0x31); + M[1] = _mm256_permute2f128_si256(r2, r3, 0x20); + M[9] = _mm256_permute2f128_si256(r2, r3, 0x31); + r5 = _mm256_unpackhi_epi16(M[10], M[11]); + r6 = _mm256_unpackhi_epi16(M[12], M[13]); + r7 = _mm256_unpackhi_epi16(M[14], M[15]); + + r0 = _mm256_unpacklo_epi64(r8, r9); + r1 = _mm256_unpacklo_epi64(r10, r11); + r2 = _mm256_unpackhi_epi64(r8, r9); + r3 = _mm256_unpackhi_epi64(r10, r11); + + M[3] = _mm256_permute2f128_si256(r2, r3, 0x20); + M[11] = _mm256_permute2f128_si256(r2, r3, 0x31); + M[2] = _mm256_permute2f128_si256(r0, r1, 0x20); + M[10] = _mm256_permute2f128_si256(r0, r1, 0x31); + + r0 = _mm256_unpacklo_epi32(temp, temp0); + r1 = _mm256_unpacklo_epi32(temp1, temp2); + r2 = _mm256_unpacklo_epi32(r4, r5); + r3 = _mm256_unpacklo_epi32(r6, r7); + + r8 = _mm256_unpacklo_epi64(r0, r1); + r10 = _mm256_unpackhi_epi64(r0, r1); + r9 = _mm256_unpacklo_epi64(r2, r3); + r11 = _mm256_unpackhi_epi64(r2, r3); + + M[4] = _mm256_permute2f128_si256(r8, r9, 0x20); + M[12] = _mm256_permute2f128_si256(r8, r9, 0x31); + M[5] = _mm256_permute2f128_si256(r10, r11, 0x20); + M[13] = _mm256_permute2f128_si256(r10, r11, 0x31); + + r0 = _mm256_unpackhi_epi32(temp, temp0); + r1 = _mm256_unpackhi_epi32(temp1, temp2); + r2 = _mm256_unpackhi_epi32(r4, r5); + r3 = _mm256_unpackhi_epi32(r6, r7); + + r4 = _mm256_unpacklo_epi64(r0, r1); + r6 = _mm256_unpackhi_epi64(r0, r1); + r5 = _mm256_unpacklo_epi64(r2, r3); + r7 = _mm256_unpackhi_epi64(r2, r3); + + M[6] = _mm256_permute2f128_si256(r4, r5, 0x20); + M[14] = _mm256_permute2f128_si256(r4, r5, 0x31); + M[7] = _mm256_permute2f128_si256(r6, r7, 0x20); + M[15] = _mm256_permute2f128_si256(r6, r7, 0x31); +} + +static void batch_64coefficient_multiplications(toom4_points_product *c_eval, const __m256i *a, const toom4_points *b_eval, int accumulate) { + toom4_points a_eval;// Holds evaluation (a & b) for 7 Karatsuba at a time + __m256i r0_avx, r1_avx, r2_avx, r3_avx; + __m256i *va = (__m256i *)a_eval.coeffs; + __m256i *vb = (__m256i *)b_eval->coeffs; + __m256i *vc = (__m256i *)c_eval->coeffs; + + //------------------AVX evaluation for 1st poly----------------------- + r0_avx = a[0 * L + 0]; + r1_avx = a[0 * L + 1]; + r2_avx = a[0 * L + 2]; + r3_avx = a[0 * L + 3]; + + va[0] = r0_avx; + va[1] = r1_avx; + va[2] = r2_avx; + va[3] = r3_avx; + va[4] = _mm256_add_epi16(r0_avx, r1_avx); + va[5] = _mm256_add_epi16(r2_avx, r3_avx); + va[6] = _mm256_add_epi16(r0_avx, r2_avx); + va[7] = _mm256_add_epi16(r1_avx, r3_avx); + va[8] = _mm256_add_epi16(va[6], va[7]); + //------------------AVX evaluation for 1st poly ends------------------ + + //------------------AVX evaluation for 2nd poly----------------------- + r0_avx = a[1 * L + 0]; + r1_avx = a[1 * L + 1]; + r2_avx = a[1 * L + 2]; + r3_avx = a[1 * L + 3]; + + va[0 + 9] = r0_avx; + va[1 + 9] = r1_avx; + va[2 + 9] = r2_avx; + va[3 + 9] = r3_avx; + va[4 + 9] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 9] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 9] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 9] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 9] = _mm256_add_epi16(va[6 + 9], va[7 + 9]); + //------------------AVX evaluation for 2nd poly ends------------------ + + //------------------AVX evaluation for 3rd poly----------------------- + r0_avx = a[2 * L + 0]; + r1_avx = a[2 * L + 1]; + r2_avx = a[2 * L + 2]; + r3_avx = a[2 * L + 3]; + + va[0 + 18] = r0_avx; + va[1 + 18] = r1_avx; + va[2 + 18] = r2_avx; + va[3 + 18] = r3_avx; + va[4 + 18] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 18] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 18] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 18] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 18] = _mm256_add_epi16(va[6 + 18], va[7 + 18]); + //------------------AVX evaluation for 3rd poly ends------------------ + + //------------------AVX evaluation for 4th poly----------------------- + r0_avx = a[3 * L + 0]; + r1_avx = a[3 * L + 1]; + r2_avx = a[3 * L + 2]; + r3_avx = a[3 * L + 3]; + + va[0 + 27] = r0_avx; + va[1 + 27] = r1_avx; + va[2 + 27] = r2_avx; + va[3 + 27] = r3_avx; + va[4 + 27] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 27] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 27] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 27] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 27] = _mm256_add_epi16(va[6 + 27], va[7 + 27]); + //------------------AVX evaluation for 4th poly ends------------------ + + //------------------AVX evaluation for 5th poly----------------------- + r0_avx = a[4 * L + 0]; + r1_avx = a[4 * L + 1]; + r2_avx = a[4 * L + 2]; + r3_avx = a[4 * L + 3]; + + va[0 + 36] = r0_avx; + va[1 + 36] = r1_avx; + va[2 + 36] = r2_avx; + va[3 + 36] = r3_avx; + va[4 + 36] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 36] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 36] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 36] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 36] = _mm256_add_epi16(va[6 + 36], va[7 + 36]); + //------------------AVX evaluation for 5th poly ends------------------ + + //------------------AVX evaluation for 6th poly----------------------- + r0_avx = a[5 * L + 0]; + r1_avx = a[5 * L + 1]; + r2_avx = a[5 * L + 2]; + r3_avx = a[5 * L + 3]; + + va[0 + 45] = r0_avx; + va[1 + 45] = r1_avx; + va[2 + 45] = r2_avx; + va[3 + 45] = r3_avx; + va[4 + 45] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 45] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 45] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 45] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 45] = _mm256_add_epi16(va[6 + 45], va[7 + 45]); + //------------------AVX evaluation for 6th poly ends------------------ + + //------------------AVX evaluation for 7th poly----------------------- + r0_avx = a[6 * L + 0]; + r1_avx = a[6 * L + 1]; + r2_avx = a[6 * L + 2]; + r3_avx = a[6 * L + 3]; + + va[0 + 54] = r0_avx; + va[1 + 54] = r1_avx; + va[2 + 54] = r2_avx; + va[3 + 54] = r3_avx; + va[4 + 54] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 54] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 54] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 54] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 54] = _mm256_add_epi16(va[6 + 54], va[7 + 54]); + //------------------AVX evaluation for 7th poly ends------------------ + + //-----------------Forward transposes-------------------------------------- + transpose(va); + transpose(va + 16); + transpose(va + 32); + transpose(va + 48); + //-----------------Forward transposes ends--------------------------------- + + if (accumulate == 0) { + schoolbook16x16(vc, va, vb); + schoolbook16x16(vc + 32, va + 16, vb + 16); + schoolbook16x16(vc + 64, va + 32, vb + 32); + schoolbook16x16(vc + 96, va + 48, vb + 48); + } else { + schoolbook16x16_acc(vc, va, vb); + schoolbook16x16_acc(vc + 32, va + 16, vb + 16); + schoolbook16x16_acc(vc + 64, va + 32, vb + 32); + schoolbook16x16_acc(vc + 96, va + 48, vb + 48); + } +} + +static void karatsuba_eval(__m256i *b_eval, const __m256i *b) { + __m256i r0_avx, r1_avx, r2_avx, r3_avx; + + //-------1st poly---------------------------------------------------- + r0_avx = b[0 * L + 0]; + r1_avx = b[0 * L + 1]; + r2_avx = b[0 * L + 2]; + r3_avx = b[0 * L + 3]; + + b_eval[0] = r0_avx; + b_eval[1] = r1_avx; + b_eval[2] = r2_avx; + b_eval[3] = r3_avx; + b_eval[4] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8] = _mm256_add_epi16(b_eval[6], b_eval[7]); + + //-------2nd poly---------------------------------------------------- + r0_avx = b[1 * L + 0]; + r1_avx = b[1 * L + 1]; + r2_avx = b[1 * L + 2]; + r3_avx = b[1 * L + 3]; + + b_eval[0 + 9] = r0_avx; + b_eval[1 + 9] = r1_avx; + b_eval[2 + 9] = r2_avx; + b_eval[3 + 9] = r3_avx; + b_eval[4 + 9] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 9] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 9] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 9] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 9] = _mm256_add_epi16(b_eval[6 + 9], b_eval[7 + 9]); + + //-------3rd poly---------------------------------------------------- + r0_avx = b[2 * L + 0]; + r1_avx = b[2 * L + 1]; + r2_avx = b[2 * L + 2]; + r3_avx = b[2 * L + 3]; + + b_eval[0 + 18] = r0_avx; + b_eval[1 + 18] = r1_avx; + b_eval[2 + 18] = r2_avx; + b_eval[3 + 18] = r3_avx; + b_eval[4 + 18] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 18] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 18] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 18] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 18] = _mm256_add_epi16(b_eval[6 + 18], b_eval[7 + 18]); + + //-------4th poly---------------------------------------------------- + r0_avx = b[3 * L + 0]; + r1_avx = b[3 * L + 1]; + r2_avx = b[3 * L + 2]; + r3_avx = b[3 * L + 3]; + + b_eval[0 + 27] = r0_avx; + b_eval[1 + 27] = r1_avx; + b_eval[2 + 27] = r2_avx; + b_eval[3 + 27] = r3_avx; + b_eval[4 + 27] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 27] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 27] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 27] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 27] = _mm256_add_epi16(b_eval[6 + 27], b_eval[7 + 27]); + + //-------5th poly---------------------------------------------------- + r0_avx = b[4 * L + 0]; + r1_avx = b[4 * L + 1]; + r2_avx = b[4 * L + 2]; + r3_avx = b[4 * L + 3]; + + b_eval[0 + 36] = r0_avx; + b_eval[1 + 36] = r1_avx; + b_eval[2 + 36] = r2_avx; + b_eval[3 + 36] = r3_avx; + b_eval[4 + 36] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 36] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 36] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 36] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 36] = _mm256_add_epi16(b_eval[6 + 36], b_eval[7 + 36]); + + //-------6th poly---------------------------------------------------- + r0_avx = b[5 * L + 0]; + r1_avx = b[5 * L + 1]; + r2_avx = b[5 * L + 2]; + r3_avx = b[5 * L + 3]; + + b_eval[0 + 45] = r0_avx; + b_eval[1 + 45] = r1_avx; + b_eval[2 + 45] = r2_avx; + b_eval[3 + 45] = r3_avx; + b_eval[4 + 45] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 45] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 45] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 45] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 45] = _mm256_add_epi16(b_eval[6 + 45], b_eval[7 + 45]); + + //-------7th poly---------------------------------------------------- + r0_avx = b[6 * L + 0]; + r1_avx = b[6 * L + 1]; + r2_avx = b[6 * L + 2]; + r3_avx = b[6 * L + 3]; + + b_eval[0 + 54] = r0_avx; + b_eval[1 + 54] = r1_avx; + b_eval[2 + 54] = r2_avx; + b_eval[3 + 54] = r3_avx; + b_eval[4 + 54] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 54] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 54] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 54] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 54] = _mm256_add_epi16(b_eval[6 + 54], b_eval[7 + 54]); + + //--------------Evaluating B poly ends------------------------------- + transpose(b_eval); + transpose(b_eval + 16); + transpose(b_eval + 32); + transpose(b_eval + 48); +} + +static void karatsuba_interp(__m256i *result_final0, __m256i *result_final1, __m256i *result_final2, __m256i *result_final3, __m256i *result_final4, __m256i *result_final5, __m256i *result_final6, const __m256i *c_eval) { + __m256i res_avx0, res_avx1, res_avx2, res_avx3, res_avx4, res_avx5, res_avx6, res_avx7; // to hold each 64X64 poly mul results + __m256i temp, c6_avx, c7_avx, c8_avx, c20_avx, c21_avx, c22_avx, c23_avx, c24_avx; + + //------------------------AVX interpolation for 1st poly external------------------- + res_avx0 = c_eval[0]; + res_avx2 = c_eval[1]; + res_avx4 = c_eval[2]; + res_avx6 = c_eval[3]; + c6_avx = c_eval[6]; + c7_avx = c_eval[7]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[8], c6_avx), c7_avx); + + res_avx1 = c_eval[16]; + res_avx3 = c_eval[17]; + res_avx5 = c_eval[18]; + res_avx7 = c_eval[19]; + c22_avx = c_eval[22]; + c23_avx = c_eval[23]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[21], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[24], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[20], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[5], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[4], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final0[0] = res_avx0; + result_final0[1] = res_avx1; + result_final0[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final0[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final0[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final0[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final0[6] = res_avx6; + result_final0[7] = res_avx7; + //------------------------AVX interpolation for 1st poly ends-------------- + + + //------------------------AVX interpolation for 2nd poly external------------------- + res_avx0 = c_eval[9]; //c_eval0 + res_avx2 = c_eval[10]; //c_eval1 + res_avx4 = c_eval[11]; //c_eval2 + res_avx6 = c_eval[12]; //c_eval3 + c6_avx = c_eval[15]; //c_eval6 + c7_avx = c_eval[32]; //c_eval7 + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[33], c6_avx), c7_avx); + + res_avx1 = c_eval[25]; //c_eval0 + res_avx3 = c_eval[26]; //c_eval1 + res_avx5 = c_eval[27]; //c_eval2 + res_avx7 = c_eval[28]; //c_eval3 + c22_avx = c_eval[31]; + c23_avx = c_eval[48]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[30], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[49], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[29], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[14], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[13], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final1[0] = res_avx0; + result_final1[1] = res_avx1; + result_final1[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final1[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final1[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final1[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final1[6] = res_avx6; + result_final1[7] = res_avx7; + //------------------------AVX interpolation for 2nd poly ends-------------- + + //------------------------AVX interpolation for 3rd poly external------------------- + res_avx0 = c_eval[34]; //c_eval0 + res_avx2 = c_eval[35]; //c_eval1 + res_avx4 = c_eval[36]; + res_avx6 = c_eval[37]; + c6_avx = c_eval[40]; + c7_avx = c_eval[41]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[42], c6_avx), c7_avx); + + res_avx1 = c_eval[50]; //c_eval0 + res_avx3 = c_eval[51]; //c_eval1 + res_avx5 = c_eval[52]; + res_avx7 = c_eval[53]; + c22_avx = c_eval[56]; + c23_avx = c_eval[57]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[55], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[58], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[54], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[39], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[38], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final2[0] = res_avx0; + result_final2[1] = res_avx1; + result_final2[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final2[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final2[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final2[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final2[6] = res_avx6; + result_final2[7] = res_avx7; + //------------------------AVX interpolation for 3rd poly ends-------------- + + //------------------------AVX interpolation for 4th poly external------------------- + res_avx0 = c_eval[43]; + res_avx2 = c_eval[44]; + res_avx4 = c_eval[45]; + res_avx6 = c_eval[46]; + c6_avx = c_eval[65]; + c7_avx = c_eval[66]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[67], c6_avx), c7_avx); + + res_avx1 = c_eval[59]; + res_avx3 = c_eval[60]; + res_avx5 = c_eval[61]; + res_avx7 = c_eval[62]; + c22_avx = c_eval[81]; + c23_avx = c_eval[82]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[80], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[83], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[63], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[64], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[47], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final3[0] = res_avx0; + result_final3[1] = res_avx1; + result_final3[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final3[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final3[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final3[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final3[6] = res_avx6; + result_final3[7] = res_avx7; + //------------------------AVX interpolation for 4th poly ends-------------- + + //------------------------AVX interpolation for 5th poly external------------------- + res_avx0 = c_eval[68]; + res_avx2 = c_eval[69]; + res_avx4 = c_eval[70]; + res_avx6 = c_eval[71]; + c6_avx = c_eval[74]; + c7_avx = c_eval[75]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[76], c6_avx), c7_avx); + + res_avx1 = c_eval[84]; + res_avx3 = c_eval[85]; + res_avx5 = c_eval[86]; + res_avx7 = c_eval[87]; + c22_avx = c_eval[90]; + c23_avx = c_eval[91]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[89], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[92], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[88], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[73], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[72], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final4[0] = res_avx0; + result_final4[1] = res_avx1; + result_final4[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final4[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final4[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final4[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final4[6] = res_avx6; + result_final4[7] = res_avx7; + //------------------------AVX interpolation for 5th poly ends-------------- + + //------------------------AVX interpolation for 6th poly external------------------- + res_avx0 = c_eval[77]; + res_avx2 = c_eval[78]; + res_avx4 = c_eval[79]; + res_avx6 = c_eval[96]; + c6_avx = c_eval[99]; + c7_avx = c_eval[100]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[101], c6_avx), c7_avx); + + res_avx1 = c_eval[93]; + res_avx3 = c_eval[94]; + res_avx5 = c_eval[95]; + res_avx7 = c_eval[112]; + c22_avx = c_eval[115]; + c23_avx = c_eval[116]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[114], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[117], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[113], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[98], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[97], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final5[0] = res_avx0; + result_final5[1] = res_avx1; + result_final5[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final5[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final5[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final5[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final5[6] = res_avx6; + result_final5[7] = res_avx7; + //------------------------AVX interpolation for 6th poly ends-------------- + + //------------------------AVX interpolation for 7th poly external------------------- + res_avx0 = c_eval[102]; + res_avx2 = c_eval[103]; + res_avx4 = c_eval[104]; + res_avx6 = c_eval[105]; + c6_avx = c_eval[108]; + c7_avx = c_eval[109]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[110], c6_avx), c7_avx); + + res_avx1 = c_eval[118]; + res_avx3 = c_eval[119]; + res_avx5 = c_eval[120]; + res_avx7 = c_eval[121]; + c22_avx = c_eval[124]; + c23_avx = c_eval[125]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[123], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[126], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[122], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[107], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[106], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final6[0] = res_avx0; + result_final6[1] = res_avx1; + result_final6[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final6[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final6[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final6[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final6[6] = res_avx6; + result_final6[7] = res_avx7; + //------------------------AVX interpolation for 7th poly ends-------------- +} + +void PQCLEAN_LIGHTSABER_AVX2_toom4_mul_A_by_B_eval(toom4_points_product *c_eval, const poly *a, const toom4_points *b_eval, int accumulate) { + size_t i; + __m256i r0_avx, r1_avx, r2_avx, r3_avx, r4_avx, r5_avx, r6_avx; + __m256i aw_avx[7 * L]; + __m256i *va = (__m256i *)a->coeffs; + + for (i = 0; i < L; i++) { + r0_avx = va[0 * L + i]; + r1_avx = va[1 * L + i]; + r2_avx = va[2 * L + i]; + r3_avx = va[3 * L + i]; + r4_avx = _mm256_add_epi16(r0_avx, r2_avx); + r5_avx = _mm256_add_epi16(r1_avx, r3_avx); + aw_avx[2 * L + i] = _mm256_add_epi16(r4_avx, r5_avx); + aw_avx[3 * L + i] = _mm256_sub_epi16(r4_avx, r5_avx); + r4_avx = _mm256_slli_epi16(r0_avx, 2); + r4_avx = _mm256_add_epi16(r4_avx, r2_avx); + r4_avx = _mm256_slli_epi16(r4_avx, 1); + r5_avx = _mm256_slli_epi16(r1_avx, 2); + r5_avx = _mm256_add_epi16(r5_avx, r3_avx); + aw_avx[4 * L + i] = _mm256_add_epi16(r4_avx, r5_avx); + aw_avx[5 * L + i] = _mm256_sub_epi16(r4_avx, r5_avx); + r4_avx = _mm256_slli_epi16(r3_avx, 3); + r6_avx = _mm256_slli_epi16(r2_avx, 2); + r4_avx = _mm256_add_epi16(r4_avx, r6_avx); + r6_avx = _mm256_slli_epi16(r1_avx, 1); + r4_avx = _mm256_add_epi16(r4_avx, r6_avx); + aw_avx[1 * L + i] = _mm256_add_epi16(r4_avx, r0_avx); + aw_avx[6 * L + i] = r0_avx; + aw_avx[0 * L + i] = r3_avx; + } + + batch_64coefficient_multiplications(c_eval, aw_avx, b_eval, accumulate); +} + +void PQCLEAN_LIGHTSABER_AVX2_toom4_eval(toom4_points *b_eval, const poly *b) { + size_t i; + __m256i bw_avx[7 * L]; + __m256i r0_avx, r1_avx, r2_avx, r3_avx, r4_avx, r5_avx, r6_avx; + __m256i *vb = (__m256i *)b->coeffs; + __m256i *vb_eval = (__m256i *)b_eval->coeffs; + + for (i = 0; i < L; i++) { + r0_avx = vb[0 * L + i]; + r1_avx = vb[1 * L + i]; + r2_avx = vb[2 * L + i]; + r3_avx = vb[3 * L + i]; + r4_avx = _mm256_add_epi16(r0_avx, r2_avx); + r5_avx = _mm256_add_epi16(r1_avx, r3_avx); + bw_avx[2 * L + i] = _mm256_add_epi16(r4_avx, r5_avx); + bw_avx[3 * L + i] = _mm256_sub_epi16(r4_avx, r5_avx); + r4_avx = _mm256_slli_epi16(r0_avx, 2); + r4_avx = _mm256_add_epi16(r4_avx, r2_avx); + r4_avx = _mm256_slli_epi16(r4_avx, 1); + r5_avx = _mm256_slli_epi16(r1_avx, 2); + r5_avx = _mm256_add_epi16(r5_avx, r3_avx); + bw_avx[4 * L + i] = _mm256_add_epi16(r4_avx, r5_avx); + bw_avx[5 * L + i] = _mm256_sub_epi16(r4_avx, r5_avx); + r4_avx = _mm256_slli_epi16(r3_avx, 3); + r6_avx = _mm256_slli_epi16(r2_avx, 2); + r4_avx = _mm256_add_epi16(r4_avx, r6_avx); + r6_avx = _mm256_slli_epi16(r1_avx, 1); + r4_avx = _mm256_add_epi16(r4_avx, r6_avx); + bw_avx[1 * L + i] = _mm256_add_epi16(r4_avx, r0_avx); + bw_avx[6 * L + i] = r0_avx; + bw_avx[0 * L + i] = r3_avx; + } + + karatsuba_eval(vb_eval, bw_avx); +} + + +void PQCLEAN_LIGHTSABER_AVX2_toom4_interp(poly *res, const toom4_points_product *c_eval) { + size_t i; + __m256i r0_avx, r1_avx, r2_avx, r3_avx, r4_avx, r5_avx, r6_avx, temp_avx; + __m256i w1_avx[2 * L], w2_avx[2 * L], w3_avx[2 * L], w4_avx[2 * L], w5_avx[2 * L], w6_avx[2 * L], w7_avx[2 * L]; + __m256i res_full[32]; + __m256i *vc = (__m256i *)c_eval->coeffs; + __m256i *vres = (__m256i *)res->coeffs; + + transpose(vc); + transpose(vc + 16); + transpose(vc + 32); + transpose(vc + 48); + transpose(vc + 64); + transpose(vc + 80); + transpose(vc + 96); + transpose(vc + 112); + + karatsuba_interp(w1_avx, w2_avx, w3_avx, w4_avx, w5_avx, w6_avx, w7_avx, vc); + + for (i = 0; i < 2 * L; i++) { + r0_avx = w1_avx[i]; + r1_avx = w2_avx[i]; + r2_avx = w3_avx[i]; + r3_avx = w4_avx[i]; + r4_avx = w5_avx[i]; + r5_avx = w6_avx[i]; + r6_avx = w7_avx[i]; + + r1_avx = _mm256_add_epi16(r1_avx, r4_avx); + r5_avx = _mm256_sub_epi16(r5_avx, r4_avx); + r3_avx = _mm256_sub_epi16(r3_avx, r2_avx); + r3_avx = _mm256_srli_epi16(r3_avx, 1); + r4_avx = _mm256_sub_epi16(r4_avx, r0_avx); + temp_avx = _mm256_slli_epi16(r6_avx, 6); + + r4_avx = _mm256_sub_epi16(r4_avx, temp_avx); + r4_avx = _mm256_slli_epi16(r4_avx, 1); + r4_avx = _mm256_add_epi16(r4_avx, r5_avx); + r2_avx = _mm256_add_epi16(r2_avx, r3_avx); + temp_avx = _mm256_slli_epi16(r2_avx, 6); + + r1_avx = _mm256_sub_epi16(r1_avx, temp_avx); + r1_avx = _mm256_sub_epi16(r1_avx, r2_avx); + r2_avx = _mm256_sub_epi16(r2_avx, r6_avx); + r2_avx = _mm256_sub_epi16(r2_avx, r0_avx); + temp_avx = _mm256_mullo_epi16(r2_avx, _mm256_set1_epi16(45)); + + r1_avx = _mm256_add_epi16(r1_avx, temp_avx); + temp_avx = _mm256_slli_epi16(r2_avx, 3); + + r4_avx = _mm256_sub_epi16(r4_avx, temp_avx); + r4_avx = _mm256_mullo_epi16(r4_avx, _mm256_set1_epi16(-21845)); // -21845 = 1/3 (mod 2^16) + r4_avx = _mm256_srli_epi16(r4_avx, 3); + r5_avx = _mm256_add_epi16(r5_avx, r1_avx); + temp_avx = _mm256_slli_epi16(r3_avx, 4); + + r1_avx = _mm256_add_epi16(r1_avx, temp_avx); + r1_avx = _mm256_mullo_epi16(r1_avx, _mm256_set1_epi16(-29127)); // -29127 = 1/9 (mod 2^16) + r1_avx = _mm256_srli_epi16(r1_avx, 1); + r3_avx = _mm256_add_epi16(r1_avx, r3_avx); + r3_avx = _mm256_sub_epi16(_mm256_set1_epi16(0), r3_avx); + temp_avx = _mm256_mullo_epi16(r1_avx, _mm256_set1_epi16(30)); + temp_avx = _mm256_sub_epi16(temp_avx, r5_avx); + temp_avx = _mm256_mullo_epi16(temp_avx, _mm256_set1_epi16(-4369)); // -4369 = 1/15 (mod 2^16) + + r5_avx = _mm256_srli_epi16(temp_avx, 2); + r2_avx = _mm256_sub_epi16(r2_avx, r4_avx); + r1_avx = _mm256_sub_epi16(r1_avx, r5_avx); + + if (i < L) { + res_full[0 * L + i] = r6_avx; + res_full[1 * L + i] = r5_avx; + res_full[2 * L + i] = r4_avx; + res_full[3 * L + i] = r3_avx; + res_full[4 * L + i] = r2_avx; + res_full[5 * L + i] = r1_avx; + res_full[6 * L + i] = r0_avx; + } else { + res_full[0 * L + i] = _mm256_add_epi16(res_full[0 * L + i], r6_avx); + res_full[1 * L + i] = _mm256_add_epi16(res_full[1 * L + i], r5_avx); + res_full[2 * L + i] = _mm256_add_epi16(res_full[2 * L + i], r4_avx); + res_full[3 * L + i] = _mm256_add_epi16(res_full[3 * L + i], r3_avx); + res_full[4 * L + i] = _mm256_add_epi16(res_full[4 * L + i], r2_avx); + res_full[5 * L + i] = _mm256_add_epi16(res_full[5 * L + i], r1_avx); + res_full[6 * L + i] = r0_avx; + } + } + + // Reduction by X^256 + 1 + for (i = 0; i < 16; i++) { + vres[i] = _mm256_sub_epi16(res_full[i], res_full[i + 16]); + } +} diff --git a/crypto_kem/lightsaber/avx2/verify.c b/crypto_kem/lightsaber/avx2/verify.c new file mode 100644 index 00000000..c2e5dc72 --- /dev/null +++ b/crypto_kem/lightsaber/avx2/verify.c @@ -0,0 +1,35 @@ +#include "verify.h" + +/*------------------------------------------------- +This file has been adapted from the implementation +(available at https://github.com/pq-crystals/kyber) of +"CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" + by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, +Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle +----------------------------------------------------*/ + + +/* returns 0 for equal strings, 1 for non-equal strings */ +uint8_t PQCLEAN_LIGHTSABER_AVX2_verify(const uint8_t *a, const uint8_t *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 (uint8_t) r; +} + +/* b = 1 means mov, b = 0 means don't mov*/ +void PQCLEAN_LIGHTSABER_AVX2_cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b) { + size_t i; + + b = -b; + for (i = 0; i < len; i++) { + r[i] ^= b & (x[i] ^ r[i]); + } +} diff --git a/crypto_kem/lightsaber/avx2/verify.h b/crypto_kem/lightsaber/avx2/verify.h new file mode 100644 index 00000000..f57ee9bc --- /dev/null +++ b/crypto_kem/lightsaber/avx2/verify.h @@ -0,0 +1,22 @@ +#ifndef VERIFY_H +#define VERIFY_H +/*------------------------------------------------- +This file has been adapted from the implementation +(available at https://github.com/pq-crystals/kyber) of +"CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" + by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, +Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle +----------------------------------------------------*/ + +#include +#include + +/* returns 0 for equal strings, 1 for non-equal strings */ +uint8_t PQCLEAN_LIGHTSABER_AVX2_verify(const uint8_t *a, const uint8_t *b, size_t len); + + +/* b = 1 means mov, b = 0 means don't mov*/ +void PQCLEAN_LIGHTSABER_AVX2_cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b); + + +#endif diff --git a/crypto_kem/lightsaber/clean/LICENSE b/crypto_kem/lightsaber/clean/LICENSE index 08c799e3..d5d21fff 100644 --- a/crypto_kem/lightsaber/clean/LICENSE +++ b/crypto_kem/lightsaber/clean/LICENSE @@ -1,8 +1 @@ ----------------------------------------------------------------------------------------- -SABER_v1.1 - -Public domain - -Authors: Jan-Pieter D'Anvers, Angshuman Karmakar, Sujoy Sinha Roy, -Frederik Vercauteren ----------------------------------------------------------------------------------------- +Public Domain diff --git a/crypto_kem/lightsaber/clean/Makefile b/crypto_kem/lightsaber/clean/Makefile index b1b532e4..160435dc 100644 --- a/crypto_kem/lightsaber/clean/Makefile +++ b/crypto_kem/lightsaber/clean/Makefile @@ -1,10 +1,10 @@ # This Makefile can be used with GNU Make or BSD Make LIB=liblightsaber_clean.a -HEADERS=api.h cbd.h poly.h poly_mul.h SABER_indcpa.h SABER_params.h verify.h pack_unpack.h +HEADERS=api.h cbd.h pack_unpack.h poly.h poly_mul.h SABER_indcpa.h SABER_params.h verify.h OBJECTS=cbd.o kem.o pack_unpack.o poly.o poly_mul.o SABER_indcpa.o verify.o -CFLAGS=-O3 -Wall -Wextra -Wpedantic -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) +CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wredundant-decls -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) all: $(LIB) diff --git a/crypto_kem/lightsaber/clean/SABER_indcpa.c b/crypto_kem/lightsaber/clean/SABER_indcpa.c index 20cf1de2..fac58484 100644 --- a/crypto_kem/lightsaber/clean/SABER_indcpa.c +++ b/crypto_kem/lightsaber/clean/SABER_indcpa.c @@ -3,296 +3,111 @@ #include "fips202.h" #include "pack_unpack.h" #include "poly.h" -#include "poly_mul.h" #include "randombytes.h" #include #include +#define h1 (1 << (SABER_EQ - SABER_EP - 1)) +#define h2 ((1 << (SABER_EP - 2)) - (1 << (SABER_EP - SABER_ET - 1)) + (1 << (SABER_EQ - SABER_EP - 1))) + +void PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_keypair(uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES], uint8_t sk[SABER_INDCPA_SECRETKEYBYTES]) { + size_t i, j; + + poly A[SABER_L][SABER_L]; + poly s[SABER_L]; + poly res[SABER_L]; + + uint8_t rand[SABER_NOISESEEDBYTES]; + uint8_t *seed_A = pk + SABER_POLYVECCOMPRESSEDBYTES; + + randombytes(seed_A, SABER_SEEDBYTES); + shake128(seed_A, SABER_SEEDBYTES, seed_A, SABER_SEEDBYTES); // for not revealing system RNG state + + randombytes(rand, SABER_NOISESEEDBYTES); + PQCLEAN_LIGHTSABER_CLEAN_GenSecret(s, rand); + PQCLEAN_LIGHTSABER_CLEAN_POLVECq2BS(sk, s); + + PQCLEAN_LIGHTSABER_CLEAN_GenMatrix(A, seed_A); // sample matrix A + PQCLEAN_LIGHTSABER_CLEAN_MatrixVectorMul(res, (const poly (*)[SABER_L])A, (const poly *)s, 1); // Matrix in transposed order -/*----------------------------------------------------------------------------------- - This routine generates a=[Matrix K x K] of 256-coefficient polynomials --------------------------------------------------------------------------------------*/ - -#define h1 4 //2^(EQ-EP-1) - -#define h2 ( (1<<(SABER_EP-2)) - (1<<(SABER_EP-SABER_ET-1)) + (1<<(SABER_EQ-SABER_EP-1)) ) - -static void InnerProd(uint16_t pkcl[SABER_K][SABER_N], uint16_t skpv[SABER_K][SABER_N], uint16_t mod, uint16_t res[SABER_N]); -static void MatrixVectorMul(polyvec *a, uint16_t skpv[SABER_K][SABER_N], uint16_t res[SABER_K][SABER_N], uint16_t mod, int16_t transpose); - -static void POL2MSG(const uint16_t *message_dec_unpacked, unsigned char *message_dec); - -static void GenMatrix(polyvec *a, const unsigned char *seed) { - unsigned char buf[SABER_K * SABER_K * (13 * SABER_N / 8)]; - - uint16_t temp_ar[SABER_N]; - - int i, j, k; - uint16_t mod = (SABER_Q - 1); - - shake128(buf, sizeof(buf), seed, SABER_SEEDBYTES); - - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_K; j++) { - PQCLEAN_LIGHTSABER_CLEAN_BS2POL(buf + (i * SABER_K + j) * (13 * SABER_N / 8), temp_ar); - for (k = 0; k < SABER_N; k++) { - a[i].vec[j].coeffs[k] = (temp_ar[k])& mod ; - } + // rounding + for (i = 0; i < SABER_L; i++) { + for (j = 0; j < SABER_N; j++) { + res[i].coeffs[j] += h1; + res[i].coeffs[j] >>= SABER_EQ - SABER_EP; + res[i].coeffs[j] &= SABER_Q - 1; } } + + PQCLEAN_LIGHTSABER_CLEAN_POLVECp2BS(pk, res); // pack public key } -void PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_keypair(unsigned char *pk, unsigned char *sk) { - polyvec a[SABER_K]; +void PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_enc(uint8_t ciphertext[SABER_BYTES_CCA_DEC], const uint8_t m[SABER_KEYBYTES], const uint8_t noiseseed[SABER_NOISESEEDBYTES], const uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES]) { + size_t i, j; - uint16_t skpv[SABER_K][SABER_N]; + poly A[SABER_L][SABER_L]; + poly res[SABER_L]; + poly s[SABER_L]; + poly *temp = A[0]; // re-use stack space + poly *vprime = &A[0][0]; + poly *message = &A[0][1]; - unsigned char seed[SABER_SEEDBYTES]; - unsigned char noiseseed[SABER_COINBYTES]; - int32_t i, j; - uint16_t mod_q = SABER_Q - 1; + const uint8_t *seed_A = pk + SABER_POLYVECCOMPRESSEDBYTES; + uint8_t *msk_c = ciphertext + SABER_POLYVECCOMPRESSEDBYTES; + + PQCLEAN_LIGHTSABER_CLEAN_GenSecret(s, noiseseed); + PQCLEAN_LIGHTSABER_CLEAN_GenMatrix(A, seed_A); + PQCLEAN_LIGHTSABER_CLEAN_MatrixVectorMul(res, (const poly (*)[SABER_L])A, (const poly *)s, 0); // 0 => not transposed - uint16_t res[SABER_K][SABER_N]; - - randombytes(seed, SABER_SEEDBYTES); - - // for not revealing system RNG state - shake128(seed, SABER_SEEDBYTES, seed, SABER_SEEDBYTES); - randombytes(noiseseed, SABER_COINBYTES); - - GenMatrix(a, seed); //sample matrix A - - // generate secret from constant-time binomial distribution - PQCLEAN_LIGHTSABER_CLEAN_GenSecret(skpv, noiseseed); - - // do the matrix vector multiplication and rounding - for (i = 0; i < SABER_K; i++) { + // rounding + for (i = 0; i < SABER_L; i++) { //shift right EQ-EP bits for (j = 0; j < SABER_N; j++) { - res[i][j] = 0; - } - } - MatrixVectorMul(a, skpv, res, SABER_Q - 1, 1); - - // now rounding - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - // shift right 3 bits - res[i][j] = (res[i][j] + h1) & (mod_q); - res[i][j] = (res[i][j] >> (SABER_EQ - SABER_EP)); - } - } - - // unload and pack sk=3 x (256 coefficients of 14 bits) - PQCLEAN_LIGHTSABER_CLEAN_POLVEC2BS(sk, skpv, SABER_Q); - - // unload and pack pk=256 bits seed and 3 x (256 coefficients of 11 bits) - // load the public-key coefficients - PQCLEAN_LIGHTSABER_CLEAN_POLVEC2BS(pk, res, SABER_P); - - - // now load the seedbytes in PK. Easy since seed bytes are kept in byte format. - for (i = 0; i < SABER_SEEDBYTES; i++) { - pk[SABER_POLYVECCOMPRESSEDBYTES + i] = seed[i]; - } - -} - - -void PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_enc(const unsigned char *message_received, unsigned char *noiseseed, const unsigned char *pk, unsigned char *ciphertext) { - uint32_t i, j, k; - polyvec a[SABER_K]; - unsigned char seed[SABER_SEEDBYTES]; - // public key of received by the client - uint16_t pkcl[SABER_K][SABER_N]; - uint16_t skpv1[SABER_K][SABER_N]; - uint16_t message[SABER_KEYBYTES * 8]; - uint16_t res[SABER_K][SABER_N]; - uint16_t mod_p = SABER_P - 1; - uint16_t mod_q = SABER_Q - 1; - uint16_t vprime[SABER_N]; - unsigned char msk_c[SABER_SCALEBYTES_KEM]; - - // extract the seedbytes from Public Key. - for (i = 0; i < SABER_SEEDBYTES; i++) { - seed[i] = pk[ SABER_POLYVECCOMPRESSEDBYTES + i]; - } - - GenMatrix(a, seed); - - // generate secret from constant-time binomial distribution - PQCLEAN_LIGHTSABER_CLEAN_GenSecret(skpv1, noiseseed); - - // matrix-vector multiplication and rounding - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - res[i][j] = 0; - } - } - MatrixVectorMul(a, skpv1, res, SABER_Q - 1, 0); - - // now rounding - //shift right 3 bits - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - res[i][j] = ( res[i][j] + h1 ) & mod_q; - res[i][j] = (res[i][j] >> (SABER_EQ - SABER_EP) ); - } - } - - PQCLEAN_LIGHTSABER_CLEAN_POLVEC2BS(ciphertext, res, SABER_P); - - // ************client matrix-vector multiplication ends************ - - // now calculate the v' - // unpack the public_key - // pkcl is the b in the protocol - PQCLEAN_LIGHTSABER_CLEAN_BS2POLVEC(pk, pkcl, SABER_P); - for (i = 0; i < SABER_N; i++) { - vprime[i] = 0; - } - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - skpv1[i][j] = skpv1[i][j] & (mod_p); + res[i].coeffs[j] += h1; + res[i].coeffs[j] >>= SABER_EQ - SABER_EP; + res[i].coeffs[j] &= SABER_Q - 1; } } + PQCLEAN_LIGHTSABER_CLEAN_POLVECp2BS(ciphertext, res); // vector-vector scalar multiplication with mod p - InnerProd(pkcl, skpv1, mod_p, vprime); + PQCLEAN_LIGHTSABER_CLEAN_BS2POLVECp(temp, pk); + PQCLEAN_LIGHTSABER_CLEAN_InnerProd(vprime, temp, s); + PQCLEAN_LIGHTSABER_CLEAN_BS2POLmsg(message, m); - // addition of h1 to vprime for (i = 0; i < SABER_N; i++) { - vprime[i] = vprime[i] + h1; + vprime->coeffs[i] += h1 - (message->coeffs[i] << (SABER_EP - 1)); + vprime->coeffs[i] &= SABER_P - 1; + vprime->coeffs[i] >>= SABER_EP - SABER_ET; } - // unpack message_received; - for (j = 0; j < SABER_KEYBYTES; j++) { - for (i = 0; i < 8; i++) { - message[8 * j + i] = ((message_received[j] >> i) & 0x01); - } - } + PQCLEAN_LIGHTSABER_CLEAN_POLT2BS(msk_c, vprime); +} + + +void PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_dec(uint8_t m[SABER_KEYBYTES], const uint8_t sk[SABER_INDCPA_SECRETKEYBYTES], const uint8_t ciphertext[SABER_BYTES_CCA_DEC]) { + size_t i; + + poly temp[SABER_L]; + poly s[SABER_L]; + + const uint8_t *packed_cm = ciphertext + SABER_POLYVECCOMPRESSEDBYTES; + poly *v = &temp[0]; + poly *cm = &temp[1]; + + PQCLEAN_LIGHTSABER_CLEAN_BS2POLVECq(s, sk); + PQCLEAN_LIGHTSABER_CLEAN_BS2POLVECp(temp, ciphertext); + PQCLEAN_LIGHTSABER_CLEAN_InnerProd(&temp[0], temp, s); + + PQCLEAN_LIGHTSABER_CLEAN_BS2POLT(cm, packed_cm); - // message encoding for (i = 0; i < SABER_N; i++) { - message[i] = (message[i] << (SABER_EP - 1)); + v->coeffs[i] += h2 - (cm->coeffs[i] << (SABER_EP - SABER_ET)); + v->coeffs[i] &= SABER_P - 1; + v->coeffs[i] >>= SABER_EP - 1; } - for (k = 0; k < SABER_N; k++) { - vprime[k] = ( (vprime[k] - message[k]) & (mod_p) ) >> (SABER_EP - SABER_ET); - } - - - PQCLEAN_LIGHTSABER_CLEAN_pack_3bit(msk_c, vprime); - - for (j = 0; j < SABER_SCALEBYTES_KEM; j++) { - ciphertext[SABER_POLYVECCOMPRESSEDBYTES + j] = msk_c[j]; - } -} - - -void PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_dec(const unsigned char *sk, const unsigned char *ciphertext, unsigned char message_dec[]) { - uint32_t i, j; - // secret key of the server - uint16_t sksv[SABER_K][SABER_N]; - uint16_t pksv[SABER_K][SABER_N]; - uint8_t scale_ar[SABER_SCALEBYTES_KEM]; - uint16_t mod_p = SABER_P - 1; - uint16_t v[SABER_N]; - uint16_t op[SABER_N]; - - // sksv is the secret-key - PQCLEAN_LIGHTSABER_CLEAN_BS2POLVEC(sk, sksv, SABER_Q); - // pksv is the ciphertext - PQCLEAN_LIGHTSABER_CLEAN_BS2POLVEC(ciphertext, pksv, SABER_P); - - // vector-vector scalar multiplication with mod p - for (i = 0; i < SABER_N; i++) { - v[i] = 0; - } - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - sksv[i][j] = sksv[i][j] & (mod_p); - } - } - InnerProd(pksv, sksv, mod_p, v); - - //Extraction - for (i = 0; i < SABER_SCALEBYTES_KEM; i++) { - scale_ar[i] = ciphertext[SABER_POLYVECCOMPRESSEDBYTES + i]; - } - - PQCLEAN_LIGHTSABER_CLEAN_un_pack3bit(scale_ar, op); - - //addition of h1 - for (i = 0; i < SABER_N; i++) { - v[i] = ( ( v[i] + h2 - (op[i] << (SABER_EP - SABER_ET)) ) & (mod_p) ) >> (SABER_EP - 1); - } - - // pack decrypted message - POL2MSG(v, message_dec); -} -static void MatrixVectorMul(polyvec *a, uint16_t skpv[SABER_K][SABER_N], uint16_t res[SABER_K][SABER_N], uint16_t mod, int16_t transpose) { - uint16_t acc[SABER_N]; - int32_t i, j, k; - - if (transpose == 1) { - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_K; j++) { - PQCLEAN_LIGHTSABER_CLEAN_pol_mul((uint16_t *)&a[j].vec[i], skpv[j], acc, SABER_Q, SABER_N); - - for (k = 0; k < SABER_N; k++) { - res[i][k] = res[i][k] + acc[k]; - //reduction mod p - res[i][k] = (res[i][k] & mod); - //clear the accumulator - acc[k] = 0; - } - } - } - } else { - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_K; j++) { - PQCLEAN_LIGHTSABER_CLEAN_pol_mul((uint16_t *)&a[i].vec[j], skpv[j], acc, SABER_Q, SABER_N); - for (k = 0; k < SABER_N; k++) { - res[i][k] = res[i][k] + acc[k]; - // reduction - res[i][k] = res[i][k] & mod; - // clear the accumulator - acc[k] = 0; - } - } - } - } -} - -static void POL2MSG(const uint16_t *message_dec_unpacked, unsigned char *message_dec) { - int32_t i, j; - - for (j = 0; j < SABER_KEYBYTES; j++) { - message_dec[j] = 0; - for (i = 0; i < 8; i++) { - message_dec[j] = message_dec[j] | (uint8_t) (message_dec_unpacked[j * 8 + i] << i); - } - } -} - - -static void InnerProd(uint16_t pkcl[SABER_K][SABER_N], uint16_t skpv[SABER_K][SABER_N], uint16_t mod, uint16_t res[SABER_N]) { - uint32_t j, k; - uint16_t acc[SABER_N]; - - // vector-vector scalar multiplication with mod p - for (j = 0; j < SABER_K; j++) { - PQCLEAN_LIGHTSABER_CLEAN_pol_mul(pkcl[j], skpv[j], acc, SABER_P, SABER_N); - - for (k = 0; k < SABER_N; k++) { - res[k] = res[k] + acc[k]; - // reduction - res[k] = res[k] & mod; - // clear the accumulator - acc[k] = 0; - } - } + PQCLEAN_LIGHTSABER_CLEAN_POLmsg2BS(m, v); } diff --git a/crypto_kem/lightsaber/clean/SABER_indcpa.h b/crypto_kem/lightsaber/clean/SABER_indcpa.h index 4f806c55..df8906ab 100644 --- a/crypto_kem/lightsaber/clean/SABER_indcpa.h +++ b/crypto_kem/lightsaber/clean/SABER_indcpa.h @@ -1,9 +1,13 @@ #ifndef INDCPA_H #define INDCPA_H +#include "SABER_params.h" +#include + +void PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_keypair(uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES], uint8_t sk[SABER_INDCPA_SECRETKEYBYTES]); + +void PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_enc(uint8_t ciphertext[SABER_BYTES_CCA_DEC], const uint8_t m[SABER_KEYBYTES], const uint8_t noiseseed[SABER_NOISESEEDBYTES], const uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES]); + +void PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_dec(uint8_t m[SABER_KEYBYTES], const uint8_t sk[SABER_INDCPA_SECRETKEYBYTES], const uint8_t ciphertext[SABER_BYTES_CCA_DEC]); -void PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_keypair(unsigned char *pk, unsigned char *sk); -void PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_enc(const unsigned char *message, unsigned char *noiseseed, const unsigned char *pk, unsigned char *ciphertext); -void PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_dec(const unsigned char *sk, const unsigned char *ciphertext, unsigned char *message_dec); #endif - diff --git a/crypto_kem/lightsaber/clean/SABER_params.h b/crypto_kem/lightsaber/clean/SABER_params.h index eb3825f2..8da6ec34 100644 --- a/crypto_kem/lightsaber/clean/SABER_params.h +++ b/crypto_kem/lightsaber/clean/SABER_params.h @@ -1,50 +1,41 @@ #ifndef PARAMS_H #define PARAMS_H -#include "api.h" -#define SABER_K 2 +/* Don't change anything below this line */ +#define SABER_L 2 #define SABER_MU 10 #define SABER_ET 3 +#define SABER_N 256 + +#define SABER_EP 10 +#define SABER_P (1 << SABER_EP) #define SABER_EQ 13 -#define SABER_EP 10 +#define SABER_Q (1 << SABER_EQ) -#define SABER_N 256 -#define SABER_Q 8192 -#define SABER_P 1024 +#define SABER_SEEDBYTES 32 +#define SABER_NOISESEEDBYTES 32 +#define SABER_KEYBYTES 32 +#define SABER_HASHBYTES 32 -#define SABER_SEEDBYTES 32 -#define SABER_NOISESEEDBYTES 32 -#define SABER_COINBYTES 32 -#define SABER_KEYBYTES 32 +#define SABER_POLYCOINBYTES (SABER_MU * SABER_N / 8) -#define SABER_HASHBYTES 32 +#define SABER_POLYBYTES (SABER_EQ * SABER_N / 8) +#define SABER_POLYVECBYTES (SABER_L * SABER_POLYBYTES) -#define SABER_POLYBYTES 416 //13*256/8 +#define SABER_POLYCOMPRESSEDBYTES (SABER_EP * SABER_N / 8) +#define SABER_POLYVECCOMPRESSEDBYTES (SABER_L * SABER_POLYCOMPRESSEDBYTES) -#define SABER_POLYVECBYTES (SABER_K * SABER_POLYBYTES) - -#define SABER_POLYVECCOMPRESSEDBYTES (SABER_K * 320) //10*256/8 NOTE : changed till here due to parameter adaptation - -#define SABER_CIPHERTEXTBYTES (SABER_POLYVECCOMPRESSEDBYTES) - -#define SABER_SCALEBYTES (SABER_DELTA*SABER_N/8) - -#define SABER_SCALEBYTES_KEM ((SABER_ET)*SABER_N/8) +#define SABER_SCALEBYTES_KEM (SABER_ET * SABER_N / 8) #define SABER_INDCPA_PUBLICKEYBYTES (SABER_POLYVECCOMPRESSEDBYTES + SABER_SEEDBYTES) #define SABER_INDCPA_SECRETKEYBYTES (SABER_POLYVECBYTES) #define SABER_PUBLICKEYBYTES (SABER_INDCPA_PUBLICKEYBYTES) +#define SABER_SECRETKEYBYTES (SABER_INDCPA_SECRETKEYBYTES + SABER_INDCPA_PUBLICKEYBYTES + SABER_HASHBYTES + SABER_KEYBYTES) -#define SABER_SECRETKEYBYTES (SABER_INDCPA_SECRETKEYBYTES + SABER_INDCPA_PUBLICKEYBYTES + SABER_HASHBYTES + SABER_KEYBYTES) - -#define SABER_BYTES_CCA_DEC (SABER_POLYVECCOMPRESSEDBYTES + SABER_SCALEBYTES_KEM) /* Second part is for Targhi-Unruh */ - - - +#define SABER_BYTES_CCA_DEC (SABER_POLYVECCOMPRESSEDBYTES + SABER_SCALEBYTES_KEM) #endif - diff --git a/crypto_kem/lightsaber/clean/api.h b/crypto_kem/lightsaber/clean/api.h index 4f73c035..2e39ae02 100644 --- a/crypto_kem/lightsaber/clean/api.h +++ b/crypto_kem/lightsaber/clean/api.h @@ -1,14 +1,18 @@ #ifndef PQCLEAN_LIGHTSABER_CLEAN_API_H #define PQCLEAN_LIGHTSABER_CLEAN_API_H + #define PQCLEAN_LIGHTSABER_CLEAN_CRYPTO_ALGNAME "LightSaber" -#define PQCLEAN_LIGHTSABER_CLEAN_CRYPTO_SECRETKEYBYTES 1568 -#define PQCLEAN_LIGHTSABER_CLEAN_CRYPTO_PUBLICKEYBYTES (2*320+32) #define PQCLEAN_LIGHTSABER_CLEAN_CRYPTO_BYTES 32 #define PQCLEAN_LIGHTSABER_CLEAN_CRYPTO_CIPHERTEXTBYTES 736 +#define PQCLEAN_LIGHTSABER_CLEAN_CRYPTO_PUBLICKEYBYTES 672 +#define PQCLEAN_LIGHTSABER_CLEAN_CRYPTO_SECRETKEYBYTES 1568 int PQCLEAN_LIGHTSABER_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned char *sk); -int PQCLEAN_LIGHTSABER_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk); -int PQCLEAN_LIGHTSABER_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk); -#endif /* api_h */ +int PQCLEAN_LIGHTSABER_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *k, const unsigned char *pk); + +int PQCLEAN_LIGHTSABER_CLEAN_crypto_kem_dec(unsigned char *k, const unsigned char *ct, const unsigned char *sk); + + +#endif /* PQCLEAN_LIGHTSABER_CLEAN_API_H */ diff --git a/crypto_kem/lightsaber/clean/cbd.c b/crypto_kem/lightsaber/clean/cbd.c index f6ebe4d7..7e3f2be2 100644 --- a/crypto_kem/lightsaber/clean/cbd.c +++ b/crypto_kem/lightsaber/clean/cbd.c @@ -1,3 +1,7 @@ +#include "SABER_params.h" +#include "api.h" +#include "cbd.h" +#include /*--------------------------------------------------------------------- This file has been adapted from the implementation (available at, Public Domain https://github.com/pq-crystals/kyber) @@ -6,12 +10,8 @@ by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle ----------------------------------------------------------------------*/ -#include "SABER_params.h" -#include "api.h" -#include "cbd.h" -#include -static uint64_t load_littleendian(const unsigned char *x, int bytes) { +static uint64_t load_littleendian(const uint8_t *x, int bytes) { int i; uint64_t r = x[0]; for (i = 1; i < bytes; i++) { @@ -20,10 +20,7 @@ static uint64_t load_littleendian(const unsigned char *x, int bytes) { return r; } - -void PQCLEAN_LIGHTSABER_CLEAN_cbd(uint16_t *r, const unsigned char *buf) { - uint16_t Qmod_minus1 = SABER_Q - 1; - +void PQCLEAN_LIGHTSABER_CLEAN_cbd(uint16_t s[SABER_N], const uint8_t buf[SABER_POLYCOINBYTES]) { uint64_t t, d, a[4], b[4]; int i, j; @@ -34,8 +31,8 @@ void PQCLEAN_LIGHTSABER_CLEAN_cbd(uint16_t *r, const unsigned char *buf) { d += (t >> j) & 0x0842108421UL; } - a[0] = d & 0x1f; - b[0] = (d >> 5) & 0x1f; + a[0] = d & 0x1f; + b[0] = (d >> 5) & 0x1f; a[1] = (d >> 10) & 0x1f; b[1] = (d >> 15) & 0x1f; a[2] = (d >> 20) & 0x1f; @@ -43,9 +40,9 @@ void PQCLEAN_LIGHTSABER_CLEAN_cbd(uint16_t *r, const unsigned char *buf) { a[3] = (d >> 30) & 0x1f; b[3] = (d >> 35); - r[4 * i + 0] = (uint16_t)(a[0] - b[0]) & Qmod_minus1; - r[4 * i + 1] = (uint16_t)(a[1] - b[1]) & Qmod_minus1; - r[4 * i + 2] = (uint16_t)(a[2] - b[2]) & Qmod_minus1; - r[4 * i + 3] = (uint16_t)(a[3] - b[3]) & Qmod_minus1; + s[4 * i + 0] = (uint16_t)(a[0] - b[0]); + s[4 * i + 1] = (uint16_t)(a[1] - b[1]); + s[4 * i + 2] = (uint16_t)(a[2] - b[2]); + s[4 * i + 3] = (uint16_t)(a[3] - b[3]); } } diff --git a/crypto_kem/lightsaber/clean/cbd.h b/crypto_kem/lightsaber/clean/cbd.h index 37553425..dffd4dc5 100644 --- a/crypto_kem/lightsaber/clean/cbd.h +++ b/crypto_kem/lightsaber/clean/cbd.h @@ -1,6 +1,5 @@ #ifndef CBD_H #define CBD_H - /*--------------------------------------------------------------------- This file has been adapted from the implementation (available at, Public Domain https://github.com/pq-crystals/kyber) @@ -8,10 +7,10 @@ of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle ----------------------------------------------------------------------*/ - -#include "poly.h" +#include "SABER_params.h" #include -void PQCLEAN_LIGHTSABER_CLEAN_cbd(uint16_t *r, const unsigned char *buf); +void PQCLEAN_LIGHTSABER_CLEAN_cbd(uint16_t s[SABER_N], const uint8_t buf[SABER_POLYCOINBYTES]); + #endif diff --git a/crypto_kem/lightsaber/clean/kem.c b/crypto_kem/lightsaber/clean/kem.c index 8aad4302..d0a67736 100644 --- a/crypto_kem/lightsaber/clean/kem.c +++ b/crypto_kem/lightsaber/clean/kem.c @@ -1,96 +1,77 @@ #include "SABER_indcpa.h" #include "SABER_params.h" +#include "api.h" #include "fips202.h" #include "randombytes.h" #include "verify.h" +#include #include -#include -#include -int PQCLEAN_LIGHTSABER_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned char *sk) { - int i; - // sk[0:SABER_INDCPA_SECRETKEYBYTES-1] <-- sk - PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_keypair(pk, sk); +int PQCLEAN_LIGHTSABER_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + size_t i; - // sk[SABER_INDCPA_SECRETKEYBYTES:SABER_INDCPA_SECRETKEYBYTES+SABER_INDCPA_SECRETKEYBYTES-1] <-- pk + PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_keypair(pk, sk); // sk[0:SABER_INDCPA_SECRETKEYBYTES-1] <-- sk for (i = 0; i < SABER_INDCPA_PUBLICKEYBYTES; i++) { - sk[i + SABER_INDCPA_SECRETKEYBYTES] = pk[i]; + sk[i + SABER_INDCPA_SECRETKEYBYTES] = pk[i]; // sk[SABER_INDCPA_SECRETKEYBYTES:SABER_INDCPA_SECRETKEYBYTES+SABER_INDCPA_SECRETKEYBYTES-1] <-- pk } - // Then hash(pk) is appended. - sha3_256(sk + SABER_SECRETKEYBYTES - 64, pk, SABER_INDCPA_PUBLICKEYBYTES); + sha3_256(sk + SABER_SECRETKEYBYTES - 64, pk, SABER_INDCPA_PUBLICKEYBYTES); // Then hash(pk) is appended. - // Remaining part of sk contains a pseudo-random number. - // This is output when check in crypto_kem_dec() fails. - randombytes(sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES ); + randombytes(sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES); // Remaining part of sk contains a pseudo-random number. + // This is output when check in PQCLEAN_LIGHTSABER_CLEAN_crypto_kem_dec() fails. return (0); } -int PQCLEAN_LIGHTSABER_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk) { - // Will contain key, coins - unsigned char kr[64]; - unsigned char buf[64]; +int PQCLEAN_LIGHTSABER_CLEAN_crypto_kem_enc(uint8_t *c, uint8_t *k, const uint8_t *pk) { + + uint8_t kr[64]; // Will contain key, coins + uint8_t buf[64]; randombytes(buf, 32); - // BUF[0:31] <-- random message (will be used as the key for client) Note: hash doesnot release system RNG output - sha3_256(buf, buf, 32); + sha3_256(buf, buf, 32); // BUF[0:31] <-- random message (will be used as the key for client) Note: hash doesnot release system RNG output - // BUF[32:63] <-- Hash(public key); Multitarget countermeasure for coins + contributory KEM - sha3_256(buf + 32, pk, SABER_INDCPA_PUBLICKEYBYTES); - - // kr[0:63] <-- Hash(buf[0:63]); - sha3_512(kr, buf, 64); + sha3_256(buf + 32, pk, SABER_INDCPA_PUBLICKEYBYTES); // BUF[32:63] <-- Hash(public key); Multitarget countermeasure for coins + contributory KEM + sha3_512(kr, buf, 64); // kr[0:63] <-- Hash(buf[0:63]); // K^ <-- kr[0:31] // noiseseed (r) <-- kr[32:63]; - // buf[0:31] contains message; kr[32:63] contains randomness r; - PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_enc(buf, kr + 32, pk, ct); + PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_enc(c, buf, kr + 32, pk); // buf[0:31] contains message; kr[32:63] contains randomness r; - sha3_256(kr + 32, ct, SABER_BYTES_CCA_DEC); + sha3_256(kr + 32, c, SABER_BYTES_CCA_DEC); - // hash concatenation of pre-k and h(c) to k - sha3_256(ss, kr, 64); + sha3_256(k, kr, 64); // hash concatenation of pre-k and h(c) to k return (0); } +int PQCLEAN_LIGHTSABER_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, const uint8_t *sk) { + size_t i; + uint8_t fail; + uint8_t cmp[SABER_BYTES_CCA_DEC]; + uint8_t buf[64]; + uint8_t kr[64]; // Will contain key, coins + const uint8_t *pk = sk + SABER_INDCPA_SECRETKEYBYTES; -int PQCLEAN_LIGHTSABER_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk) { - int i; - unsigned char fail; - unsigned char cmp[SABER_BYTES_CCA_DEC]; - unsigned char buf[64]; - - // Will contain key, coins - unsigned char kr[64]; - const unsigned char *pk = sk + SABER_INDCPA_SECRETKEYBYTES; - - // buf[0:31] <-- message - PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_dec(sk, ct, buf); - + PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_dec(buf, sk, c); // buf[0:31] <-- message // Multitarget countermeasure for coins + contributory KEM - // Save hash by storing h(pk) in sk - for (i = 0; i < 32; i++) { + for (i = 0; i < 32; i++) { // Save hash by storing h(pk) in sk buf[32 + i] = sk[SABER_SECRETKEYBYTES - 64 + i]; } sha3_512(kr, buf, 64); - PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_enc(buf, kr + 32, pk, cmp); + PQCLEAN_LIGHTSABER_CLEAN_indcpa_kem_enc(cmp, buf, kr + 32, pk); + fail = PQCLEAN_LIGHTSABER_CLEAN_verify(c, cmp, SABER_BYTES_CCA_DEC); - fail = PQCLEAN_LIGHTSABER_CLEAN_verify(ct, cmp, SABER_BYTES_CCA_DEC); - - // overwrite coins in kr with h(c) - sha3_256(kr + 32, ct, SABER_BYTES_CCA_DEC); + sha3_256(kr + 32, c, SABER_BYTES_CCA_DEC); // overwrite coins in kr with h(c) PQCLEAN_LIGHTSABER_CLEAN_cmov(kr, sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES, fail); - // hash concatenation of pre-k and h(c) to k - sha3_256(ss, kr, 64); + sha3_256(k, kr, 64); // hash concatenation of pre-k and h(c) to k return (0); } diff --git a/crypto_kem/lightsaber/clean/pack_unpack.c b/crypto_kem/lightsaber/clean/pack_unpack.c index 4b1c409f..106a62d4 100644 --- a/crypto_kem/lightsaber/clean/pack_unpack.c +++ b/crypto_kem/lightsaber/clean/pack_unpack.c @@ -1,254 +1,155 @@ +#include "SABER_params.h" #include "pack_unpack.h" +#include "poly.h" +#include -void PQCLEAN_LIGHTSABER_CLEAN_pack_3bit(uint8_t *bytes, const uint16_t *data) { - uint32_t j; - uint32_t offset_data, offset_byte; - +void PQCLEAN_LIGHTSABER_CLEAN_POLT2BS(uint8_t bytes[SABER_SCALEBYTES_KEM], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; for (j = 0; j < SABER_N / 8; j++) { - offset_byte = 3 * j; - offset_data = 8 * j; - bytes[offset_byte + 0] = (data[offset_data + 0] & 0x7) | - ((data[offset_data + 1] & 0x7) << 3) | - ((data[offset_data + 2] & 0x3) << 6); - bytes[offset_byte + 1] = ((data[offset_data + 2] >> 2 ) & 0x01) | - ((data[offset_data + 3] & 0x7) << 1) | - ((data[offset_data + 4] & 0x7) << 4) | - (((data[offset_data + 5]) & 0x01) << 7); - bytes[offset_byte + 2] = ((data[offset_data + 5] >> 1 ) & 0x03) | - ((data[offset_data + 6] & 0x7) << 2) | - ((data[offset_data + 7] & 0x7) << 5); + out[0] = (uint8_t) ((in[0] & 0x7) | ((in[1] & 0x7) << 3) | (in[2] << 6)); + out[1] = (uint8_t) (((in[2] >> 2) & 0x01) | ((in[3] & 0x7) << 1) | ((in[4] & 0x7) << 4) | (in[5] << 7)); + out[2] = (uint8_t) (((in[5] >> 1) & 0x03) | ((in[6] & 0x7) << 2) | (in[7] << 5)); + in += 8; + out += 3; } } -void PQCLEAN_LIGHTSABER_CLEAN_un_pack3bit(const uint8_t *bytes, uint16_t *data) { - uint32_t j; - uint32_t offset_data, offset_byte; - +void PQCLEAN_LIGHTSABER_CLEAN_BS2POLT(poly *data, const uint8_t bytes[SABER_SCALEBYTES_KEM]) { + /* This function does not reduce its output mod T */ + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; for (j = 0; j < SABER_N / 8; j++) { - offset_byte = 3 * j; - offset_data = 8 * j; - data[offset_data + 0] = (bytes[offset_byte + 0]) & 0x07; - data[offset_data + 1] = ((bytes[offset_byte + 0]) >> 3 ) & 0x07; - data[offset_data + 2] = (((bytes[offset_byte + 0]) >> 6 ) & 0x03) | - (((bytes[offset_byte + 1]) & 0x01) << 2); - data[offset_data + 3] = ((bytes[offset_byte + 1]) >> 1 ) & 0x07; - data[offset_data + 4] = ((bytes[offset_byte + 1]) >> 4 ) & 0x07; - data[offset_data + 5] = (((bytes[offset_byte + 1]) >> 7 ) & 0x01) | - (((bytes[offset_byte + 2]) & 0x03) << 1); - data[offset_data + 6] = ((bytes[offset_byte + 2] >> 2) & 0x07); - data[offset_data + 7] = ((bytes[offset_byte + 2] >> 5) & 0x07); + out[0] = in[0]; + out[1] = in[0] >> 3; + out[2] = (in[0] >> 6) | (in[1] << 2); + out[3] = in[1] >> 1; + out[4] = in[1] >> 4; + out[5] = (in[1] >> 7) | (in[2] << 1); + out[6] = in[2] >> 2; + out[7] = in[2] >> 5; + in += 3; + out += 8; } } -void PQCLEAN_LIGHTSABER_CLEAN_pack_4bit(uint8_t *bytes, const uint16_t *data) { - uint32_t j; - uint32_t offset_data; - - for (j = 0; j < SABER_N / 2; j++) { - offset_data = 2 * j; - bytes[j] = (data[offset_data] & 0x0f) | - ((data[offset_data + 1] & 0x0f) << 4); +static void POLq2BS(uint8_t bytes[SABER_POLYBYTES], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; + for (j = 0; j < SABER_N / 8; j++) { + out[0] = (uint8_t) (in[0]); + out[1] = (uint8_t) (((in[0] >> 8) & 0x1f) | (in[1] << 5)); + out[2] = (uint8_t) (in[1] >> 3); + out[3] = (uint8_t) (((in[1] >> 11) & 0x03) | (in[2] << 2)); + out[4] = (uint8_t) (((in[2] >> 6) & 0x7f) | (in[3] << 7)); + out[5] = (uint8_t) (in[3] >> 1); + out[6] = (uint8_t) (((in[3] >> 9) & 0x0f) | (in[4] << 4)); + out[7] = (uint8_t) (in[4] >> 4); + out[8] = (uint8_t) (((in[4] >> 12) & 0x01) | (in[5] << 1)); + out[9] = (uint8_t) (((in[5] >> 7) & 0x3f) | (in[6] << 6)); + out[10] = (uint8_t) (in[6] >> 2); + out[11] = (uint8_t) (((in[6] >> 10) & 0x07) | (in[7] << 3)); + out[12] = (uint8_t) (in[7] >> 5); + in += 8; + out += 13; } } -void PQCLEAN_LIGHTSABER_CLEAN_un_pack4bit(const unsigned char *bytes, uint16_t *ar) { - uint32_t j; - uint32_t offset_data; - - for (j = 0; j < SABER_N / 2; j++) { - offset_data = 2 * j; - ar[offset_data] = bytes[j] & 0x0f; - ar[offset_data + 1] = (bytes[j] >> 4) & 0x0f; +static void BS2POLq(poly *data, const uint8_t bytes[SABER_POLYBYTES]) { + /* This function does not reduce its output mod Q */ + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; + for (j = 0; j < SABER_N / 8; j++) { + out[0] = (in[0]) | (in[1] << 8); + out[1] = (in[1] >> 5) | (in[2] << 3) | (in[3] << 11); + out[2] = (in[3] >> 2) | (in[4] << 6); + out[3] = (in[4] >> 7) | (in[5] << 1) | (in[6] << 9); + out[4] = (in[6] >> 4) | (in[7] << 4) | (in[8] << 12); + out[5] = (in[8] >> 1) | (in[9] << 7); + out[6] = (in[9] >> 6) | (in[10] << 2) | (in[11] << 10); + out[7] = (in[11] >> 3) | (in[12] << 5); + in += 13; + out += 8; } } -void PQCLEAN_LIGHTSABER_CLEAN_pack_6bit(uint8_t *bytes, const uint16_t *data) { - uint32_t j; - uint32_t offset_data, offset_byte; - +static void POLp2BS(uint8_t bytes[SABER_POLYCOMPRESSEDBYTES], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; for (j = 0; j < SABER_N / 4; j++) { - offset_byte = 3 * j; - offset_data = 4 * j; - bytes[offset_byte + 0] = (data[offset_data + 0] & 0x3f) | - ((data[offset_data + 1] & 0x03) << 6); - bytes[offset_byte + 1] = ((data[offset_data + 1] >> 2) & 0x0f) | - ((data[offset_data + 2] & 0x0f) << 4); - bytes[offset_byte + 2] = ((data[offset_data + 2] >> 4) & 0x03) | - ((data[offset_data + 3] & 0x3f) << 2); + out[0] = (uint8_t) (in[0]); + out[1] = (uint8_t) (((in[0] >> 8) & 0x03) | (in[1] << 2)); + out[2] = (uint8_t) (((in[1] >> 6) & 0x0f) | (in[2] << 4)); + out[3] = (uint8_t) (((in[2] >> 4) & 0x3f) | (in[3] << 6)); + out[4] = (uint8_t) (in[3] >> 2); + in += 4; + out += 5; } } - -void PQCLEAN_LIGHTSABER_CLEAN_un_pack6bit(const unsigned char *bytes, uint16_t *data) { - uint32_t j; - uint32_t offset_data, offset_byte; - +static void BS2POLp(poly *data, const uint8_t bytes[SABER_POLYCOMPRESSEDBYTES]) { + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; for (j = 0; j < SABER_N / 4; j++) { - offset_byte = 3 * j; - offset_data = 4 * j; - data[offset_data + 0] = bytes[offset_byte + 0] & 0x3f; - data[offset_data + 1] = ((bytes[offset_byte + 0] >> 6) & 0x03) | - ((bytes[offset_byte + 1] & 0x0f) << 2); - data[offset_data + 2] = ((bytes[offset_byte + 1] & 0xff) >> 4) | - ((bytes[offset_byte + 2] & 0x03) << 4); - data[offset_data + 3] = ((bytes[offset_byte + 2] & 0xff) >> 2); + out[0] = in[0] | (in[1] << 8); + out[1] = (in[1] >> 2) | (in[2] << 6); + out[2] = (in[2] >> 4) | (in[3] << 4); + out[3] = (in[3] >> 6) | (in[4] << 2); + in += 5; + out += 4; } } +void PQCLEAN_LIGHTSABER_CLEAN_POLVECq2BS(uint8_t bytes[SABER_POLYVECBYTES], const poly data[SABER_L]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + POLq2BS(bytes + i * SABER_POLYBYTES, &data[i]); + } +} -static void POLVECp2BS(uint8_t *bytes, uint16_t data[SABER_K][SABER_N]) { - uint32_t i, j; - uint32_t offset_data, offset_byte, offset_byte1; +void PQCLEAN_LIGHTSABER_CLEAN_BS2POLVECq(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECBYTES]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + BS2POLq(&data[i], bytes + i * SABER_POLYBYTES); + } +} - for (i = 0; i < SABER_K; i++) { - offset_byte1 = i * (SABER_N * 10) / 8; - for (j = 0; j < SABER_N / 4; j++) { - offset_byte = offset_byte1 + 5 * j; - offset_data = 4 * j; - bytes[offset_byte + 0] = (data[i][offset_data + 0] & (0xff)); - bytes[offset_byte + 1] = ((data[i][offset_data + 0] >> 8) & 0x03) | - ((data[i][offset_data + 1] & 0x3f) << 2); - bytes[offset_byte + 2] = ((data[i][offset_data + 1] >> 6) & 0x0f) | - ((data[i][offset_data + 2] & 0x0f) << 4); - bytes[offset_byte + 3] = ((data[i][offset_data + 2] >> 4) & 0x3f) | - ((data[i][offset_data + 3] & 0x03) << 6); - bytes[offset_byte + 4] = ((data[i][offset_data + 3] >> 2) & 0xff); +void PQCLEAN_LIGHTSABER_CLEAN_POLVECp2BS(uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES], const poly data[SABER_L]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + POLp2BS(bytes + i * SABER_POLYCOMPRESSEDBYTES, &data[i]); + } +} + +void PQCLEAN_LIGHTSABER_CLEAN_BS2POLVECp(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + BS2POLp(&data[i], bytes + i * SABER_POLYCOMPRESSEDBYTES); + } +} + +void PQCLEAN_LIGHTSABER_CLEAN_BS2POLmsg(poly *data, const uint8_t bytes[SABER_KEYBYTES]) { + size_t i, j; + for (j = 0; j < SABER_KEYBYTES; j++) { + for (i = 0; i < 8; i++) { + data->coeffs[j * 8 + i] = ((bytes[j] >> i) & 0x01); } } } -static void BS2POLVECp(const unsigned char *bytes, uint16_t data[SABER_K][SABER_N]) { - uint32_t i, j; - uint32_t offset_data, offset_byte, offset_byte1; +void PQCLEAN_LIGHTSABER_CLEAN_POLmsg2BS(uint8_t bytes[SABER_KEYBYTES], const poly *data) { + size_t i, j; + memset(bytes, 0, SABER_KEYBYTES); - for (i = 0; i < SABER_K; i++) { - offset_byte1 = i * (SABER_N * 10) / 8; - for (j = 0; j < SABER_N / 4; j++) { - offset_byte = offset_byte1 + 5 * j; - offset_data = 4 * j; - data[i][offset_data + 0] = (bytes[offset_byte + 0] & (0xff)) | - ((bytes[offset_byte + 1] & 0x03) << 8); - data[i][offset_data + 1] = ((bytes[offset_byte + 1] >> 2) & (0x3f)) | - ((bytes[offset_byte + 2] & 0x0f) << 6); - data[i][offset_data + 2] = ((bytes[offset_byte + 2] >> 4) & (0x0f)) | - ((bytes[offset_byte + 3] & 0x3f) << 4); - data[i][offset_data + 3] = ((bytes[offset_byte + 3] >> 6) & (0x03)) | - ((bytes[offset_byte + 4] & 0xff) << 2); + for (j = 0; j < SABER_KEYBYTES; j++) { + for (i = 0; i < 8; i++) { + bytes[j] = bytes[j] | ((data->coeffs[j * 8 + i] & 0x01) << i); } } } - - - -static void POLVECq2BS(uint8_t *bytes, uint16_t data[SABER_K][SABER_N]) { - uint32_t i, j; - uint32_t offset_data, offset_byte, offset_byte1; - - for (i = 0; i < SABER_K; i++) { - offset_byte1 = i * (SABER_N * 13) / 8; - for (j = 0; j < SABER_N / 8; j++) { - offset_byte = offset_byte1 + 13 * j; - offset_data = 8 * j; - bytes[offset_byte + 0] = (data[i][offset_data + 0] & (0xff)); - bytes[offset_byte + 1] = ((data[i][offset_data + 0] >> 8) & 0x1f) | - ((data[i][offset_data + 1] & 0x07) << 5); - bytes[offset_byte + 2] = ((data[i][offset_data + 1] >> 3) & 0xff); - bytes[offset_byte + 3] = ((data[i][offset_data + 1] >> 11) & 0x03) | - ((data[i][offset_data + 2] & 0x3f) << 2); - bytes[offset_byte + 4] = ((data[i][offset_data + 2] >> 6) & 0x7f) | - ((data[i][offset_data + 3] & 0x01) << 7); - bytes[offset_byte + 5] = ((data[i][offset_data + 3] >> 1) & 0xff); - bytes[offset_byte + 6] = ((data[i][offset_data + 3] >> 9) & 0x0f) | - ((data[i][offset_data + 4] & 0x0f) << 4); - bytes[offset_byte + 7] = ((data[i][offset_data + 4] >> 4) & 0xff); - bytes[offset_byte + 8] = ((data[i][offset_data + 4] >> 12) & 0x01) | - ((data[i][offset_data + 5] & 0x7f) << 1); - bytes[offset_byte + 9] = ((data[i][offset_data + 5] >> 7) & 0x3f) | - ((data[i][offset_data + 6] & 0x03) << 6); - bytes[offset_byte + 10] = ((data[i][offset_data + 6] >> 2) & 0xff); - bytes[offset_byte + 11] = ((data[i][offset_data + 6] >> 10) & 0x07) | - ((data[i][offset_data + 7] & 0x1f) << 3); - bytes[offset_byte + 12] = ((data[i][offset_data + 7] >> 5) & 0xff); - } - } -} - -static void BS2POLVECq(const unsigned char *bytes, uint16_t data[SABER_K][SABER_N]) { - uint32_t i, j; - uint32_t offset_data, offset_byte, offset_byte1; - - for (i = 0; i < SABER_K; i++) { - offset_byte1 = i * (SABER_N * 13) / 8; - for (j = 0; j < SABER_N / 8; j++) { - offset_byte = offset_byte1 + 13 * j; - offset_data = 8 * j; - data[i][offset_data + 0] = (bytes[offset_byte + 0] & (0xff)) | - ((bytes[offset_byte + 1] & 0x1f) << 8); - data[i][offset_data + 1] = (bytes[offset_byte + 1] >> 5 & (0x07)) | - ((bytes[offset_byte + 2] & 0xff) << 3) | - ((bytes[offset_byte + 3] & 0x03) << 11); - data[i][offset_data + 2] = (bytes[offset_byte + 3] >> 2 & (0x3f)) | - ((bytes[offset_byte + 4] & 0x7f) << 6); - data[i][offset_data + 3] = (bytes[offset_byte + 4] >> 7 & (0x01)) | - ((bytes[offset_byte + 5] & 0xff) << 1) | - ((bytes[offset_byte + 6] & 0x0f) << 9); - data[i][offset_data + 4] = (bytes[offset_byte + 6] >> 4 & (0x0f)) | - ((bytes[offset_byte + 7] & 0xff) << 4) | - ((bytes[offset_byte + 8] & 0x01) << 12); - data[i][offset_data + 5] = (bytes[offset_byte + 8] >> 1 & (0x7f)) | - ((bytes[offset_byte + 9] & 0x3f) << 7); - data[i][offset_data + 6] = (bytes[offset_byte + 9] >> 6 & (0x03)) | - ((bytes[offset_byte + 10] & 0xff) << 2) | - ((bytes[offset_byte + 11] & 0x07) << 10); - data[i][offset_data + 7] = (bytes[offset_byte + 11] >> 3 & (0x1f)) | - ((bytes[offset_byte + 12] & 0xff) << 5); - } - } -} - -//only BS2POLq no BS2POLp -void PQCLEAN_LIGHTSABER_CLEAN_BS2POL(const unsigned char *bytes, uint16_t data[SABER_N]) { - uint32_t j; - uint32_t offset_data, offset_byte; - - for (j = 0; j < SABER_N / 8; j++) { - offset_byte = 13 * j; - offset_data = 8 * j; - data[offset_data + 0] = (bytes[offset_byte + 0] & (0xff)) | - ((bytes[offset_byte + 1] & 0x1f) << 8); - data[offset_data + 1] = (bytes[offset_byte + 1] >> 5 & (0x07)) | - ((bytes[offset_byte + 2] & 0xff) << 3) | - ((bytes[offset_byte + 3] & 0x03) << 11); - data[offset_data + 2] = (bytes[offset_byte + 3] >> 2 & (0x3f)) | - ((bytes[offset_byte + 4] & 0x7f) << 6); - data[offset_data + 3] = (bytes[offset_byte + 4] >> 7 & (0x01)) | - ((bytes[offset_byte + 5] & 0xff) << 1) | - ((bytes[offset_byte + 6] & 0x0f) << 9); - data[offset_data + 4] = (bytes[offset_byte + 6] >> 4 & (0x0f)) | - ((bytes[offset_byte + 7] & 0xff) << 4) | - ((bytes[offset_byte + 8] & 0x01) << 12); - data[offset_data + 5] = (bytes[offset_byte + 8] >> 1 & (0x7f)) | - ((bytes[offset_byte + 9] & 0x3f) << 7); - data[offset_data + 6] = (bytes[offset_byte + 9] >> 6 & (0x03)) | - ((bytes[offset_byte + 10] & 0xff) << 2) | - ((bytes[offset_byte + 11] & 0x07) << 10); - data[offset_data + 7] = (bytes[offset_byte + 11] >> 3 & (0x1f)) | - ((bytes[offset_byte + 12] & 0xff) << 5); - } -} - -void PQCLEAN_LIGHTSABER_CLEAN_POLVEC2BS(uint8_t *bytes, uint16_t data[SABER_K][SABER_N], uint16_t modulus) { - if (modulus == 1024) { - POLVECp2BS(bytes, data); - } else if (modulus == 8192) { - POLVECq2BS(bytes, data); - } -} - -void PQCLEAN_LIGHTSABER_CLEAN_BS2POLVEC(const unsigned char *bytes, uint16_t data[SABER_K][SABER_N], uint16_t modulus) { - if (modulus == 1024) { - BS2POLVECp(bytes, data); - } else if (modulus == 8192) { - BS2POLVECq(bytes, data); - } -} diff --git a/crypto_kem/lightsaber/clean/pack_unpack.h b/crypto_kem/lightsaber/clean/pack_unpack.h index 86fd2fad..0eda3392 100644 --- a/crypto_kem/lightsaber/clean/pack_unpack.h +++ b/crypto_kem/lightsaber/clean/pack_unpack.h @@ -1,28 +1,28 @@ #ifndef PACK_UNPACK_H #define PACK_UNPACK_H - #include "SABER_params.h" +#include "poly.h" #include #include +void PQCLEAN_LIGHTSABER_CLEAN_POLT2BS(uint8_t bytes[SABER_SCALEBYTES_KEM], const poly *data); -void PQCLEAN_LIGHTSABER_CLEAN_pack_3bit(uint8_t *bytes, const uint16_t *data); - -void PQCLEAN_LIGHTSABER_CLEAN_un_pack3bit(const uint8_t *bytes, uint16_t *data); - -void PQCLEAN_LIGHTSABER_CLEAN_pack_4bit(uint8_t *bytes, const uint16_t *data); - -void PQCLEAN_LIGHTSABER_CLEAN_un_pack4bit(const unsigned char *bytes, uint16_t *ar); - -void PQCLEAN_LIGHTSABER_CLEAN_pack_6bit(uint8_t *bytes, const uint16_t *data); - -void PQCLEAN_LIGHTSABER_CLEAN_un_pack6bit(const unsigned char *bytes, uint16_t *data); +void PQCLEAN_LIGHTSABER_CLEAN_BS2POLT(poly *data, const uint8_t bytes[SABER_SCALEBYTES_KEM]); -void PQCLEAN_LIGHTSABER_CLEAN_BS2POL(const unsigned char *bytes, uint16_t data[SABER_N]); +void PQCLEAN_LIGHTSABER_CLEAN_POLVECq2BS(uint8_t bytes[SABER_POLYVECBYTES], const poly data[SABER_L]); -void PQCLEAN_LIGHTSABER_CLEAN_POLVEC2BS(uint8_t *bytes, uint16_t data[SABER_K][SABER_N], uint16_t modulus); +void PQCLEAN_LIGHTSABER_CLEAN_POLVECp2BS(uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES], const poly data[SABER_L]); + + +void PQCLEAN_LIGHTSABER_CLEAN_BS2POLVECq(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECBYTES]); + +void PQCLEAN_LIGHTSABER_CLEAN_BS2POLVECp(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES]); + + +void PQCLEAN_LIGHTSABER_CLEAN_BS2POLmsg(poly *data, const uint8_t bytes[SABER_KEYBYTES]); + +void PQCLEAN_LIGHTSABER_CLEAN_POLmsg2BS(uint8_t bytes[SABER_KEYBYTES], const poly *data); -void PQCLEAN_LIGHTSABER_CLEAN_BS2POLVEC(const unsigned char *bytes, uint16_t data[SABER_K][SABER_N], uint16_t modulus); #endif diff --git a/crypto_kem/lightsaber/clean/poly.c b/crypto_kem/lightsaber/clean/poly.c index fc86ab3c..e5be857f 100644 --- a/crypto_kem/lightsaber/clean/poly.c +++ b/crypto_kem/lightsaber/clean/poly.c @@ -1,21 +1,57 @@ -/*--------------------------------------------------------------------- -This file has been adapted from the implementation -(available at, Public Domain https://github.com/pq-crystals/kyber) -of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" -by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, -Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle -----------------------------------------------------------------------*/ -#include "SABER_params.h" +#include "api.h" #include "cbd.h" #include "fips202.h" +#include "pack_unpack.h" #include "poly.h" +#include -void PQCLEAN_LIGHTSABER_CLEAN_GenSecret(uint16_t r[SABER_K][SABER_N], const unsigned char *seed) { - uint8_t buf[SABER_MU * SABER_N * SABER_K / 8]; +void PQCLEAN_LIGHTSABER_CLEAN_MatrixVectorMul(poly c[SABER_L], const poly A[SABER_L][SABER_L], const poly s[SABER_L], int16_t transpose) { + size_t i, j; + + if (transpose) { + for (i = 0; i < SABER_L; i++) { + PQCLEAN_LIGHTSABER_CLEAN_poly_mul(&c[i], &A[0][i], &s[0], 0); + for (j = 1; j < SABER_L; j++) { + PQCLEAN_LIGHTSABER_CLEAN_poly_mul(&c[i], &A[j][i], &s[j], 1); + } + } + } else { + for (i = 0; i < SABER_L; i++) { + PQCLEAN_LIGHTSABER_CLEAN_poly_mul(&c[i], &A[i][0], &s[0], 0); + for (j = 1; j < SABER_L; j++) { + PQCLEAN_LIGHTSABER_CLEAN_poly_mul(&c[i], &A[i][j], &s[j], 1); + } + } + } +} + +void PQCLEAN_LIGHTSABER_CLEAN_InnerProd(poly *c, const poly b[SABER_L], const poly s[SABER_L]) { + size_t i; + + PQCLEAN_LIGHTSABER_CLEAN_poly_mul(c, &b[0], &s[0], 0); + for (i = 1; i < SABER_L; i++) { + PQCLEAN_LIGHTSABER_CLEAN_poly_mul(c, &b[i], &s[i], 1); + } +} + +void PQCLEAN_LIGHTSABER_CLEAN_GenMatrix(poly A[SABER_L][SABER_L], const uint8_t seed[SABER_SEEDBYTES]) { + size_t i; + uint8_t buf[SABER_L * SABER_POLYVECBYTES]; + + shake128(buf, sizeof(buf), seed, SABER_SEEDBYTES); + + for (i = 0; i < SABER_L; i++) { + PQCLEAN_LIGHTSABER_CLEAN_BS2POLVECq(A[i], buf + i * SABER_POLYVECBYTES); + } +} + +void PQCLEAN_LIGHTSABER_CLEAN_GenSecret(poly s[SABER_L], const uint8_t seed[SABER_NOISESEEDBYTES]) { + size_t i; + uint8_t buf[SABER_L * SABER_POLYCOINBYTES]; shake128(buf, sizeof(buf), seed, SABER_NOISESEEDBYTES); - for (size_t i = 0; i < SABER_K; i++) { - PQCLEAN_LIGHTSABER_CLEAN_cbd(r[i], buf + i * SABER_MU * SABER_N / 8); + for (i = 0; i < SABER_L; i++) { + PQCLEAN_LIGHTSABER_CLEAN_cbd(s[i].coeffs, buf + i * SABER_POLYCOINBYTES); } } diff --git a/crypto_kem/lightsaber/clean/poly.h b/crypto_kem/lightsaber/clean/poly.h index 47ceeebb..be074e43 100644 --- a/crypto_kem/lightsaber/clean/poly.h +++ b/crypto_kem/lightsaber/clean/poly.h @@ -1,26 +1,23 @@ #ifndef POLY_H #define POLY_H - -/*--------------------------------------------------------------------- -This file has been adapted from the implementation -(available at, Public Domain https://github.com/pq-crystals/kyber) -of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" -by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, -Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle -----------------------------------------------------------------------*/ - - #include "SABER_params.h" #include -typedef struct { +typedef union { uint16_t coeffs[SABER_N]; } poly; -typedef struct { - poly vec[SABER_K]; -} polyvec; -void PQCLEAN_LIGHTSABER_CLEAN_GenSecret(uint16_t r[SABER_K][SABER_N], const unsigned char *seed); +void PQCLEAN_LIGHTSABER_CLEAN_MatrixVectorMul(poly c[SABER_L], const poly A[SABER_L][SABER_L], const poly s[SABER_L], int16_t transpose); + +void PQCLEAN_LIGHTSABER_CLEAN_InnerProd(poly *c, const poly b[SABER_L], const poly s[SABER_L]); + +void PQCLEAN_LIGHTSABER_CLEAN_GenMatrix(poly A[SABER_L][SABER_L], const uint8_t seed[SABER_SEEDBYTES]); + +void PQCLEAN_LIGHTSABER_CLEAN_GenSecret(poly s[SABER_L], const uint8_t seed[SABER_NOISESEEDBYTES]); + + +void PQCLEAN_LIGHTSABER_CLEAN_poly_mul(poly *c, const poly *a, const poly *b, int accumulate); + #endif diff --git a/crypto_kem/lightsaber/clean/poly_mul.c b/crypto_kem/lightsaber/clean/poly_mul.c index 926910b5..d82d8585 100644 --- a/crypto_kem/lightsaber/clean/poly_mul.c +++ b/crypto_kem/lightsaber/clean/poly_mul.c @@ -1,4 +1,4 @@ -#include "poly_mul.h" +#include "poly.h" #include #include @@ -11,13 +11,13 @@ #define OVERFLOWING_MUL(X, Y) ((uint16_t)((uint32_t)(X) * (uint32_t)(Y))) #define KARATSUBA_N 64 -static void karatsuba_simple(const uint16_t *a_1, const uint16_t *b_1, uint16_t *result_final) { +static void karatsuba_simple(uint16_t *result_final, const uint16_t *a_1, const uint16_t *b_1) { uint16_t d01[KARATSUBA_N / 2 - 1]; uint16_t d0123[KARATSUBA_N / 2 - 1]; uint16_t d23[KARATSUBA_N / 2 - 1]; uint16_t result_d01[KARATSUBA_N - 1]; - int32_t i, j; + size_t i, j; memset(result_d01, 0, (KARATSUBA_N - 1)*sizeof(uint16_t)); memset(d01, 0, (KARATSUBA_N / 2 - 1)*sizeof(uint16_t)); @@ -110,7 +110,7 @@ static void karatsuba_simple(const uint16_t *a_1, const uint16_t *b_1, uint16_t -static void toom_cook_4way (const uint16_t *a1, const uint16_t *b1, uint16_t *result) { +static void toom_cook_4way (uint16_t *result, const uint16_t *a1, const uint16_t *b1) { uint16_t inv3 = 43691, inv9 = 36409, inv15 = 61167; uint16_t aw1[N_SB], aw2[N_SB], aw3[N_SB], aw4[N_SB], aw5[N_SB], aw6[N_SB], aw7[N_SB]; @@ -181,13 +181,13 @@ static void toom_cook_4way (const uint16_t *a1, const uint16_t *b1, uint16_t *re // MULTIPLICATION - karatsuba_simple(aw1, bw1, w1); - karatsuba_simple(aw2, bw2, w2); - karatsuba_simple(aw3, bw3, w3); - karatsuba_simple(aw4, bw4, w4); - karatsuba_simple(aw5, bw5, w5); - karatsuba_simple(aw6, bw6, w6); - karatsuba_simple(aw7, bw7, w7); + karatsuba_simple(w1, aw1, bw1); + karatsuba_simple(w2, aw2, bw2); + karatsuba_simple(w3, aw3, bw3); + karatsuba_simple(w4, aw4, bw4); + karatsuba_simple(w5, aw5, bw5); + karatsuba_simple(w6, aw6, bw6); + karatsuba_simple(w7, aw7, bw7); // INTERPOLATION for (i = 0; i < N_SB_RES; ++i) { @@ -228,19 +228,21 @@ static void toom_cook_4way (const uint16_t *a1, const uint16_t *b1, uint16_t *re } } -void PQCLEAN_LIGHTSABER_CLEAN_pol_mul(uint16_t *a, uint16_t *b, uint16_t *res, uint16_t p, uint32_t n) { - uint32_t i; - // normal multiplication - uint16_t c[512]; +/* res += a*b */ +void PQCLEAN_LIGHTSABER_CLEAN_poly_mul(poly *c, const poly *a, const poly *b, const int accumulate) { + uint16_t C[2 * SABER_N] = {0}; + size_t i; - for (i = 0; i < 512; i++) { - c[i] = 0; - } + toom_cook_4way(C, a->coeffs, b->coeffs); - toom_cook_4way(a, b, c); - - // reduction - for (i = n; i < 2 * n; i++) { - res[i - n] = (c[i - n] - c[i]) & (p - 1); + /* reduction */ + if (accumulate == 0) { + for (i = SABER_N; i < 2 * SABER_N; i++) { + c->coeffs[i - SABER_N] = (C[i - SABER_N] - C[i]); + } + } else { + for (i = SABER_N; i < 2 * SABER_N; i++) { + c->coeffs[i - SABER_N] += (C[i - SABER_N] - C[i]); + } } } diff --git a/crypto_kem/lightsaber/clean/poly_mul.h b/crypto_kem/lightsaber/clean/poly_mul.h index 8d634584..b28b04f6 100644 --- a/crypto_kem/lightsaber/clean/poly_mul.h +++ b/crypto_kem/lightsaber/clean/poly_mul.h @@ -1,9 +1,3 @@ -#ifndef POLYMUL_H -#define POLYMUL_H -#include "SABER_params.h" -#include -void PQCLEAN_LIGHTSABER_CLEAN_pol_mul(uint16_t *a, uint16_t *b, uint16_t *res, uint16_t p, uint32_t n); -#endif diff --git a/crypto_kem/lightsaber/clean/verify.c b/crypto_kem/lightsaber/clean/verify.c index 52c6969b..05e564da 100644 --- a/crypto_kem/lightsaber/clean/verify.c +++ b/crypto_kem/lightsaber/clean/verify.c @@ -1,3 +1,5 @@ +#include "verify.h" + /*------------------------------------------------- This file has been adapted from the implementation (available at https://github.com/pq-crystals/kyber) of @@ -5,26 +7,25 @@ This file has been adapted from the implementation by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle ----------------------------------------------------*/ -#include "verify.h" -#include + /* returns 0 for equal strings, 1 for non-equal strings */ -unsigned char PQCLEAN_LIGHTSABER_CLEAN_verify(const unsigned char *a, const unsigned char *b, size_t len) { +uint8_t PQCLEAN_LIGHTSABER_CLEAN_verify(const uint8_t *a, const uint8_t *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; + return (uint8_t) r; } /* b = 1 means mov, b = 0 means don't mov*/ -void PQCLEAN_LIGHTSABER_CLEAN_cmov(unsigned char *r, const unsigned char *x, size_t len, unsigned char b) { +void PQCLEAN_LIGHTSABER_CLEAN_cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b) { size_t i; b = -b; diff --git a/crypto_kem/lightsaber/clean/verify.h b/crypto_kem/lightsaber/clean/verify.h index 32c2adb5..4f538e6f 100644 --- a/crypto_kem/lightsaber/clean/verify.h +++ b/crypto_kem/lightsaber/clean/verify.h @@ -1,6 +1,5 @@ #ifndef VERIFY_H #define VERIFY_H - /*------------------------------------------------- This file has been adapted from the implementation (available at https://github.com/pq-crystals/kyber) of @@ -13,9 +12,11 @@ Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle #include /* returns 0 for equal strings, 1 for non-equal strings */ -unsigned char PQCLEAN_LIGHTSABER_CLEAN_verify(const unsigned char *a, const unsigned char *b, size_t len); +uint8_t PQCLEAN_LIGHTSABER_CLEAN_verify(const uint8_t *a, const uint8_t *b, size_t len); + /* b = 1 means mov, b = 0 means don't mov*/ -void PQCLEAN_LIGHTSABER_CLEAN_cmov(unsigned char *r, const unsigned char *x, size_t len, unsigned char b); +void PQCLEAN_LIGHTSABER_CLEAN_cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b); + #endif diff --git a/crypto_kem/saber/META.yml b/crypto_kem/saber/META.yml index 4de4f1c8..742d77c5 100644 --- a/crypto_kem/saber/META.yml +++ b/crypto_kem/saber/META.yml @@ -14,4 +14,13 @@ principal-submitters: - Frederik Vercauteren implementations: - name: clean - version: https://github.com/KULeuven-COSIC/SABER/commit/14ede83f1ff3bcc41f0464543542366c68b55871 + version: https://github.com/KULeuven-COSIC/SABER/tree/509cc5ec3a7e12a751ccdd2ef5bd6e54e00bd350 via https://github.com/jschanck/package-pqclean/tree/1ae84c3c/saber + - name: avx2 + version: https://github.com/KULeuven-COSIC/SABER/tree/509cc5ec3a7e12a751ccdd2ef5bd6e54e00bd350 via https://github.com/jschanck/package-pqclean/tree/1ae84c3c/saber + supported_platforms: + - architecture: x86_64 + operating_systems: + - Linux + - Darwin + required_flags: + - avx2 diff --git a/crypto_kem/saber/avx2/LICENSE b/crypto_kem/saber/avx2/LICENSE new file mode 100644 index 00000000..d5d21fff --- /dev/null +++ b/crypto_kem/saber/avx2/LICENSE @@ -0,0 +1 @@ +Public Domain diff --git a/crypto_kem/saber/avx2/Makefile b/crypto_kem/saber/avx2/Makefile new file mode 100644 index 00000000..41ea6101 --- /dev/null +++ b/crypto_kem/saber/avx2/Makefile @@ -0,0 +1,22 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsaber_avx2.a +HEADERS=api.h cbd.h pack_unpack.h poly.h SABER_indcpa.h SABER_params.h verify.h +OBJECTS=cbd.o kem.o pack_unpack.o poly.o poly_mul.o SABER_indcpa.o verify.o + +CFLAGS=-O3 -mavx2 -Wall -Wextra -Wpedantic -Wvla -Werror -Wredundant-decls -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.s $(HEADERS) + $(AS) -o $@ $< + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_kem/saber/avx2/SABER_indcpa.c b/crypto_kem/saber/avx2/SABER_indcpa.c new file mode 100644 index 00000000..e01eb650 --- /dev/null +++ b/crypto_kem/saber/avx2/SABER_indcpa.c @@ -0,0 +1,125 @@ +#include "SABER_indcpa.h" +#include "SABER_params.h" +#include "fips202.h" +#include "pack_unpack.h" +#include "poly.h" +#include "randombytes.h" +#include +#include + +#define h1 (1 << (SABER_EQ - SABER_EP - 1)) +#define h2 ((1 << (SABER_EP - 2)) - (1 << (SABER_EP - SABER_ET - 1)) + (1 << (SABER_EQ - SABER_EP - 1))) + +void PQCLEAN_SABER_AVX2_indcpa_kem_keypair(uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES], uint8_t sk[SABER_INDCPA_SECRETKEYBYTES]) { + size_t i, j; + + poly A[SABER_L][SABER_L]; + poly *skpv1 = A[0]; // use first row of A to hold sk temporarily + toom4_points skpv1_eval[SABER_L]; + poly res[SABER_L]; + + uint8_t rand[SABER_NOISESEEDBYTES]; + uint8_t *seed_A = pk + SABER_POLYVECCOMPRESSEDBYTES; + + randombytes(seed_A, SABER_SEEDBYTES); + shake128(seed_A, SABER_SEEDBYTES, seed_A, SABER_SEEDBYTES); // for not revealing system RNG state + + randombytes(rand, SABER_NOISESEEDBYTES); + PQCLEAN_SABER_AVX2_GenSecret(skpv1, rand); + PQCLEAN_SABER_AVX2_POLVECq2BS(sk, skpv1); // pack secret key + + for (j = 0; j < SABER_L; j++) { + PQCLEAN_SABER_AVX2_toom4_eval(&skpv1_eval[j], &skpv1[j]); + } + + PQCLEAN_SABER_AVX2_GenMatrix(A, seed_A); // sample matrix A + PQCLEAN_SABER_AVX2_MatrixVectorMul(res, (const poly (*)[SABER_L])A, (const toom4_points *)skpv1_eval, 1); // Matrix in transposed order + + // rounding + for (i = 0; i < SABER_L; i++) { + for (j = 0; j < SABER_N; j++) { + res[i].coeffs[j] += h1; + res[i].coeffs[j] >>= SABER_EQ - SABER_EP; + res[i].coeffs[j] &= SABER_Q - 1; + } + } + + PQCLEAN_SABER_AVX2_POLVECp2BS(pk, res); // pack public key +} + + +void PQCLEAN_SABER_AVX2_indcpa_kem_enc(uint8_t ciphertext[SABER_BYTES_CCA_DEC], const uint8_t m[SABER_KEYBYTES], const uint8_t noiseseed[SABER_NOISESEEDBYTES], const uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES]) { + size_t i, j; + + poly A[SABER_L][SABER_L]; + poly res[SABER_L]; + toom4_points skpv1_eval[SABER_L]; + + poly *temp = A[0]; // re-use stack space + poly *vprime = &A[0][0]; + poly *message = &A[0][1]; + + const uint8_t *seed_A = pk + SABER_POLYVECCOMPRESSEDBYTES; + uint8_t *msk_c = ciphertext + SABER_POLYVECCOMPRESSEDBYTES; + + PQCLEAN_SABER_AVX2_GenSecret(temp, noiseseed); + for (j = 0; j < SABER_L; j++) { + PQCLEAN_SABER_AVX2_toom4_eval(&skpv1_eval[j], &temp[j]); + } + + PQCLEAN_SABER_AVX2_GenMatrix(A, seed_A); + PQCLEAN_SABER_AVX2_MatrixVectorMul(res, (const poly (*)[SABER_L])A, (const toom4_points *)skpv1_eval, 0); // 0 => not transposed + + // rounding + for (i = 0; i < SABER_L; i++) { //shift right EQ-EP bits + for (j = 0; j < SABER_N; j++) { + res[i].coeffs[j] += h1; + res[i].coeffs[j] >>= SABER_EQ - SABER_EP; + res[i].coeffs[j] &= SABER_Q - 1; + } + } + PQCLEAN_SABER_AVX2_POLVECp2BS(ciphertext, res); + + // vector-vector scalar multiplication with mod p + PQCLEAN_SABER_AVX2_BS2POLVECp(temp, pk); + PQCLEAN_SABER_AVX2_InnerProd(vprime, temp, skpv1_eval); + PQCLEAN_SABER_AVX2_BS2POLmsg(message, m); + + for (i = 0; i < SABER_N; i++) { + vprime->coeffs[i] += h1 - (message->coeffs[i] << (SABER_EP - 1)); + vprime->coeffs[i] &= SABER_P - 1; + vprime->coeffs[i] >>= SABER_EP - SABER_ET; + } + + PQCLEAN_SABER_AVX2_POLT2BS(msk_c, vprime); +} + + +void PQCLEAN_SABER_AVX2_indcpa_kem_dec(uint8_t m[SABER_KEYBYTES], const uint8_t sk[SABER_INDCPA_SECRETKEYBYTES], const uint8_t ciphertext[SABER_BYTES_CCA_DEC]) { + size_t i; + + poly temp[SABER_L]; + toom4_points sksv_eval[SABER_L]; + + const uint8_t *packed_cm = ciphertext + SABER_POLYVECCOMPRESSEDBYTES; + poly *v = &temp[0]; + poly *cm = &temp[1]; + + PQCLEAN_SABER_AVX2_BS2POLVECq(temp, sk); + for (i = 0; i < SABER_L; i++) { + PQCLEAN_SABER_AVX2_toom4_eval(&sksv_eval[i], &temp[i]); + } + + PQCLEAN_SABER_AVX2_BS2POLVECp(temp, ciphertext); + PQCLEAN_SABER_AVX2_InnerProd(v, temp, sksv_eval); + + PQCLEAN_SABER_AVX2_BS2POLT(cm, packed_cm); + + for (i = 0; i < SABER_N; i++) { + v->coeffs[i] += h2 - (cm->coeffs[i] << (SABER_EP - SABER_ET)); + v->coeffs[i] &= SABER_P - 1; + v->coeffs[i] >>= SABER_EP - 1; + } + + PQCLEAN_SABER_AVX2_POLmsg2BS(m, v); +} diff --git a/crypto_kem/saber/avx2/SABER_indcpa.h b/crypto_kem/saber/avx2/SABER_indcpa.h new file mode 100644 index 00000000..acdda606 --- /dev/null +++ b/crypto_kem/saber/avx2/SABER_indcpa.h @@ -0,0 +1,13 @@ +#ifndef INDCPA_H +#define INDCPA_H +#include "SABER_params.h" +#include + +void PQCLEAN_SABER_AVX2_indcpa_kem_keypair(uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES], uint8_t sk[SABER_INDCPA_SECRETKEYBYTES]); + +void PQCLEAN_SABER_AVX2_indcpa_kem_enc(uint8_t ciphertext[SABER_BYTES_CCA_DEC], const uint8_t m[SABER_KEYBYTES], const uint8_t noiseseed[SABER_NOISESEEDBYTES], const uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES]); + +void PQCLEAN_SABER_AVX2_indcpa_kem_dec(uint8_t m[SABER_KEYBYTES], const uint8_t sk[SABER_INDCPA_SECRETKEYBYTES], const uint8_t ciphertext[SABER_BYTES_CCA_DEC]); + + +#endif diff --git a/crypto_kem/saber/avx2/SABER_params.h b/crypto_kem/saber/avx2/SABER_params.h new file mode 100644 index 00000000..d1a5ddd7 --- /dev/null +++ b/crypto_kem/saber/avx2/SABER_params.h @@ -0,0 +1,41 @@ +#ifndef PARAMS_H +#define PARAMS_H + + +/* Don't change anything below this line */ +#define SABER_L 3 +#define SABER_MU 8 +#define SABER_ET 4 + +#define SABER_N 256 + +#define SABER_EP 10 +#define SABER_P (1 << SABER_EP) + +#define SABER_EQ 13 +#define SABER_Q (1 << SABER_EQ) + +#define SABER_SEEDBYTES 32 +#define SABER_NOISESEEDBYTES 32 +#define SABER_KEYBYTES 32 +#define SABER_HASHBYTES 32 + +#define SABER_POLYCOINBYTES (SABER_MU * SABER_N / 8) + +#define SABER_POLYBYTES (SABER_EQ * SABER_N / 8) +#define SABER_POLYVECBYTES (SABER_L * SABER_POLYBYTES) + +#define SABER_POLYCOMPRESSEDBYTES (SABER_EP * SABER_N / 8) +#define SABER_POLYVECCOMPRESSEDBYTES (SABER_L * SABER_POLYCOMPRESSEDBYTES) + +#define SABER_SCALEBYTES_KEM (SABER_ET * SABER_N / 8) + +#define SABER_INDCPA_PUBLICKEYBYTES (SABER_POLYVECCOMPRESSEDBYTES + SABER_SEEDBYTES) +#define SABER_INDCPA_SECRETKEYBYTES (SABER_POLYVECBYTES) + +#define SABER_PUBLICKEYBYTES (SABER_INDCPA_PUBLICKEYBYTES) +#define SABER_SECRETKEYBYTES (SABER_INDCPA_SECRETKEYBYTES + SABER_INDCPA_PUBLICKEYBYTES + SABER_HASHBYTES + SABER_KEYBYTES) + +#define SABER_BYTES_CCA_DEC (SABER_POLYVECCOMPRESSEDBYTES + SABER_SCALEBYTES_KEM) + +#endif diff --git a/crypto_kem/saber/avx2/api.h b/crypto_kem/saber/avx2/api.h new file mode 100644 index 00000000..20bf0df3 --- /dev/null +++ b/crypto_kem/saber/avx2/api.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_SABER_AVX2_API_H +#define PQCLEAN_SABER_AVX2_API_H + + +#define PQCLEAN_SABER_AVX2_CRYPTO_ALGNAME "Saber" +#define PQCLEAN_SABER_AVX2_CRYPTO_BYTES 32 +#define PQCLEAN_SABER_AVX2_CRYPTO_CIPHERTEXTBYTES 1088 +#define PQCLEAN_SABER_AVX2_CRYPTO_PUBLICKEYBYTES 992 +#define PQCLEAN_SABER_AVX2_CRYPTO_SECRETKEYBYTES 2304 + +int PQCLEAN_SABER_AVX2_crypto_kem_keypair(unsigned char *pk, unsigned char *sk); + +int PQCLEAN_SABER_AVX2_crypto_kem_enc(unsigned char *ct, unsigned char *k, const unsigned char *pk); + +int PQCLEAN_SABER_AVX2_crypto_kem_dec(unsigned char *k, const unsigned char *ct, const unsigned char *sk); + + +#endif /* PQCLEAN_SABER_AVX2_API_H */ diff --git a/crypto_kem/saber/avx2/cbd.c b/crypto_kem/saber/avx2/cbd.c new file mode 100644 index 00000000..53335375 --- /dev/null +++ b/crypto_kem/saber/avx2/cbd.c @@ -0,0 +1,48 @@ +#include "SABER_params.h" +#include "api.h" +#include "cbd.h" +#include +/*--------------------------------------------------------------------- +This file has been adapted from the implementation +(available at, Public Domain https://github.com/pq-crystals/kyber) +of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" +by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, +Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle +----------------------------------------------------------------------*/ + + +static uint64_t load_littleendian(const uint8_t *x, int bytes) { + int i; + uint64_t r = x[0]; + for (i = 1; i < bytes; i++) { + r |= (uint64_t)x[i] << (8 * i); + } + return r; +} + +void PQCLEAN_SABER_AVX2_cbd(uint16_t s[SABER_N], const uint8_t buf[SABER_POLYCOINBYTES]) { + uint32_t t, d, a[4], b[4]; + int i, j; + + for (i = 0; i < SABER_N / 4; i++) { + t = (uint32_t) load_littleendian(buf + 4 * i, 4); + d = 0; + for (j = 0; j < 4; j++) { + d += (t >> j) & 0x11111111; + } + + a[0] = d & 0xf; + b[0] = (d >> 4) & 0xf; + a[1] = (d >> 8) & 0xf; + b[1] = (d >> 12) & 0xf; + a[2] = (d >> 16) & 0xf; + b[2] = (d >> 20) & 0xf; + a[3] = (d >> 24) & 0xf; + b[3] = (d >> 28); + + s[4 * i + 0] = (uint16_t)(a[0] - b[0]); + s[4 * i + 1] = (uint16_t)(a[1] - b[1]); + s[4 * i + 2] = (uint16_t)(a[2] - b[2]); + s[4 * i + 3] = (uint16_t)(a[3] - b[3]); + } +} diff --git a/crypto_kem/saber/avx2/cbd.h b/crypto_kem/saber/avx2/cbd.h new file mode 100644 index 00000000..afe84bf3 --- /dev/null +++ b/crypto_kem/saber/avx2/cbd.h @@ -0,0 +1,16 @@ +#ifndef CBD_H +#define CBD_H +/*--------------------------------------------------------------------- +This file has been adapted from the implementation +(available at, Public Domain https://github.com/pq-crystals/kyber) +of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" +by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, +Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle +----------------------------------------------------------------------*/ +#include "SABER_params.h" +#include + +void PQCLEAN_SABER_AVX2_cbd(uint16_t s[SABER_N], const uint8_t buf[SABER_POLYCOINBYTES]); + + +#endif diff --git a/crypto_kem/saber/avx2/kem.c b/crypto_kem/saber/avx2/kem.c new file mode 100644 index 00000000..e47e985f --- /dev/null +++ b/crypto_kem/saber/avx2/kem.c @@ -0,0 +1,77 @@ +#include "SABER_indcpa.h" +#include "SABER_params.h" +#include "api.h" +#include "fips202.h" +#include "randombytes.h" +#include "verify.h" +#include +#include + + +int PQCLEAN_SABER_AVX2_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + size_t i; + + PQCLEAN_SABER_AVX2_indcpa_kem_keypair(pk, sk); // sk[0:SABER_INDCPA_SECRETKEYBYTES-1] <-- sk + for (i = 0; i < SABER_INDCPA_PUBLICKEYBYTES; i++) { + sk[i + SABER_INDCPA_SECRETKEYBYTES] = pk[i]; // sk[SABER_INDCPA_SECRETKEYBYTES:SABER_INDCPA_SECRETKEYBYTES+SABER_INDCPA_SECRETKEYBYTES-1] <-- pk + } + + sha3_256(sk + SABER_SECRETKEYBYTES - 64, pk, SABER_INDCPA_PUBLICKEYBYTES); // Then hash(pk) is appended. + + randombytes(sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES); // Remaining part of sk contains a pseudo-random number. + // This is output when check in PQCLEAN_SABER_AVX2_crypto_kem_dec() fails. + return (0); +} + +int PQCLEAN_SABER_AVX2_crypto_kem_enc(uint8_t *c, uint8_t *k, const uint8_t *pk) { + + uint8_t kr[64]; // Will contain key, coins + uint8_t buf[64]; + + randombytes(buf, 32); + + sha3_256(buf, buf, 32); // BUF[0:31] <-- random message (will be used as the key for client) Note: hash doesnot release system RNG output + + sha3_256(buf + 32, pk, SABER_INDCPA_PUBLICKEYBYTES); // BUF[32:63] <-- Hash(public key); Multitarget countermeasure for coins + contributory KEM + + sha3_512(kr, buf, 64); // kr[0:63] <-- Hash(buf[0:63]); + // K^ <-- kr[0:31] + // noiseseed (r) <-- kr[32:63]; + PQCLEAN_SABER_AVX2_indcpa_kem_enc(c, buf, kr + 32, pk); // buf[0:31] contains message; kr[32:63] contains randomness r; + + sha3_256(kr + 32, c, SABER_BYTES_CCA_DEC); + + sha3_256(k, kr, 64); // hash concatenation of pre-k and h(c) to k + + return (0); +} + +int PQCLEAN_SABER_AVX2_crypto_kem_dec(uint8_t *k, const uint8_t *c, const uint8_t *sk) { + size_t i; + uint8_t fail; + uint8_t cmp[SABER_BYTES_CCA_DEC]; + uint8_t buf[64]; + uint8_t kr[64]; // Will contain key, coins + const uint8_t *pk = sk + SABER_INDCPA_SECRETKEYBYTES; + + PQCLEAN_SABER_AVX2_indcpa_kem_dec(buf, sk, c); // buf[0:31] <-- message + + // Multitarget countermeasure for coins + contributory KEM + for (i = 0; i < 32; i++) { // Save hash by storing h(pk) in sk + buf[32 + i] = sk[SABER_SECRETKEYBYTES - 64 + i]; + } + + sha3_512(kr, buf, 64); + + PQCLEAN_SABER_AVX2_indcpa_kem_enc(cmp, buf, kr + 32, pk); + + fail = PQCLEAN_SABER_AVX2_verify(c, cmp, SABER_BYTES_CCA_DEC); + + sha3_256(kr + 32, c, SABER_BYTES_CCA_DEC); // overwrite coins in kr with h(c) + + PQCLEAN_SABER_AVX2_cmov(kr, sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES, fail); + + sha3_256(k, kr, 64); // hash concatenation of pre-k and h(c) to k + + return (0); +} diff --git a/crypto_kem/saber/avx2/pack_unpack.c b/crypto_kem/saber/avx2/pack_unpack.c new file mode 100644 index 00000000..f9315d5d --- /dev/null +++ b/crypto_kem/saber/avx2/pack_unpack.c @@ -0,0 +1,147 @@ +#include "SABER_params.h" +#include "pack_unpack.h" +#include "poly.h" +#include + +void PQCLEAN_SABER_AVX2_POLT2BS(uint8_t bytes[SABER_SCALEBYTES_KEM], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; + for (j = 0; j < SABER_N / 2; j++) { + out[0] = (uint8_t) ((in[0] & 0x0f) | (in[1] << 4)); + in += 2; + out += 1; + } +} + +void PQCLEAN_SABER_AVX2_BS2POLT(poly *data, const uint8_t bytes[SABER_SCALEBYTES_KEM]) { + /* This function does not reduce its output mod T */ + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; + for (j = 0; j < SABER_N / 2; j++) { + out[0] = in[0]; + out[1] = in[0] >> 4; + in += 1; + out += 2; + } +} + +static void POLq2BS(uint8_t bytes[SABER_POLYBYTES], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; + for (j = 0; j < SABER_N / 8; j++) { + out[0] = (uint8_t) (in[0]); + out[1] = (uint8_t) (((in[0] >> 8) & 0x1f) | (in[1] << 5)); + out[2] = (uint8_t) (in[1] >> 3); + out[3] = (uint8_t) (((in[1] >> 11) & 0x03) | (in[2] << 2)); + out[4] = (uint8_t) (((in[2] >> 6) & 0x7f) | (in[3] << 7)); + out[5] = (uint8_t) (in[3] >> 1); + out[6] = (uint8_t) (((in[3] >> 9) & 0x0f) | (in[4] << 4)); + out[7] = (uint8_t) (in[4] >> 4); + out[8] = (uint8_t) (((in[4] >> 12) & 0x01) | (in[5] << 1)); + out[9] = (uint8_t) (((in[5] >> 7) & 0x3f) | (in[6] << 6)); + out[10] = (uint8_t) (in[6] >> 2); + out[11] = (uint8_t) (((in[6] >> 10) & 0x07) | (in[7] << 3)); + out[12] = (uint8_t) (in[7] >> 5); + in += 8; + out += 13; + } +} + +static void BS2POLq(poly *data, const uint8_t bytes[SABER_POLYBYTES]) { + /* This function does not reduce its output mod Q */ + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; + for (j = 0; j < SABER_N / 8; j++) { + out[0] = (in[0]) | (in[1] << 8); + out[1] = (in[1] >> 5) | (in[2] << 3) | (in[3] << 11); + out[2] = (in[3] >> 2) | (in[4] << 6); + out[3] = (in[4] >> 7) | (in[5] << 1) | (in[6] << 9); + out[4] = (in[6] >> 4) | (in[7] << 4) | (in[8] << 12); + out[5] = (in[8] >> 1) | (in[9] << 7); + out[6] = (in[9] >> 6) | (in[10] << 2) | (in[11] << 10); + out[7] = (in[11] >> 3) | (in[12] << 5); + in += 13; + out += 8; + } +} + +static void POLp2BS(uint8_t bytes[SABER_POLYCOMPRESSEDBYTES], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; + for (j = 0; j < SABER_N / 4; j++) { + out[0] = (uint8_t) (in[0]); + out[1] = (uint8_t) (((in[0] >> 8) & 0x03) | (in[1] << 2)); + out[2] = (uint8_t) (((in[1] >> 6) & 0x0f) | (in[2] << 4)); + out[3] = (uint8_t) (((in[2] >> 4) & 0x3f) | (in[3] << 6)); + out[4] = (uint8_t) (in[3] >> 2); + in += 4; + out += 5; + } +} + +static void BS2POLp(poly *data, const uint8_t bytes[SABER_POLYCOMPRESSEDBYTES]) { + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; + for (j = 0; j < SABER_N / 4; j++) { + out[0] = in[0] | (in[1] << 8); + out[1] = (in[1] >> 2) | (in[2] << 6); + out[2] = (in[2] >> 4) | (in[3] << 4); + out[3] = (in[3] >> 6) | (in[4] << 2); + in += 5; + out += 4; + } +} + +void PQCLEAN_SABER_AVX2_POLVECq2BS(uint8_t bytes[SABER_POLYVECBYTES], const poly data[SABER_L]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + POLq2BS(bytes + i * SABER_POLYBYTES, &data[i]); + } +} + +void PQCLEAN_SABER_AVX2_BS2POLVECq(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECBYTES]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + BS2POLq(&data[i], bytes + i * SABER_POLYBYTES); + } +} + +void PQCLEAN_SABER_AVX2_POLVECp2BS(uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES], const poly data[SABER_L]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + POLp2BS(bytes + i * SABER_POLYCOMPRESSEDBYTES, &data[i]); + } +} + +void PQCLEAN_SABER_AVX2_BS2POLVECp(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + BS2POLp(&data[i], bytes + i * SABER_POLYCOMPRESSEDBYTES); + } +} + +void PQCLEAN_SABER_AVX2_BS2POLmsg(poly *data, const uint8_t bytes[SABER_KEYBYTES]) { + size_t i, j; + for (j = 0; j < SABER_KEYBYTES; j++) { + for (i = 0; i < 8; i++) { + data->coeffs[j * 8 + i] = ((bytes[j] >> i) & 0x01); + } + } +} + +void PQCLEAN_SABER_AVX2_POLmsg2BS(uint8_t bytes[SABER_KEYBYTES], const poly *data) { + size_t i, j; + memset(bytes, 0, SABER_KEYBYTES); + + for (j = 0; j < SABER_KEYBYTES; j++) { + for (i = 0; i < 8; i++) { + bytes[j] = bytes[j] | ((data->coeffs[j * 8 + i] & 0x01) << i); + } + } +} diff --git a/crypto_kem/saber/avx2/pack_unpack.h b/crypto_kem/saber/avx2/pack_unpack.h new file mode 100644 index 00000000..0965bbcd --- /dev/null +++ b/crypto_kem/saber/avx2/pack_unpack.h @@ -0,0 +1,28 @@ +#ifndef PACK_UNPACK_H +#define PACK_UNPACK_H +#include "SABER_params.h" +#include "poly.h" +#include +#include + +void PQCLEAN_SABER_AVX2_POLT2BS(uint8_t bytes[SABER_SCALEBYTES_KEM], const poly *data); + +void PQCLEAN_SABER_AVX2_BS2POLT(poly *data, const uint8_t bytes[SABER_SCALEBYTES_KEM]); + + +void PQCLEAN_SABER_AVX2_POLVECq2BS(uint8_t bytes[SABER_POLYVECBYTES], const poly data[SABER_L]); + +void PQCLEAN_SABER_AVX2_POLVECp2BS(uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES], const poly data[SABER_L]); + + +void PQCLEAN_SABER_AVX2_BS2POLVECq(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECBYTES]); + +void PQCLEAN_SABER_AVX2_BS2POLVECp(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES]); + + +void PQCLEAN_SABER_AVX2_BS2POLmsg(poly *data, const uint8_t bytes[SABER_KEYBYTES]); + +void PQCLEAN_SABER_AVX2_POLmsg2BS(uint8_t bytes[SABER_KEYBYTES], const poly *data); + + +#endif diff --git a/crypto_kem/saber/avx2/poly.c b/crypto_kem/saber/avx2/poly.c new file mode 100644 index 00000000..1bc268b6 --- /dev/null +++ b/crypto_kem/saber/avx2/poly.c @@ -0,0 +1,62 @@ +#include "cbd.h" +#include "fips202.h" +#include "pack_unpack.h" +#include "poly.h" + + +void PQCLEAN_SABER_AVX2_MatrixVectorMul(poly c[SABER_L], const poly A[SABER_L][SABER_L], const toom4_points s_eval[SABER_L], int transpose) { + size_t i, j; + toom4_points_product c_eval; + + if (transpose) { + for (i = 0; i < SABER_L; i++) { + PQCLEAN_SABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &A[0][i], &s_eval[0], 0); + for (j = 1; j < SABER_L; j++) { + PQCLEAN_SABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &A[j][i], &s_eval[j], 1); + } + PQCLEAN_SABER_AVX2_toom4_interp(&c[i], &c_eval); + } + } else { + for (i = 0; i < SABER_L; i++) { + PQCLEAN_SABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &A[i][0], &s_eval[0], 0); + for (j = 1; j < SABER_L; j++) { + PQCLEAN_SABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &A[i][j], &s_eval[j], 1); + } + PQCLEAN_SABER_AVX2_toom4_interp(&c[i], &c_eval); + } + } +} + +void PQCLEAN_SABER_AVX2_InnerProd(poly *c, const poly b[SABER_L], const toom4_points s_eval[SABER_L]) { + size_t i; + toom4_points_product c_eval; //Holds results for 9 Karatsuba at a time + + PQCLEAN_SABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &b[0], &s_eval[0], 0); + for (i = 1; i < SABER_L; i++) { + PQCLEAN_SABER_AVX2_toom4_mul_A_by_B_eval(&c_eval, &b[i], &s_eval[i], 1); + } + + PQCLEAN_SABER_AVX2_toom4_interp(c, &c_eval); +} + +void PQCLEAN_SABER_AVX2_GenMatrix(poly A[SABER_L][SABER_L], const uint8_t seed[SABER_SEEDBYTES]) { + size_t i; + uint8_t buf[SABER_L * SABER_POLYVECBYTES]; + + shake128(buf, sizeof(buf), seed, SABER_SEEDBYTES); + + for (i = 0; i < SABER_L; i++) { + PQCLEAN_SABER_AVX2_BS2POLVECq(A[i], buf + i * SABER_POLYVECBYTES); + } +} + +void PQCLEAN_SABER_AVX2_GenSecret(poly s[SABER_L], const uint8_t seed[SABER_NOISESEEDBYTES]) { + size_t i; + uint8_t buf[SABER_L * SABER_POLYCOINBYTES]; + + shake128(buf, sizeof(buf), seed, SABER_NOISESEEDBYTES); + + for (i = 0; i < SABER_L; i++) { + PQCLEAN_SABER_AVX2_cbd(s[i].coeffs, buf + i * SABER_POLYCOINBYTES); + } +} diff --git a/crypto_kem/saber/avx2/poly.h b/crypto_kem/saber/avx2/poly.h new file mode 100644 index 00000000..188e31e7 --- /dev/null +++ b/crypto_kem/saber/avx2/poly.h @@ -0,0 +1,38 @@ +#ifndef POLY_H +#define POLY_H +#include "SABER_params.h" +#include +#include + +typedef union { + uint16_t coeffs[SABER_N]; + __m256i dummy; +} poly; + +typedef union { + uint16_t coeffs[4 * SABER_N]; + __m256i dummy; +} toom4_points; + +typedef union { + uint16_t coeffs[8 * SABER_N]; + __m256i dummy; +} toom4_points_product; + +void PQCLEAN_SABER_AVX2_MatrixVectorMul(poly c[SABER_L], const poly A[SABER_L][SABER_L], const toom4_points s_eval[SABER_L], int transpose); + +void PQCLEAN_SABER_AVX2_InnerProd(poly *c, const poly b[SABER_L], const toom4_points s_eval[SABER_L]); + +void PQCLEAN_SABER_AVX2_GenMatrix(poly a[SABER_L][SABER_L], const uint8_t seed[SABER_SEEDBYTES]); + +void PQCLEAN_SABER_AVX2_GenSecret(poly s[SABER_L], const uint8_t seed[SABER_NOISESEEDBYTES]); + + +void PQCLEAN_SABER_AVX2_toom4_interp(poly *res_avx, const toom4_points_product *c_eval); + +void PQCLEAN_SABER_AVX2_toom4_eval(toom4_points *b_eval, const poly *b); + +void PQCLEAN_SABER_AVX2_toom4_mul_A_by_B_eval(toom4_points_product *c_eval, const poly *a_avx, const toom4_points *b_eval, int accumulate); + + +#endif diff --git a/crypto_kem/saber/avx2/poly_mul.c b/crypto_kem/saber/avx2/poly_mul.c new file mode 100644 index 00000000..2090e64f --- /dev/null +++ b/crypto_kem/saber/avx2/poly_mul.c @@ -0,0 +1,1496 @@ +#include "SABER_params.h" +#include "poly.h" + + +#define L (SABER_N / 64) + +/* 16 word parallel multiply */ +#define mul(a, b) _mm256_mullo_epi16((a), (b)) +/* 16 word parallel multiply and accumulate */ +#define mac(a, b, c) _mm256_add_epi16(_mm256_mullo_epi16((a), (b)), (c)) + +static void schoolbook16x16(__m256i *c, const __m256i *a, const __m256i *b) { + __m256i a0, a1, a2, a3; + __m256i b0, b1, b2, b3; + __m256i t0; + a0 = a[0]; + a1 = a[1]; + a2 = a[2]; + a3 = a[3]; + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[0] = mul(a0, b0); + t0 = mul(a0, b1); + c[1] = mac(a1, b0, t0); + t0 = mul(a0, b2); + t0 = mac(a1, b1, t0); + c[2] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[3] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[4] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[5] = mac(a3, b2, t0); + c[6] = mul(a3, b3); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[4] = mac(a0, b0, c[4]); + t0 = mac(a0, b1, c[5]); + c[5] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[6]); + t0 = mac(a1, b1, t0); + c[6] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[7] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[8] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[9] = mac(a3, b2, t0); + c[10] = mul(a3, b3); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[13] = mac(a3, b2, t0); + c[14] = mul(a3, b3); + b0 = b[12]; + b1 = b[13]; + b2 = b[14]; + b3 = b[15]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[17] = mac(a3, b2, t0); + c[18] = mul(a3, b3); + a0 = a[4]; + a1 = a[5]; + a2 = a[6]; + a3 = a[7]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[21] = mac(a3, b2, t0); + c[22] = mul(a3, b3); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[4] = mac(a0, b0, c[4]); + t0 = mac(a0, b1, c[5]); + c[5] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[6]); + t0 = mac(a1, b1, t0); + c[6] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[7]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[7] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[8]); + t0 = mac(a2, b2, t0); + c[8] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[9]); + c[9] = mac(a3, b2, t0); + c[10] = mac(a3, b3, c[10]); + a0 = a[8]; + a1 = a[9]; + a2 = a[10]; + a3 = a[11]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[12]; + b1 = b[13]; + b2 = b[14]; + b3 = b[15]; + c[20] = mac(a0, b0, c[20]); + t0 = mac(a0, b1, c[21]); + c[21] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[22]); + t0 = mac(a1, b1, t0); + c[22] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[23] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[24] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[25] = mac(a3, b2, t0); + c[26] = mul(a3, b3); + a0 = a[12]; + a1 = a[13]; + a2 = a[14]; + a3 = a[15]; + c[24] = mac(a0, b0, c[24]); + t0 = mac(a0, b1, c[25]); + c[25] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[26]); + t0 = mac(a1, b1, t0); + c[26] = mac(a2, b0, t0); + t0 = mul(a0, b3); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[27] = mac(a3, b0, t0); + t0 = mul(a1, b3); + t0 = mac(a2, b2, t0); + c[28] = mac(a3, b1, t0); + t0 = mul(a2, b3); + c[29] = mac(a3, b2, t0); + c[30] = mul(a3, b3); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[20] = mac(a0, b0, c[20]); + t0 = mac(a0, b1, c[21]); + c[21] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[22]); + t0 = mac(a1, b1, t0); + c[22] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[23]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[23] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[24]); + t0 = mac(a2, b2, t0); + c[24] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[25]); + c[25] = mac(a3, b2, t0); + c[26] = mac(a3, b3, c[26]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + c[31] = _mm256_setzero_si256(); +} + +static void schoolbook16x16_acc(__m256i *c, const __m256i *a, const __m256i *b) { + __m256i a0, a1, a2, a3; + __m256i b0, b1, b2, b3; + __m256i t0; + a0 = a[0]; + a1 = a[1]; + a2 = a[2]; + a3 = a[3]; + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[0] = mac(a0, b0, c[0]); + t0 = mac(a0, b1, c[1]); + c[1] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[2]); + t0 = mac(a1, b1, t0); + c[2] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[3]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[3] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[4]); + t0 = mac(a2, b2, t0); + c[4] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[5]); + c[5] = mac(a3, b2, t0); + c[6] = mac(a3, b3, c[6]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[4] = mac(a0, b0, c[4]); + t0 = mac(a0, b1, c[5]); + c[5] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[6]); + t0 = mac(a1, b1, t0); + c[6] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[7]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[7] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[8]); + t0 = mac(a2, b2, t0); + c[8] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[9]); + c[9] = mac(a3, b2, t0); + c[10] = mac(a3, b3, c[10]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[12]; + b1 = b[13]; + b2 = b[14]; + b3 = b[15]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + a0 = a[4]; + a1 = a[5]; + a2 = a[6]; + a3 = a[7]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[4] = mac(a0, b0, c[4]); + t0 = mac(a0, b1, c[5]); + c[5] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[6]); + t0 = mac(a1, b1, t0); + c[6] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[7]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[7] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[8]); + t0 = mac(a2, b2, t0); + c[8] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[9]); + c[9] = mac(a3, b2, t0); + c[10] = mac(a3, b3, c[10]); + a0 = a[8]; + a1 = a[9]; + a2 = a[10]; + a3 = a[11]; + c[8] = mac(a0, b0, c[8]); + t0 = mac(a0, b1, c[9]); + c[9] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[10]); + t0 = mac(a1, b1, t0); + c[10] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[11]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[11] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[12]); + t0 = mac(a2, b2, t0); + c[12] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[13]); + c[13] = mac(a3, b2, t0); + c[14] = mac(a3, b3, c[14]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[12]; + b1 = b[13]; + b2 = b[14]; + b3 = b[15]; + c[20] = mac(a0, b0, c[20]); + t0 = mac(a0, b1, c[21]); + c[21] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[22]); + t0 = mac(a1, b1, t0); + c[22] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[23]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[23] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[24]); + t0 = mac(a2, b2, t0); + c[24] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[25]); + c[25] = mac(a3, b2, t0); + c[26] = mac(a3, b3, c[26]); + a0 = a[12]; + a1 = a[13]; + a2 = a[14]; + a3 = a[15]; + c[24] = mac(a0, b0, c[24]); + t0 = mac(a0, b1, c[25]); + c[25] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[26]); + t0 = mac(a1, b1, t0); + c[26] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[27]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[27] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[28]); + t0 = mac(a2, b2, t0); + c[28] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[29]); + c[29] = mac(a3, b2, t0); + c[30] = mac(a3, b3, c[30]); + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + c[20] = mac(a0, b0, c[20]); + t0 = mac(a0, b1, c[21]); + c[21] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[22]); + t0 = mac(a1, b1, t0); + c[22] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[23]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[23] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[24]); + t0 = mac(a2, b2, t0); + c[24] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[25]); + c[25] = mac(a3, b2, t0); + c[26] = mac(a3, b3, c[26]); + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + c[16] = mac(a0, b0, c[16]); + t0 = mac(a0, b1, c[17]); + c[17] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[18]); + t0 = mac(a1, b1, t0); + c[18] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[19]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[19] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[20]); + t0 = mac(a2, b2, t0); + c[20] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[21]); + c[21] = mac(a3, b2, t0); + c[22] = mac(a3, b3, c[22]); + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + c[12] = mac(a0, b0, c[12]); + t0 = mac(a0, b1, c[13]); + c[13] = mac(a1, b0, t0); + t0 = mac(a0, b2, c[14]); + t0 = mac(a1, b1, t0); + c[14] = mac(a2, b0, t0); + t0 = mac(a0, b3, c[15]); + t0 = mac(a1, b2, t0); + t0 = mac(a2, b1, t0); + c[15] = mac(a3, b0, t0); + t0 = mac(a1, b3, c[16]); + t0 = mac(a2, b2, t0); + c[16] = mac(a3, b1, t0); + t0 = mac(a2, b3, c[17]); + c[17] = mac(a3, b2, t0); + c[18] = mac(a3, b3, c[18]); +} + + +static void transpose(__m256i *M) { + __m256i r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11; + __m256i temp, temp0, temp1, temp2; + + r0 = _mm256_unpacklo_epi16(M[0], M[1]); + r1 = _mm256_unpacklo_epi16(M[2], M[3]); + r2 = _mm256_unpacklo_epi16(M[4], M[5]); + r3 = _mm256_unpacklo_epi16(M[6], M[7]); + r4 = _mm256_unpacklo_epi16(M[8], M[9]); + r5 = _mm256_unpacklo_epi16(M[10], M[11]); + r6 = _mm256_unpacklo_epi16(M[12], M[13]); + r7 = _mm256_unpacklo_epi16(M[14], M[15]); + + temp = _mm256_unpacklo_epi32(r0, r1); + temp0 = _mm256_unpacklo_epi32(r2, r3); + temp1 = _mm256_unpacklo_epi32(r4, r5); + temp2 = _mm256_unpacklo_epi32(r6, r7); + + r8 = _mm256_unpackhi_epi32(r0, r1); + r9 = _mm256_unpackhi_epi32(r2, r3); + r10 = _mm256_unpackhi_epi32(r4, r5); + r11 = _mm256_unpackhi_epi32(r6, r7); + + r0 = _mm256_unpacklo_epi64(temp, temp0); + r2 = _mm256_unpackhi_epi64(temp, temp0); + r1 = _mm256_unpacklo_epi64(temp1, temp2); + r3 = _mm256_unpackhi_epi64(temp1, temp2); + + temp = _mm256_unpackhi_epi16(M[0], M[1]); + temp0 = _mm256_unpackhi_epi16(M[2], M[3]); + temp1 = _mm256_unpackhi_epi16(M[4], M[5]); + temp2 = _mm256_unpackhi_epi16(M[6], M[7]); + + r4 = _mm256_unpackhi_epi16(M[8], M[9]); + M[0] = _mm256_permute2f128_si256(r0, r1, 0x20); + M[8] = _mm256_permute2f128_si256(r0, r1, 0x31); + M[1] = _mm256_permute2f128_si256(r2, r3, 0x20); + M[9] = _mm256_permute2f128_si256(r2, r3, 0x31); + r5 = _mm256_unpackhi_epi16(M[10], M[11]); + r6 = _mm256_unpackhi_epi16(M[12], M[13]); + r7 = _mm256_unpackhi_epi16(M[14], M[15]); + + r0 = _mm256_unpacklo_epi64(r8, r9); + r1 = _mm256_unpacklo_epi64(r10, r11); + r2 = _mm256_unpackhi_epi64(r8, r9); + r3 = _mm256_unpackhi_epi64(r10, r11); + + M[3] = _mm256_permute2f128_si256(r2, r3, 0x20); + M[11] = _mm256_permute2f128_si256(r2, r3, 0x31); + M[2] = _mm256_permute2f128_si256(r0, r1, 0x20); + M[10] = _mm256_permute2f128_si256(r0, r1, 0x31); + + r0 = _mm256_unpacklo_epi32(temp, temp0); + r1 = _mm256_unpacklo_epi32(temp1, temp2); + r2 = _mm256_unpacklo_epi32(r4, r5); + r3 = _mm256_unpacklo_epi32(r6, r7); + + r8 = _mm256_unpacklo_epi64(r0, r1); + r10 = _mm256_unpackhi_epi64(r0, r1); + r9 = _mm256_unpacklo_epi64(r2, r3); + r11 = _mm256_unpackhi_epi64(r2, r3); + + M[4] = _mm256_permute2f128_si256(r8, r9, 0x20); + M[12] = _mm256_permute2f128_si256(r8, r9, 0x31); + M[5] = _mm256_permute2f128_si256(r10, r11, 0x20); + M[13] = _mm256_permute2f128_si256(r10, r11, 0x31); + + r0 = _mm256_unpackhi_epi32(temp, temp0); + r1 = _mm256_unpackhi_epi32(temp1, temp2); + r2 = _mm256_unpackhi_epi32(r4, r5); + r3 = _mm256_unpackhi_epi32(r6, r7); + + r4 = _mm256_unpacklo_epi64(r0, r1); + r6 = _mm256_unpackhi_epi64(r0, r1); + r5 = _mm256_unpacklo_epi64(r2, r3); + r7 = _mm256_unpackhi_epi64(r2, r3); + + M[6] = _mm256_permute2f128_si256(r4, r5, 0x20); + M[14] = _mm256_permute2f128_si256(r4, r5, 0x31); + M[7] = _mm256_permute2f128_si256(r6, r7, 0x20); + M[15] = _mm256_permute2f128_si256(r6, r7, 0x31); +} + +static void batch_64coefficient_multiplications(toom4_points_product *c_eval, const __m256i *a, const toom4_points *b_eval, int accumulate) { + toom4_points a_eval;// Holds evaluation (a & b) for 7 Karatsuba at a time + __m256i r0_avx, r1_avx, r2_avx, r3_avx; + __m256i *va = (__m256i *)a_eval.coeffs; + __m256i *vb = (__m256i *)b_eval->coeffs; + __m256i *vc = (__m256i *)c_eval->coeffs; + + //------------------AVX evaluation for 1st poly----------------------- + r0_avx = a[0 * L + 0]; + r1_avx = a[0 * L + 1]; + r2_avx = a[0 * L + 2]; + r3_avx = a[0 * L + 3]; + + va[0] = r0_avx; + va[1] = r1_avx; + va[2] = r2_avx; + va[3] = r3_avx; + va[4] = _mm256_add_epi16(r0_avx, r1_avx); + va[5] = _mm256_add_epi16(r2_avx, r3_avx); + va[6] = _mm256_add_epi16(r0_avx, r2_avx); + va[7] = _mm256_add_epi16(r1_avx, r3_avx); + va[8] = _mm256_add_epi16(va[6], va[7]); + //------------------AVX evaluation for 1st poly ends------------------ + + //------------------AVX evaluation for 2nd poly----------------------- + r0_avx = a[1 * L + 0]; + r1_avx = a[1 * L + 1]; + r2_avx = a[1 * L + 2]; + r3_avx = a[1 * L + 3]; + + va[0 + 9] = r0_avx; + va[1 + 9] = r1_avx; + va[2 + 9] = r2_avx; + va[3 + 9] = r3_avx; + va[4 + 9] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 9] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 9] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 9] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 9] = _mm256_add_epi16(va[6 + 9], va[7 + 9]); + //------------------AVX evaluation for 2nd poly ends------------------ + + //------------------AVX evaluation for 3rd poly----------------------- + r0_avx = a[2 * L + 0]; + r1_avx = a[2 * L + 1]; + r2_avx = a[2 * L + 2]; + r3_avx = a[2 * L + 3]; + + va[0 + 18] = r0_avx; + va[1 + 18] = r1_avx; + va[2 + 18] = r2_avx; + va[3 + 18] = r3_avx; + va[4 + 18] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 18] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 18] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 18] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 18] = _mm256_add_epi16(va[6 + 18], va[7 + 18]); + //------------------AVX evaluation for 3rd poly ends------------------ + + //------------------AVX evaluation for 4th poly----------------------- + r0_avx = a[3 * L + 0]; + r1_avx = a[3 * L + 1]; + r2_avx = a[3 * L + 2]; + r3_avx = a[3 * L + 3]; + + va[0 + 27] = r0_avx; + va[1 + 27] = r1_avx; + va[2 + 27] = r2_avx; + va[3 + 27] = r3_avx; + va[4 + 27] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 27] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 27] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 27] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 27] = _mm256_add_epi16(va[6 + 27], va[7 + 27]); + //------------------AVX evaluation for 4th poly ends------------------ + + //------------------AVX evaluation for 5th poly----------------------- + r0_avx = a[4 * L + 0]; + r1_avx = a[4 * L + 1]; + r2_avx = a[4 * L + 2]; + r3_avx = a[4 * L + 3]; + + va[0 + 36] = r0_avx; + va[1 + 36] = r1_avx; + va[2 + 36] = r2_avx; + va[3 + 36] = r3_avx; + va[4 + 36] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 36] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 36] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 36] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 36] = _mm256_add_epi16(va[6 + 36], va[7 + 36]); + //------------------AVX evaluation for 5th poly ends------------------ + + //------------------AVX evaluation for 6th poly----------------------- + r0_avx = a[5 * L + 0]; + r1_avx = a[5 * L + 1]; + r2_avx = a[5 * L + 2]; + r3_avx = a[5 * L + 3]; + + va[0 + 45] = r0_avx; + va[1 + 45] = r1_avx; + va[2 + 45] = r2_avx; + va[3 + 45] = r3_avx; + va[4 + 45] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 45] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 45] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 45] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 45] = _mm256_add_epi16(va[6 + 45], va[7 + 45]); + //------------------AVX evaluation for 6th poly ends------------------ + + //------------------AVX evaluation for 7th poly----------------------- + r0_avx = a[6 * L + 0]; + r1_avx = a[6 * L + 1]; + r2_avx = a[6 * L + 2]; + r3_avx = a[6 * L + 3]; + + va[0 + 54] = r0_avx; + va[1 + 54] = r1_avx; + va[2 + 54] = r2_avx; + va[3 + 54] = r3_avx; + va[4 + 54] = _mm256_add_epi16(r0_avx, r1_avx); + va[5 + 54] = _mm256_add_epi16(r2_avx, r3_avx); + va[6 + 54] = _mm256_add_epi16(r0_avx, r2_avx); + va[7 + 54] = _mm256_add_epi16(r1_avx, r3_avx); + va[8 + 54] = _mm256_add_epi16(va[6 + 54], va[7 + 54]); + //------------------AVX evaluation for 7th poly ends------------------ + + //-----------------Forward transposes-------------------------------------- + transpose(va); + transpose(va + 16); + transpose(va + 32); + transpose(va + 48); + //-----------------Forward transposes ends--------------------------------- + + if (accumulate == 0) { + schoolbook16x16(vc, va, vb); + schoolbook16x16(vc + 32, va + 16, vb + 16); + schoolbook16x16(vc + 64, va + 32, vb + 32); + schoolbook16x16(vc + 96, va + 48, vb + 48); + } else { + schoolbook16x16_acc(vc, va, vb); + schoolbook16x16_acc(vc + 32, va + 16, vb + 16); + schoolbook16x16_acc(vc + 64, va + 32, vb + 32); + schoolbook16x16_acc(vc + 96, va + 48, vb + 48); + } +} + +static void karatsuba_eval(__m256i *b_eval, const __m256i *b) { + __m256i r0_avx, r1_avx, r2_avx, r3_avx; + + //-------1st poly---------------------------------------------------- + r0_avx = b[0 * L + 0]; + r1_avx = b[0 * L + 1]; + r2_avx = b[0 * L + 2]; + r3_avx = b[0 * L + 3]; + + b_eval[0] = r0_avx; + b_eval[1] = r1_avx; + b_eval[2] = r2_avx; + b_eval[3] = r3_avx; + b_eval[4] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8] = _mm256_add_epi16(b_eval[6], b_eval[7]); + + //-------2nd poly---------------------------------------------------- + r0_avx = b[1 * L + 0]; + r1_avx = b[1 * L + 1]; + r2_avx = b[1 * L + 2]; + r3_avx = b[1 * L + 3]; + + b_eval[0 + 9] = r0_avx; + b_eval[1 + 9] = r1_avx; + b_eval[2 + 9] = r2_avx; + b_eval[3 + 9] = r3_avx; + b_eval[4 + 9] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 9] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 9] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 9] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 9] = _mm256_add_epi16(b_eval[6 + 9], b_eval[7 + 9]); + + //-------3rd poly---------------------------------------------------- + r0_avx = b[2 * L + 0]; + r1_avx = b[2 * L + 1]; + r2_avx = b[2 * L + 2]; + r3_avx = b[2 * L + 3]; + + b_eval[0 + 18] = r0_avx; + b_eval[1 + 18] = r1_avx; + b_eval[2 + 18] = r2_avx; + b_eval[3 + 18] = r3_avx; + b_eval[4 + 18] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 18] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 18] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 18] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 18] = _mm256_add_epi16(b_eval[6 + 18], b_eval[7 + 18]); + + //-------4th poly---------------------------------------------------- + r0_avx = b[3 * L + 0]; + r1_avx = b[3 * L + 1]; + r2_avx = b[3 * L + 2]; + r3_avx = b[3 * L + 3]; + + b_eval[0 + 27] = r0_avx; + b_eval[1 + 27] = r1_avx; + b_eval[2 + 27] = r2_avx; + b_eval[3 + 27] = r3_avx; + b_eval[4 + 27] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 27] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 27] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 27] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 27] = _mm256_add_epi16(b_eval[6 + 27], b_eval[7 + 27]); + + //-------5th poly---------------------------------------------------- + r0_avx = b[4 * L + 0]; + r1_avx = b[4 * L + 1]; + r2_avx = b[4 * L + 2]; + r3_avx = b[4 * L + 3]; + + b_eval[0 + 36] = r0_avx; + b_eval[1 + 36] = r1_avx; + b_eval[2 + 36] = r2_avx; + b_eval[3 + 36] = r3_avx; + b_eval[4 + 36] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 36] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 36] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 36] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 36] = _mm256_add_epi16(b_eval[6 + 36], b_eval[7 + 36]); + + //-------6th poly---------------------------------------------------- + r0_avx = b[5 * L + 0]; + r1_avx = b[5 * L + 1]; + r2_avx = b[5 * L + 2]; + r3_avx = b[5 * L + 3]; + + b_eval[0 + 45] = r0_avx; + b_eval[1 + 45] = r1_avx; + b_eval[2 + 45] = r2_avx; + b_eval[3 + 45] = r3_avx; + b_eval[4 + 45] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 45] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 45] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 45] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 45] = _mm256_add_epi16(b_eval[6 + 45], b_eval[7 + 45]); + + //-------7th poly---------------------------------------------------- + r0_avx = b[6 * L + 0]; + r1_avx = b[6 * L + 1]; + r2_avx = b[6 * L + 2]; + r3_avx = b[6 * L + 3]; + + b_eval[0 + 54] = r0_avx; + b_eval[1 + 54] = r1_avx; + b_eval[2 + 54] = r2_avx; + b_eval[3 + 54] = r3_avx; + b_eval[4 + 54] = _mm256_add_epi16(r0_avx, r1_avx); + b_eval[5 + 54] = _mm256_add_epi16(r2_avx, r3_avx); + b_eval[6 + 54] = _mm256_add_epi16(r0_avx, r2_avx); + b_eval[7 + 54] = _mm256_add_epi16(r1_avx, r3_avx); + b_eval[8 + 54] = _mm256_add_epi16(b_eval[6 + 54], b_eval[7 + 54]); + + //--------------Evaluating B poly ends------------------------------- + transpose(b_eval); + transpose(b_eval + 16); + transpose(b_eval + 32); + transpose(b_eval + 48); +} + +static void karatsuba_interp(__m256i *result_final0, __m256i *result_final1, __m256i *result_final2, __m256i *result_final3, __m256i *result_final4, __m256i *result_final5, __m256i *result_final6, const __m256i *c_eval) { + __m256i res_avx0, res_avx1, res_avx2, res_avx3, res_avx4, res_avx5, res_avx6, res_avx7; // to hold each 64X64 poly mul results + __m256i temp, c6_avx, c7_avx, c8_avx, c20_avx, c21_avx, c22_avx, c23_avx, c24_avx; + + //------------------------AVX interpolation for 1st poly external------------------- + res_avx0 = c_eval[0]; + res_avx2 = c_eval[1]; + res_avx4 = c_eval[2]; + res_avx6 = c_eval[3]; + c6_avx = c_eval[6]; + c7_avx = c_eval[7]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[8], c6_avx), c7_avx); + + res_avx1 = c_eval[16]; + res_avx3 = c_eval[17]; + res_avx5 = c_eval[18]; + res_avx7 = c_eval[19]; + c22_avx = c_eval[22]; + c23_avx = c_eval[23]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[21], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[24], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[20], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[5], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[4], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final0[0] = res_avx0; + result_final0[1] = res_avx1; + result_final0[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final0[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final0[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final0[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final0[6] = res_avx6; + result_final0[7] = res_avx7; + //------------------------AVX interpolation for 1st poly ends-------------- + + + //------------------------AVX interpolation for 2nd poly external------------------- + res_avx0 = c_eval[9]; //c_eval0 + res_avx2 = c_eval[10]; //c_eval1 + res_avx4 = c_eval[11]; //c_eval2 + res_avx6 = c_eval[12]; //c_eval3 + c6_avx = c_eval[15]; //c_eval6 + c7_avx = c_eval[32]; //c_eval7 + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[33], c6_avx), c7_avx); + + res_avx1 = c_eval[25]; //c_eval0 + res_avx3 = c_eval[26]; //c_eval1 + res_avx5 = c_eval[27]; //c_eval2 + res_avx7 = c_eval[28]; //c_eval3 + c22_avx = c_eval[31]; + c23_avx = c_eval[48]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[30], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[49], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[29], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[14], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[13], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final1[0] = res_avx0; + result_final1[1] = res_avx1; + result_final1[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final1[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final1[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final1[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final1[6] = res_avx6; + result_final1[7] = res_avx7; + //------------------------AVX interpolation for 2nd poly ends-------------- + + //------------------------AVX interpolation for 3rd poly external------------------- + res_avx0 = c_eval[34]; //c_eval0 + res_avx2 = c_eval[35]; //c_eval1 + res_avx4 = c_eval[36]; + res_avx6 = c_eval[37]; + c6_avx = c_eval[40]; + c7_avx = c_eval[41]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[42], c6_avx), c7_avx); + + res_avx1 = c_eval[50]; //c_eval0 + res_avx3 = c_eval[51]; //c_eval1 + res_avx5 = c_eval[52]; + res_avx7 = c_eval[53]; + c22_avx = c_eval[56]; + c23_avx = c_eval[57]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[55], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[58], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[54], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[39], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[38], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final2[0] = res_avx0; + result_final2[1] = res_avx1; + result_final2[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final2[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final2[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final2[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final2[6] = res_avx6; + result_final2[7] = res_avx7; + //------------------------AVX interpolation for 3rd poly ends-------------- + + //------------------------AVX interpolation for 4th poly external------------------- + res_avx0 = c_eval[43]; + res_avx2 = c_eval[44]; + res_avx4 = c_eval[45]; + res_avx6 = c_eval[46]; + c6_avx = c_eval[65]; + c7_avx = c_eval[66]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[67], c6_avx), c7_avx); + + res_avx1 = c_eval[59]; + res_avx3 = c_eval[60]; + res_avx5 = c_eval[61]; + res_avx7 = c_eval[62]; + c22_avx = c_eval[81]; + c23_avx = c_eval[82]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[80], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[83], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[63], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[64], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[47], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final3[0] = res_avx0; + result_final3[1] = res_avx1; + result_final3[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final3[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final3[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final3[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final3[6] = res_avx6; + result_final3[7] = res_avx7; + //------------------------AVX interpolation for 4th poly ends-------------- + + //------------------------AVX interpolation for 5th poly external------------------- + res_avx0 = c_eval[68]; + res_avx2 = c_eval[69]; + res_avx4 = c_eval[70]; + res_avx6 = c_eval[71]; + c6_avx = c_eval[74]; + c7_avx = c_eval[75]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[76], c6_avx), c7_avx); + + res_avx1 = c_eval[84]; + res_avx3 = c_eval[85]; + res_avx5 = c_eval[86]; + res_avx7 = c_eval[87]; + c22_avx = c_eval[90]; + c23_avx = c_eval[91]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[89], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[92], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[88], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[73], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[72], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final4[0] = res_avx0; + result_final4[1] = res_avx1; + result_final4[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final4[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final4[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final4[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final4[6] = res_avx6; + result_final4[7] = res_avx7; + //------------------------AVX interpolation for 5th poly ends-------------- + + //------------------------AVX interpolation for 6th poly external------------------- + res_avx0 = c_eval[77]; + res_avx2 = c_eval[78]; + res_avx4 = c_eval[79]; + res_avx6 = c_eval[96]; + c6_avx = c_eval[99]; + c7_avx = c_eval[100]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[101], c6_avx), c7_avx); + + res_avx1 = c_eval[93]; + res_avx3 = c_eval[94]; + res_avx5 = c_eval[95]; + res_avx7 = c_eval[112]; + c22_avx = c_eval[115]; + c23_avx = c_eval[116]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[114], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[117], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[113], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[98], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[97], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final5[0] = res_avx0; + result_final5[1] = res_avx1; + result_final5[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final5[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final5[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final5[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final5[6] = res_avx6; + result_final5[7] = res_avx7; + //------------------------AVX interpolation for 6th poly ends-------------- + + //------------------------AVX interpolation for 7th poly external------------------- + res_avx0 = c_eval[102]; + res_avx2 = c_eval[103]; + res_avx4 = c_eval[104]; + res_avx6 = c_eval[105]; + c6_avx = c_eval[108]; + c7_avx = c_eval[109]; + + c8_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[110], c6_avx), c7_avx); + + res_avx1 = c_eval[118]; + res_avx3 = c_eval[119]; + res_avx5 = c_eval[120]; + res_avx7 = c_eval[121]; + c22_avx = c_eval[124]; + c23_avx = c_eval[125]; + + c21_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[123], res_avx5), res_avx7); + c24_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[126], c22_avx), c23_avx); + c20_avx = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[122], res_avx1), res_avx3); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[107], res_avx4), res_avx6); + res_avx5 = _mm256_add_epi16(res_avx5, temp); + temp = _mm256_sub_epi16(_mm256_sub_epi16(c_eval[106], res_avx0), res_avx2); + res_avx1 = _mm256_add_epi16(res_avx1, temp); + c22_avx = _mm256_add_epi16(c22_avx, c8_avx); + res_avx6 = _mm256_add_epi16(res_avx6, c21_avx); + res_avx2 = _mm256_add_epi16(res_avx2, c20_avx); + c7_avx = _mm256_add_epi16(c7_avx, c24_avx); + c6_avx = _mm256_sub_epi16(_mm256_sub_epi16(c6_avx, res_avx0), res_avx4); + c22_avx = _mm256_sub_epi16(_mm256_sub_epi16(c22_avx, res_avx1), res_avx5); + c7_avx = _mm256_sub_epi16(_mm256_sub_epi16(c7_avx, res_avx2), res_avx6); + c23_avx = _mm256_sub_epi16(_mm256_sub_epi16(c23_avx, res_avx3), res_avx7); + + result_final6[0] = res_avx0; + result_final6[1] = res_avx1; + result_final6[2] = _mm256_add_epi16(res_avx2, c6_avx); + result_final6[3] = _mm256_add_epi16(res_avx3, c22_avx); + result_final6[4] = _mm256_add_epi16(res_avx4, c7_avx); + result_final6[5] = _mm256_add_epi16(res_avx5, c23_avx); + result_final6[6] = res_avx6; + result_final6[7] = res_avx7; + //------------------------AVX interpolation for 7th poly ends-------------- +} + +void PQCLEAN_SABER_AVX2_toom4_mul_A_by_B_eval(toom4_points_product *c_eval, const poly *a, const toom4_points *b_eval, int accumulate) { + size_t i; + __m256i r0_avx, r1_avx, r2_avx, r3_avx, r4_avx, r5_avx, r6_avx; + __m256i aw_avx[7 * L]; + __m256i *va = (__m256i *)a->coeffs; + + for (i = 0; i < L; i++) { + r0_avx = va[0 * L + i]; + r1_avx = va[1 * L + i]; + r2_avx = va[2 * L + i]; + r3_avx = va[3 * L + i]; + r4_avx = _mm256_add_epi16(r0_avx, r2_avx); + r5_avx = _mm256_add_epi16(r1_avx, r3_avx); + aw_avx[2 * L + i] = _mm256_add_epi16(r4_avx, r5_avx); + aw_avx[3 * L + i] = _mm256_sub_epi16(r4_avx, r5_avx); + r4_avx = _mm256_slli_epi16(r0_avx, 2); + r4_avx = _mm256_add_epi16(r4_avx, r2_avx); + r4_avx = _mm256_slli_epi16(r4_avx, 1); + r5_avx = _mm256_slli_epi16(r1_avx, 2); + r5_avx = _mm256_add_epi16(r5_avx, r3_avx); + aw_avx[4 * L + i] = _mm256_add_epi16(r4_avx, r5_avx); + aw_avx[5 * L + i] = _mm256_sub_epi16(r4_avx, r5_avx); + r4_avx = _mm256_slli_epi16(r3_avx, 3); + r6_avx = _mm256_slli_epi16(r2_avx, 2); + r4_avx = _mm256_add_epi16(r4_avx, r6_avx); + r6_avx = _mm256_slli_epi16(r1_avx, 1); + r4_avx = _mm256_add_epi16(r4_avx, r6_avx); + aw_avx[1 * L + i] = _mm256_add_epi16(r4_avx, r0_avx); + aw_avx[6 * L + i] = r0_avx; + aw_avx[0 * L + i] = r3_avx; + } + + batch_64coefficient_multiplications(c_eval, aw_avx, b_eval, accumulate); +} + +void PQCLEAN_SABER_AVX2_toom4_eval(toom4_points *b_eval, const poly *b) { + size_t i; + __m256i bw_avx[7 * L]; + __m256i r0_avx, r1_avx, r2_avx, r3_avx, r4_avx, r5_avx, r6_avx; + __m256i *vb = (__m256i *)b->coeffs; + __m256i *vb_eval = (__m256i *)b_eval->coeffs; + + for (i = 0; i < L; i++) { + r0_avx = vb[0 * L + i]; + r1_avx = vb[1 * L + i]; + r2_avx = vb[2 * L + i]; + r3_avx = vb[3 * L + i]; + r4_avx = _mm256_add_epi16(r0_avx, r2_avx); + r5_avx = _mm256_add_epi16(r1_avx, r3_avx); + bw_avx[2 * L + i] = _mm256_add_epi16(r4_avx, r5_avx); + bw_avx[3 * L + i] = _mm256_sub_epi16(r4_avx, r5_avx); + r4_avx = _mm256_slli_epi16(r0_avx, 2); + r4_avx = _mm256_add_epi16(r4_avx, r2_avx); + r4_avx = _mm256_slli_epi16(r4_avx, 1); + r5_avx = _mm256_slli_epi16(r1_avx, 2); + r5_avx = _mm256_add_epi16(r5_avx, r3_avx); + bw_avx[4 * L + i] = _mm256_add_epi16(r4_avx, r5_avx); + bw_avx[5 * L + i] = _mm256_sub_epi16(r4_avx, r5_avx); + r4_avx = _mm256_slli_epi16(r3_avx, 3); + r6_avx = _mm256_slli_epi16(r2_avx, 2); + r4_avx = _mm256_add_epi16(r4_avx, r6_avx); + r6_avx = _mm256_slli_epi16(r1_avx, 1); + r4_avx = _mm256_add_epi16(r4_avx, r6_avx); + bw_avx[1 * L + i] = _mm256_add_epi16(r4_avx, r0_avx); + bw_avx[6 * L + i] = r0_avx; + bw_avx[0 * L + i] = r3_avx; + } + + karatsuba_eval(vb_eval, bw_avx); +} + + +void PQCLEAN_SABER_AVX2_toom4_interp(poly *res, const toom4_points_product *c_eval) { + size_t i; + __m256i r0_avx, r1_avx, r2_avx, r3_avx, r4_avx, r5_avx, r6_avx, temp_avx; + __m256i w1_avx[2 * L], w2_avx[2 * L], w3_avx[2 * L], w4_avx[2 * L], w5_avx[2 * L], w6_avx[2 * L], w7_avx[2 * L]; + __m256i res_full[32]; + __m256i *vc = (__m256i *)c_eval->coeffs; + __m256i *vres = (__m256i *)res->coeffs; + + transpose(vc); + transpose(vc + 16); + transpose(vc + 32); + transpose(vc + 48); + transpose(vc + 64); + transpose(vc + 80); + transpose(vc + 96); + transpose(vc + 112); + + karatsuba_interp(w1_avx, w2_avx, w3_avx, w4_avx, w5_avx, w6_avx, w7_avx, vc); + + for (i = 0; i < 2 * L; i++) { + r0_avx = w1_avx[i]; + r1_avx = w2_avx[i]; + r2_avx = w3_avx[i]; + r3_avx = w4_avx[i]; + r4_avx = w5_avx[i]; + r5_avx = w6_avx[i]; + r6_avx = w7_avx[i]; + + r1_avx = _mm256_add_epi16(r1_avx, r4_avx); + r5_avx = _mm256_sub_epi16(r5_avx, r4_avx); + r3_avx = _mm256_sub_epi16(r3_avx, r2_avx); + r3_avx = _mm256_srli_epi16(r3_avx, 1); + r4_avx = _mm256_sub_epi16(r4_avx, r0_avx); + temp_avx = _mm256_slli_epi16(r6_avx, 6); + + r4_avx = _mm256_sub_epi16(r4_avx, temp_avx); + r4_avx = _mm256_slli_epi16(r4_avx, 1); + r4_avx = _mm256_add_epi16(r4_avx, r5_avx); + r2_avx = _mm256_add_epi16(r2_avx, r3_avx); + temp_avx = _mm256_slli_epi16(r2_avx, 6); + + r1_avx = _mm256_sub_epi16(r1_avx, temp_avx); + r1_avx = _mm256_sub_epi16(r1_avx, r2_avx); + r2_avx = _mm256_sub_epi16(r2_avx, r6_avx); + r2_avx = _mm256_sub_epi16(r2_avx, r0_avx); + temp_avx = _mm256_mullo_epi16(r2_avx, _mm256_set1_epi16(45)); + + r1_avx = _mm256_add_epi16(r1_avx, temp_avx); + temp_avx = _mm256_slli_epi16(r2_avx, 3); + + r4_avx = _mm256_sub_epi16(r4_avx, temp_avx); + r4_avx = _mm256_mullo_epi16(r4_avx, _mm256_set1_epi16(-21845)); // -21845 = 1/3 (mod 2^16) + r4_avx = _mm256_srli_epi16(r4_avx, 3); + r5_avx = _mm256_add_epi16(r5_avx, r1_avx); + temp_avx = _mm256_slli_epi16(r3_avx, 4); + + r1_avx = _mm256_add_epi16(r1_avx, temp_avx); + r1_avx = _mm256_mullo_epi16(r1_avx, _mm256_set1_epi16(-29127)); // -29127 = 1/9 (mod 2^16) + r1_avx = _mm256_srli_epi16(r1_avx, 1); + r3_avx = _mm256_add_epi16(r1_avx, r3_avx); + r3_avx = _mm256_sub_epi16(_mm256_set1_epi16(0), r3_avx); + temp_avx = _mm256_mullo_epi16(r1_avx, _mm256_set1_epi16(30)); + temp_avx = _mm256_sub_epi16(temp_avx, r5_avx); + temp_avx = _mm256_mullo_epi16(temp_avx, _mm256_set1_epi16(-4369)); // -4369 = 1/15 (mod 2^16) + + r5_avx = _mm256_srli_epi16(temp_avx, 2); + r2_avx = _mm256_sub_epi16(r2_avx, r4_avx); + r1_avx = _mm256_sub_epi16(r1_avx, r5_avx); + + if (i < L) { + res_full[0 * L + i] = r6_avx; + res_full[1 * L + i] = r5_avx; + res_full[2 * L + i] = r4_avx; + res_full[3 * L + i] = r3_avx; + res_full[4 * L + i] = r2_avx; + res_full[5 * L + i] = r1_avx; + res_full[6 * L + i] = r0_avx; + } else { + res_full[0 * L + i] = _mm256_add_epi16(res_full[0 * L + i], r6_avx); + res_full[1 * L + i] = _mm256_add_epi16(res_full[1 * L + i], r5_avx); + res_full[2 * L + i] = _mm256_add_epi16(res_full[2 * L + i], r4_avx); + res_full[3 * L + i] = _mm256_add_epi16(res_full[3 * L + i], r3_avx); + res_full[4 * L + i] = _mm256_add_epi16(res_full[4 * L + i], r2_avx); + res_full[5 * L + i] = _mm256_add_epi16(res_full[5 * L + i], r1_avx); + res_full[6 * L + i] = r0_avx; + } + } + + // Reduction by X^256 + 1 + for (i = 0; i < 16; i++) { + vres[i] = _mm256_sub_epi16(res_full[i], res_full[i + 16]); + } +} diff --git a/crypto_kem/saber/avx2/verify.c b/crypto_kem/saber/avx2/verify.c new file mode 100644 index 00000000..fe089639 --- /dev/null +++ b/crypto_kem/saber/avx2/verify.c @@ -0,0 +1,35 @@ +#include "verify.h" + +/*------------------------------------------------- +This file has been adapted from the implementation +(available at https://github.com/pq-crystals/kyber) of +"CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" + by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, +Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle +----------------------------------------------------*/ + + +/* returns 0 for equal strings, 1 for non-equal strings */ +uint8_t PQCLEAN_SABER_AVX2_verify(const uint8_t *a, const uint8_t *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 (uint8_t) r; +} + +/* b = 1 means mov, b = 0 means don't mov*/ +void PQCLEAN_SABER_AVX2_cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b) { + size_t i; + + b = -b; + for (i = 0; i < len; i++) { + r[i] ^= b & (x[i] ^ r[i]); + } +} diff --git a/crypto_kem/saber/avx2/verify.h b/crypto_kem/saber/avx2/verify.h new file mode 100644 index 00000000..32edf5d0 --- /dev/null +++ b/crypto_kem/saber/avx2/verify.h @@ -0,0 +1,22 @@ +#ifndef VERIFY_H +#define VERIFY_H +/*------------------------------------------------- +This file has been adapted from the implementation +(available at https://github.com/pq-crystals/kyber) of +"CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" + by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, +Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle +----------------------------------------------------*/ + +#include +#include + +/* returns 0 for equal strings, 1 for non-equal strings */ +uint8_t PQCLEAN_SABER_AVX2_verify(const uint8_t *a, const uint8_t *b, size_t len); + + +/* b = 1 means mov, b = 0 means don't mov*/ +void PQCLEAN_SABER_AVX2_cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b); + + +#endif diff --git a/crypto_kem/saber/clean/LICENSE b/crypto_kem/saber/clean/LICENSE index 08c799e3..d5d21fff 100644 --- a/crypto_kem/saber/clean/LICENSE +++ b/crypto_kem/saber/clean/LICENSE @@ -1,8 +1 @@ ----------------------------------------------------------------------------------------- -SABER_v1.1 - -Public domain - -Authors: Jan-Pieter D'Anvers, Angshuman Karmakar, Sujoy Sinha Roy, -Frederik Vercauteren ----------------------------------------------------------------------------------------- +Public Domain diff --git a/crypto_kem/saber/clean/Makefile b/crypto_kem/saber/clean/Makefile index 2052d200..cbc1357c 100644 --- a/crypto_kem/saber/clean/Makefile +++ b/crypto_kem/saber/clean/Makefile @@ -1,10 +1,10 @@ # This Makefile can be used with GNU Make or BSD Make LIB=libsaber_clean.a -HEADERS=api.h cbd.h poly.h poly_mul.h SABER_indcpa.h SABER_params.h verify.h pack_unpack.h +HEADERS=api.h cbd.h pack_unpack.h poly.h poly_mul.h SABER_indcpa.h SABER_params.h verify.h OBJECTS=cbd.o kem.o pack_unpack.o poly.o poly_mul.o SABER_indcpa.o verify.o -CFLAGS=-O3 -Wall -Wextra -Wpedantic -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) +CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wredundant-decls -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) all: $(LIB) diff --git a/crypto_kem/saber/clean/SABER_indcpa.c b/crypto_kem/saber/clean/SABER_indcpa.c index d381194c..3cc2367c 100644 --- a/crypto_kem/saber/clean/SABER_indcpa.c +++ b/crypto_kem/saber/clean/SABER_indcpa.c @@ -3,296 +3,111 @@ #include "fips202.h" #include "pack_unpack.h" #include "poly.h" -#include "poly_mul.h" #include "randombytes.h" #include #include +#define h1 (1 << (SABER_EQ - SABER_EP - 1)) +#define h2 ((1 << (SABER_EP - 2)) - (1 << (SABER_EP - SABER_ET - 1)) + (1 << (SABER_EQ - SABER_EP - 1))) + +void PQCLEAN_SABER_CLEAN_indcpa_kem_keypair(uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES], uint8_t sk[SABER_INDCPA_SECRETKEYBYTES]) { + size_t i, j; + + poly A[SABER_L][SABER_L]; + poly s[SABER_L]; + poly res[SABER_L]; + + uint8_t rand[SABER_NOISESEEDBYTES]; + uint8_t *seed_A = pk + SABER_POLYVECCOMPRESSEDBYTES; + + randombytes(seed_A, SABER_SEEDBYTES); + shake128(seed_A, SABER_SEEDBYTES, seed_A, SABER_SEEDBYTES); // for not revealing system RNG state + + randombytes(rand, SABER_NOISESEEDBYTES); + PQCLEAN_SABER_CLEAN_GenSecret(s, rand); + PQCLEAN_SABER_CLEAN_POLVECq2BS(sk, s); + + PQCLEAN_SABER_CLEAN_GenMatrix(A, seed_A); // sample matrix A + PQCLEAN_SABER_CLEAN_MatrixVectorMul(res, (const poly (*)[SABER_L])A, (const poly *)s, 1); // Matrix in transposed order -/*----------------------------------------------------------------------------------- - This routine generates a=[Matrix K x K] of 256-coefficient polynomials --------------------------------------------------------------------------------------*/ - -#define h1 4 //2^(EQ-EP-1) - -#define h2 ( (1<<(SABER_EP-2)) - (1<<(SABER_EP-SABER_ET-1)) + (1<<(SABER_EQ-SABER_EP-1)) ) - -static void InnerProd(uint16_t pkcl[SABER_K][SABER_N], uint16_t skpv[SABER_K][SABER_N], uint16_t mod, uint16_t res[SABER_N]); -static void MatrixVectorMul(polyvec *a, uint16_t skpv[SABER_K][SABER_N], uint16_t res[SABER_K][SABER_N], uint16_t mod, int16_t transpose); - -static void POL2MSG(const uint16_t *message_dec_unpacked, unsigned char *message_dec); - -static void GenMatrix(polyvec *a, const unsigned char *seed) { - unsigned char buf[SABER_K * SABER_K * (13 * SABER_N / 8)]; - - uint16_t temp_ar[SABER_N]; - - int i, j, k; - uint16_t mod = (SABER_Q - 1); - - shake128(buf, sizeof(buf), seed, SABER_SEEDBYTES); - - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_K; j++) { - PQCLEAN_SABER_CLEAN_BS2POL(buf + (i * SABER_K + j) * (13 * SABER_N / 8), temp_ar); - for (k = 0; k < SABER_N; k++) { - a[i].vec[j].coeffs[k] = (temp_ar[k])& mod ; - } + // rounding + for (i = 0; i < SABER_L; i++) { + for (j = 0; j < SABER_N; j++) { + res[i].coeffs[j] += h1; + res[i].coeffs[j] >>= SABER_EQ - SABER_EP; + res[i].coeffs[j] &= SABER_Q - 1; } } + + PQCLEAN_SABER_CLEAN_POLVECp2BS(pk, res); // pack public key } -void PQCLEAN_SABER_CLEAN_indcpa_kem_keypair(unsigned char *pk, unsigned char *sk) { - polyvec a[SABER_K]; +void PQCLEAN_SABER_CLEAN_indcpa_kem_enc(uint8_t ciphertext[SABER_BYTES_CCA_DEC], const uint8_t m[SABER_KEYBYTES], const uint8_t noiseseed[SABER_NOISESEEDBYTES], const uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES]) { + size_t i, j; - uint16_t skpv[SABER_K][SABER_N]; + poly A[SABER_L][SABER_L]; + poly res[SABER_L]; + poly s[SABER_L]; + poly *temp = A[0]; // re-use stack space + poly *vprime = &A[0][0]; + poly *message = &A[0][1]; - unsigned char seed[SABER_SEEDBYTES]; - unsigned char noiseseed[SABER_COINBYTES]; - int32_t i, j; - uint16_t mod_q = SABER_Q - 1; + const uint8_t *seed_A = pk + SABER_POLYVECCOMPRESSEDBYTES; + uint8_t *msk_c = ciphertext + SABER_POLYVECCOMPRESSEDBYTES; + + PQCLEAN_SABER_CLEAN_GenSecret(s, noiseseed); + PQCLEAN_SABER_CLEAN_GenMatrix(A, seed_A); + PQCLEAN_SABER_CLEAN_MatrixVectorMul(res, (const poly (*)[SABER_L])A, (const poly *)s, 0); // 0 => not transposed - uint16_t res[SABER_K][SABER_N]; - - randombytes(seed, SABER_SEEDBYTES); - - // for not revealing system RNG state - shake128(seed, SABER_SEEDBYTES, seed, SABER_SEEDBYTES); - randombytes(noiseseed, SABER_COINBYTES); - - GenMatrix(a, seed); //sample matrix A - - // generate secret from constant-time binomial distribution - PQCLEAN_SABER_CLEAN_GenSecret(skpv, noiseseed); - - // do the matrix vector multiplication and rounding - for (i = 0; i < SABER_K; i++) { + // rounding + for (i = 0; i < SABER_L; i++) { //shift right EQ-EP bits for (j = 0; j < SABER_N; j++) { - res[i][j] = 0; - } - } - MatrixVectorMul(a, skpv, res, SABER_Q - 1, 1); - - // now rounding - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - // shift right 3 bits - res[i][j] = (res[i][j] + h1) & (mod_q); - res[i][j] = (res[i][j] >> (SABER_EQ - SABER_EP)); - } - } - - // unload and pack sk=3 x (256 coefficients of 14 bits) - PQCLEAN_SABER_CLEAN_POLVEC2BS(sk, skpv, SABER_Q); - - // unload and pack pk=256 bits seed and 3 x (256 coefficients of 11 bits) - // load the public-key coefficients - PQCLEAN_SABER_CLEAN_POLVEC2BS(pk, res, SABER_P); - - - // now load the seedbytes in PK. Easy since seed bytes are kept in byte format. - for (i = 0; i < SABER_SEEDBYTES; i++) { - pk[SABER_POLYVECCOMPRESSEDBYTES + i] = seed[i]; - } - -} - - -void PQCLEAN_SABER_CLEAN_indcpa_kem_enc(const unsigned char *message_received, unsigned char *noiseseed, const unsigned char *pk, unsigned char *ciphertext) { - uint32_t i, j, k; - polyvec a[SABER_K]; - unsigned char seed[SABER_SEEDBYTES]; - // public key of received by the client - uint16_t pkcl[SABER_K][SABER_N]; - uint16_t skpv1[SABER_K][SABER_N]; - uint16_t message[SABER_KEYBYTES * 8]; - uint16_t res[SABER_K][SABER_N]; - uint16_t mod_p = SABER_P - 1; - uint16_t mod_q = SABER_Q - 1; - uint16_t vprime[SABER_N]; - unsigned char msk_c[SABER_SCALEBYTES_KEM]; - - // extract the seedbytes from Public Key. - for (i = 0; i < SABER_SEEDBYTES; i++) { - seed[i] = pk[ SABER_POLYVECCOMPRESSEDBYTES + i]; - } - - GenMatrix(a, seed); - - // generate secret from constant-time binomial distribution - PQCLEAN_SABER_CLEAN_GenSecret(skpv1, noiseseed); - - // matrix-vector multiplication and rounding - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - res[i][j] = 0; - } - } - MatrixVectorMul(a, skpv1, res, SABER_Q - 1, 0); - - // now rounding - //shift right 3 bits - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - res[i][j] = ( res[i][j] + h1 ) & mod_q; - res[i][j] = (res[i][j] >> (SABER_EQ - SABER_EP) ); - } - } - - PQCLEAN_SABER_CLEAN_POLVEC2BS(ciphertext, res, SABER_P); - - // ************client matrix-vector multiplication ends************ - - // now calculate the v' - // unpack the public_key - // pkcl is the b in the protocol - PQCLEAN_SABER_CLEAN_BS2POLVEC(pk, pkcl, SABER_P); - for (i = 0; i < SABER_N; i++) { - vprime[i] = 0; - } - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - skpv1[i][j] = skpv1[i][j] & (mod_p); + res[i].coeffs[j] += h1; + res[i].coeffs[j] >>= SABER_EQ - SABER_EP; + res[i].coeffs[j] &= SABER_Q - 1; } } + PQCLEAN_SABER_CLEAN_POLVECp2BS(ciphertext, res); // vector-vector scalar multiplication with mod p - InnerProd(pkcl, skpv1, mod_p, vprime); + PQCLEAN_SABER_CLEAN_BS2POLVECp(temp, pk); + PQCLEAN_SABER_CLEAN_InnerProd(vprime, temp, s); + PQCLEAN_SABER_CLEAN_BS2POLmsg(message, m); - // addition of h1 to vprime for (i = 0; i < SABER_N; i++) { - vprime[i] = vprime[i] + h1; + vprime->coeffs[i] += h1 - (message->coeffs[i] << (SABER_EP - 1)); + vprime->coeffs[i] &= SABER_P - 1; + vprime->coeffs[i] >>= SABER_EP - SABER_ET; } - // unpack message_received; - for (j = 0; j < SABER_KEYBYTES; j++) { - for (i = 0; i < 8; i++) { - message[8 * j + i] = ((message_received[j] >> i) & 0x01); - } - } + PQCLEAN_SABER_CLEAN_POLT2BS(msk_c, vprime); +} + + +void PQCLEAN_SABER_CLEAN_indcpa_kem_dec(uint8_t m[SABER_KEYBYTES], const uint8_t sk[SABER_INDCPA_SECRETKEYBYTES], const uint8_t ciphertext[SABER_BYTES_CCA_DEC]) { + size_t i; + + poly temp[SABER_L]; + poly s[SABER_L]; + + const uint8_t *packed_cm = ciphertext + SABER_POLYVECCOMPRESSEDBYTES; + poly *v = &temp[0]; + poly *cm = &temp[1]; + + PQCLEAN_SABER_CLEAN_BS2POLVECq(s, sk); + PQCLEAN_SABER_CLEAN_BS2POLVECp(temp, ciphertext); + PQCLEAN_SABER_CLEAN_InnerProd(&temp[0], temp, s); + + PQCLEAN_SABER_CLEAN_BS2POLT(cm, packed_cm); - // message encoding for (i = 0; i < SABER_N; i++) { - message[i] = (message[i] << (SABER_EP - 1)); + v->coeffs[i] += h2 - (cm->coeffs[i] << (SABER_EP - SABER_ET)); + v->coeffs[i] &= SABER_P - 1; + v->coeffs[i] >>= SABER_EP - 1; } - for (k = 0; k < SABER_N; k++) { - vprime[k] = ( (vprime[k] - message[k]) & (mod_p) ) >> (SABER_EP - SABER_ET); - } - - - PQCLEAN_SABER_CLEAN_pack_4bit(msk_c, vprime); - - for (j = 0; j < SABER_SCALEBYTES_KEM; j++) { - ciphertext[SABER_POLYVECCOMPRESSEDBYTES + j] = msk_c[j]; - } -} - - -void PQCLEAN_SABER_CLEAN_indcpa_kem_dec(const unsigned char *sk, const unsigned char *ciphertext, unsigned char message_dec[]) { - uint32_t i, j; - // secret key of the server - uint16_t sksv[SABER_K][SABER_N]; - uint16_t pksv[SABER_K][SABER_N]; - uint8_t scale_ar[SABER_SCALEBYTES_KEM]; - uint16_t mod_p = SABER_P - 1; - uint16_t v[SABER_N]; - uint16_t op[SABER_N]; - - // sksv is the secret-key - PQCLEAN_SABER_CLEAN_BS2POLVEC(sk, sksv, SABER_Q); - // pksv is the ciphertext - PQCLEAN_SABER_CLEAN_BS2POLVEC(ciphertext, pksv, SABER_P); - - // vector-vector scalar multiplication with mod p - for (i = 0; i < SABER_N; i++) { - v[i] = 0; - } - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_N; j++) { - sksv[i][j] = sksv[i][j] & (mod_p); - } - } - InnerProd(pksv, sksv, mod_p, v); - - //Extraction - for (i = 0; i < SABER_SCALEBYTES_KEM; i++) { - scale_ar[i] = ciphertext[SABER_POLYVECCOMPRESSEDBYTES + i]; - } - - PQCLEAN_SABER_CLEAN_un_pack4bit(scale_ar, op); - - //addition of h1 - for (i = 0; i < SABER_N; i++) { - v[i] = ( ( v[i] + h2 - (op[i] << (SABER_EP - SABER_ET)) ) & (mod_p) ) >> (SABER_EP - 1); - } - - // pack decrypted message - POL2MSG(v, message_dec); -} -static void MatrixVectorMul(polyvec *a, uint16_t skpv[SABER_K][SABER_N], uint16_t res[SABER_K][SABER_N], uint16_t mod, int16_t transpose) { - uint16_t acc[SABER_N]; - int32_t i, j, k; - - if (transpose == 1) { - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_K; j++) { - PQCLEAN_SABER_CLEAN_pol_mul((uint16_t *)&a[j].vec[i], skpv[j], acc, SABER_Q, SABER_N); - - for (k = 0; k < SABER_N; k++) { - res[i][k] = res[i][k] + acc[k]; - //reduction mod p - res[i][k] = (res[i][k] & mod); - //clear the accumulator - acc[k] = 0; - } - } - } - } else { - for (i = 0; i < SABER_K; i++) { - for (j = 0; j < SABER_K; j++) { - PQCLEAN_SABER_CLEAN_pol_mul((uint16_t *)&a[i].vec[j], skpv[j], acc, SABER_Q, SABER_N); - for (k = 0; k < SABER_N; k++) { - res[i][k] = res[i][k] + acc[k]; - // reduction - res[i][k] = res[i][k] & mod; - // clear the accumulator - acc[k] = 0; - } - } - } - } -} - -static void POL2MSG(const uint16_t *message_dec_unpacked, unsigned char *message_dec) { - int32_t i, j; - - for (j = 0; j < SABER_KEYBYTES; j++) { - message_dec[j] = 0; - for (i = 0; i < 8; i++) { - message_dec[j] = message_dec[j] | (uint8_t) (message_dec_unpacked[j * 8 + i] << i); - } - } -} - - -static void InnerProd(uint16_t pkcl[SABER_K][SABER_N], uint16_t skpv[SABER_K][SABER_N], uint16_t mod, uint16_t res[SABER_N]) { - uint32_t j, k; - uint16_t acc[SABER_N]; - - // vector-vector scalar multiplication with mod p - for (j = 0; j < SABER_K; j++) { - PQCLEAN_SABER_CLEAN_pol_mul(pkcl[j], skpv[j], acc, SABER_P, SABER_N); - - for (k = 0; k < SABER_N; k++) { - res[k] = res[k] + acc[k]; - // reduction - res[k] = res[k] & mod; - // clear the accumulator - acc[k] = 0; - } - } + PQCLEAN_SABER_CLEAN_POLmsg2BS(m, v); } diff --git a/crypto_kem/saber/clean/SABER_indcpa.h b/crypto_kem/saber/clean/SABER_indcpa.h index f8503f66..a5e89e96 100644 --- a/crypto_kem/saber/clean/SABER_indcpa.h +++ b/crypto_kem/saber/clean/SABER_indcpa.h @@ -1,9 +1,13 @@ #ifndef INDCPA_H #define INDCPA_H +#include "SABER_params.h" +#include + +void PQCLEAN_SABER_CLEAN_indcpa_kem_keypair(uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES], uint8_t sk[SABER_INDCPA_SECRETKEYBYTES]); + +void PQCLEAN_SABER_CLEAN_indcpa_kem_enc(uint8_t ciphertext[SABER_BYTES_CCA_DEC], const uint8_t m[SABER_KEYBYTES], const uint8_t noiseseed[SABER_NOISESEEDBYTES], const uint8_t pk[SABER_INDCPA_PUBLICKEYBYTES]); + +void PQCLEAN_SABER_CLEAN_indcpa_kem_dec(uint8_t m[SABER_KEYBYTES], const uint8_t sk[SABER_INDCPA_SECRETKEYBYTES], const uint8_t ciphertext[SABER_BYTES_CCA_DEC]); -void PQCLEAN_SABER_CLEAN_indcpa_kem_keypair(unsigned char *pk, unsigned char *sk); -void PQCLEAN_SABER_CLEAN_indcpa_kem_enc(const unsigned char *message, unsigned char *noiseseed, const unsigned char *pk, unsigned char *ciphertext); -void PQCLEAN_SABER_CLEAN_indcpa_kem_dec(const unsigned char *sk, const unsigned char *ciphertext, unsigned char *message_dec); #endif - diff --git a/crypto_kem/saber/clean/SABER_params.h b/crypto_kem/saber/clean/SABER_params.h index faa9f6db..d1a5ddd7 100644 --- a/crypto_kem/saber/clean/SABER_params.h +++ b/crypto_kem/saber/clean/SABER_params.h @@ -1,50 +1,41 @@ #ifndef PARAMS_H #define PARAMS_H -#include "api.h" -#define SABER_K 3 +/* Don't change anything below this line */ +#define SABER_L 3 #define SABER_MU 8 #define SABER_ET 4 +#define SABER_N 256 + +#define SABER_EP 10 +#define SABER_P (1 << SABER_EP) #define SABER_EQ 13 -#define SABER_EP 10 +#define SABER_Q (1 << SABER_EQ) -#define SABER_N 256 -#define SABER_Q 8192 -#define SABER_P 1024 +#define SABER_SEEDBYTES 32 +#define SABER_NOISESEEDBYTES 32 +#define SABER_KEYBYTES 32 +#define SABER_HASHBYTES 32 -#define SABER_SEEDBYTES 32 -#define SABER_NOISESEEDBYTES 32 -#define SABER_COINBYTES 32 -#define SABER_KEYBYTES 32 +#define SABER_POLYCOINBYTES (SABER_MU * SABER_N / 8) -#define SABER_HASHBYTES 32 +#define SABER_POLYBYTES (SABER_EQ * SABER_N / 8) +#define SABER_POLYVECBYTES (SABER_L * SABER_POLYBYTES) -#define SABER_POLYBYTES 416 //13*256/8 +#define SABER_POLYCOMPRESSEDBYTES (SABER_EP * SABER_N / 8) +#define SABER_POLYVECCOMPRESSEDBYTES (SABER_L * SABER_POLYCOMPRESSEDBYTES) -#define SABER_POLYVECBYTES (SABER_K * SABER_POLYBYTES) - -#define SABER_POLYVECCOMPRESSEDBYTES (SABER_K * 320) //10*256/8 NOTE : changed till here due to parameter adaptation - -#define SABER_CIPHERTEXTBYTES (SABER_POLYVECCOMPRESSEDBYTES) - -#define SABER_SCALEBYTES (SABER_DELTA*SABER_N/8) - -#define SABER_SCALEBYTES_KEM ((SABER_ET)*SABER_N/8) +#define SABER_SCALEBYTES_KEM (SABER_ET * SABER_N / 8) #define SABER_INDCPA_PUBLICKEYBYTES (SABER_POLYVECCOMPRESSEDBYTES + SABER_SEEDBYTES) #define SABER_INDCPA_SECRETKEYBYTES (SABER_POLYVECBYTES) #define SABER_PUBLICKEYBYTES (SABER_INDCPA_PUBLICKEYBYTES) +#define SABER_SECRETKEYBYTES (SABER_INDCPA_SECRETKEYBYTES + SABER_INDCPA_PUBLICKEYBYTES + SABER_HASHBYTES + SABER_KEYBYTES) -#define SABER_SECRETKEYBYTES (SABER_INDCPA_SECRETKEYBYTES + SABER_INDCPA_PUBLICKEYBYTES + SABER_HASHBYTES + SABER_KEYBYTES) - -#define SABER_BYTES_CCA_DEC (SABER_POLYVECCOMPRESSEDBYTES + SABER_SCALEBYTES_KEM) /* Second part is for Targhi-Unruh */ - - - +#define SABER_BYTES_CCA_DEC (SABER_POLYVECCOMPRESSEDBYTES + SABER_SCALEBYTES_KEM) #endif - diff --git a/crypto_kem/saber/clean/api.h b/crypto_kem/saber/clean/api.h index 66c3b8bf..7448d46d 100644 --- a/crypto_kem/saber/clean/api.h +++ b/crypto_kem/saber/clean/api.h @@ -1,14 +1,18 @@ #ifndef PQCLEAN_SABER_CLEAN_API_H #define PQCLEAN_SABER_CLEAN_API_H + #define PQCLEAN_SABER_CLEAN_CRYPTO_ALGNAME "Saber" -#define PQCLEAN_SABER_CLEAN_CRYPTO_SECRETKEYBYTES 2304 -#define PQCLEAN_SABER_CLEAN_CRYPTO_PUBLICKEYBYTES (3*320+32) #define PQCLEAN_SABER_CLEAN_CRYPTO_BYTES 32 #define PQCLEAN_SABER_CLEAN_CRYPTO_CIPHERTEXTBYTES 1088 +#define PQCLEAN_SABER_CLEAN_CRYPTO_PUBLICKEYBYTES 992 +#define PQCLEAN_SABER_CLEAN_CRYPTO_SECRETKEYBYTES 2304 int PQCLEAN_SABER_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned char *sk); -int PQCLEAN_SABER_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk); -int PQCLEAN_SABER_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk); -#endif /* api_h */ +int PQCLEAN_SABER_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *k, const unsigned char *pk); + +int PQCLEAN_SABER_CLEAN_crypto_kem_dec(unsigned char *k, const unsigned char *ct, const unsigned char *sk); + + +#endif /* PQCLEAN_SABER_CLEAN_API_H */ diff --git a/crypto_kem/saber/clean/cbd.c b/crypto_kem/saber/clean/cbd.c index a2d9fcdd..b8dee33b 100644 --- a/crypto_kem/saber/clean/cbd.c +++ b/crypto_kem/saber/clean/cbd.c @@ -1,3 +1,7 @@ +#include "SABER_params.h" +#include "api.h" +#include "cbd.h" +#include /*--------------------------------------------------------------------- This file has been adapted from the implementation (available at, Public Domain https://github.com/pq-crystals/kyber) @@ -6,12 +10,8 @@ by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle ----------------------------------------------------------------------*/ -#include "SABER_params.h" -#include "api.h" -#include "cbd.h" -#include -static uint64_t load_littleendian(const unsigned char *x, int bytes) { +static uint64_t load_littleendian(const uint8_t *x, int bytes) { int i; uint64_t r = x[0]; for (i = 1; i < bytes; i++) { @@ -20,10 +20,7 @@ static uint64_t load_littleendian(const unsigned char *x, int bytes) { return r; } - -void PQCLEAN_SABER_CLEAN_cbd(uint16_t *r, const unsigned char *buf) { - uint16_t Qmod_minus1 = SABER_Q - 1; - +void PQCLEAN_SABER_CLEAN_cbd(uint16_t s[SABER_N], const uint8_t buf[SABER_POLYCOINBYTES]) { uint32_t t, d, a[4], b[4]; int i, j; @@ -34,18 +31,18 @@ void PQCLEAN_SABER_CLEAN_cbd(uint16_t *r, const unsigned char *buf) { d += (t >> j) & 0x11111111; } - a[0] = d & 0xf; - b[0] = (d >> 4) & 0xf; - a[1] = (d >> 8) & 0xf; + a[0] = d & 0xf; + b[0] = (d >> 4) & 0xf; + a[1] = (d >> 8) & 0xf; b[1] = (d >> 12) & 0xf; a[2] = (d >> 16) & 0xf; b[2] = (d >> 20) & 0xf; a[3] = (d >> 24) & 0xf; b[3] = (d >> 28); - r[4 * i + 0] = (uint16_t)(a[0] - b[0]) & Qmod_minus1; - r[4 * i + 1] = (uint16_t)(a[1] - b[1]) & Qmod_minus1; - r[4 * i + 2] = (uint16_t)(a[2] - b[2]) & Qmod_minus1; - r[4 * i + 3] = (uint16_t)(a[3] - b[3]) & Qmod_minus1; + s[4 * i + 0] = (uint16_t)(a[0] - b[0]); + s[4 * i + 1] = (uint16_t)(a[1] - b[1]); + s[4 * i + 2] = (uint16_t)(a[2] - b[2]); + s[4 * i + 3] = (uint16_t)(a[3] - b[3]); } } diff --git a/crypto_kem/saber/clean/cbd.h b/crypto_kem/saber/clean/cbd.h index b307921f..88b0b0b5 100644 --- a/crypto_kem/saber/clean/cbd.h +++ b/crypto_kem/saber/clean/cbd.h @@ -1,6 +1,5 @@ #ifndef CBD_H #define CBD_H - /*--------------------------------------------------------------------- This file has been adapted from the implementation (available at, Public Domain https://github.com/pq-crystals/kyber) @@ -8,10 +7,10 @@ of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle ----------------------------------------------------------------------*/ - -#include "poly.h" +#include "SABER_params.h" #include -void PQCLEAN_SABER_CLEAN_cbd(uint16_t *r, const unsigned char *buf); +void PQCLEAN_SABER_CLEAN_cbd(uint16_t s[SABER_N], const uint8_t buf[SABER_POLYCOINBYTES]); + #endif diff --git a/crypto_kem/saber/clean/kem.c b/crypto_kem/saber/clean/kem.c index 9e5b01f4..6a7f20c4 100644 --- a/crypto_kem/saber/clean/kem.c +++ b/crypto_kem/saber/clean/kem.c @@ -1,96 +1,77 @@ #include "SABER_indcpa.h" #include "SABER_params.h" +#include "api.h" #include "fips202.h" #include "randombytes.h" #include "verify.h" +#include #include -#include -#include -int PQCLEAN_SABER_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned char *sk) { - int i; - // sk[0:SABER_INDCPA_SECRETKEYBYTES-1] <-- sk - PQCLEAN_SABER_CLEAN_indcpa_kem_keypair(pk, sk); +int PQCLEAN_SABER_CLEAN_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + size_t i; - // sk[SABER_INDCPA_SECRETKEYBYTES:SABER_INDCPA_SECRETKEYBYTES+SABER_INDCPA_SECRETKEYBYTES-1] <-- pk + PQCLEAN_SABER_CLEAN_indcpa_kem_keypair(pk, sk); // sk[0:SABER_INDCPA_SECRETKEYBYTES-1] <-- sk for (i = 0; i < SABER_INDCPA_PUBLICKEYBYTES; i++) { - sk[i + SABER_INDCPA_SECRETKEYBYTES] = pk[i]; + sk[i + SABER_INDCPA_SECRETKEYBYTES] = pk[i]; // sk[SABER_INDCPA_SECRETKEYBYTES:SABER_INDCPA_SECRETKEYBYTES+SABER_INDCPA_SECRETKEYBYTES-1] <-- pk } - // Then hash(pk) is appended. - sha3_256(sk + SABER_SECRETKEYBYTES - 64, pk, SABER_INDCPA_PUBLICKEYBYTES); + sha3_256(sk + SABER_SECRETKEYBYTES - 64, pk, SABER_INDCPA_PUBLICKEYBYTES); // Then hash(pk) is appended. - // Remaining part of sk contains a pseudo-random number. - // This is output when check in crypto_kem_dec() fails. - randombytes(sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES ); + randombytes(sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES); // Remaining part of sk contains a pseudo-random number. + // This is output when check in PQCLEAN_SABER_CLEAN_crypto_kem_dec() fails. return (0); } -int PQCLEAN_SABER_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk) { - // Will contain key, coins - unsigned char kr[64]; - unsigned char buf[64]; +int PQCLEAN_SABER_CLEAN_crypto_kem_enc(uint8_t *c, uint8_t *k, const uint8_t *pk) { + + uint8_t kr[64]; // Will contain key, coins + uint8_t buf[64]; randombytes(buf, 32); - // BUF[0:31] <-- random message (will be used as the key for client) Note: hash doesnot release system RNG output - sha3_256(buf, buf, 32); + sha3_256(buf, buf, 32); // BUF[0:31] <-- random message (will be used as the key for client) Note: hash doesnot release system RNG output - // BUF[32:63] <-- Hash(public key); Multitarget countermeasure for coins + contributory KEM - sha3_256(buf + 32, pk, SABER_INDCPA_PUBLICKEYBYTES); - - // kr[0:63] <-- Hash(buf[0:63]); - sha3_512(kr, buf, 64); + sha3_256(buf + 32, pk, SABER_INDCPA_PUBLICKEYBYTES); // BUF[32:63] <-- Hash(public key); Multitarget countermeasure for coins + contributory KEM + sha3_512(kr, buf, 64); // kr[0:63] <-- Hash(buf[0:63]); // K^ <-- kr[0:31] // noiseseed (r) <-- kr[32:63]; - // buf[0:31] contains message; kr[32:63] contains randomness r; - PQCLEAN_SABER_CLEAN_indcpa_kem_enc(buf, kr + 32, pk, ct); + PQCLEAN_SABER_CLEAN_indcpa_kem_enc(c, buf, kr + 32, pk); // buf[0:31] contains message; kr[32:63] contains randomness r; - sha3_256(kr + 32, ct, SABER_BYTES_CCA_DEC); + sha3_256(kr + 32, c, SABER_BYTES_CCA_DEC); - // hash concatenation of pre-k and h(c) to k - sha3_256(ss, kr, 64); + sha3_256(k, kr, 64); // hash concatenation of pre-k and h(c) to k return (0); } +int PQCLEAN_SABER_CLEAN_crypto_kem_dec(uint8_t *k, const uint8_t *c, const uint8_t *sk) { + size_t i; + uint8_t fail; + uint8_t cmp[SABER_BYTES_CCA_DEC]; + uint8_t buf[64]; + uint8_t kr[64]; // Will contain key, coins + const uint8_t *pk = sk + SABER_INDCPA_SECRETKEYBYTES; -int PQCLEAN_SABER_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk) { - int i; - unsigned char fail; - unsigned char cmp[SABER_BYTES_CCA_DEC]; - unsigned char buf[64]; - - // Will contain key, coins - unsigned char kr[64]; - const unsigned char *pk = sk + SABER_INDCPA_SECRETKEYBYTES; - - // buf[0:31] <-- message - PQCLEAN_SABER_CLEAN_indcpa_kem_dec(sk, ct, buf); - + PQCLEAN_SABER_CLEAN_indcpa_kem_dec(buf, sk, c); // buf[0:31] <-- message // Multitarget countermeasure for coins + contributory KEM - // Save hash by storing h(pk) in sk - for (i = 0; i < 32; i++) { + for (i = 0; i < 32; i++) { // Save hash by storing h(pk) in sk buf[32 + i] = sk[SABER_SECRETKEYBYTES - 64 + i]; } sha3_512(kr, buf, 64); - PQCLEAN_SABER_CLEAN_indcpa_kem_enc(buf, kr + 32, pk, cmp); + PQCLEAN_SABER_CLEAN_indcpa_kem_enc(cmp, buf, kr + 32, pk); + fail = PQCLEAN_SABER_CLEAN_verify(c, cmp, SABER_BYTES_CCA_DEC); - fail = PQCLEAN_SABER_CLEAN_verify(ct, cmp, SABER_BYTES_CCA_DEC); - - // overwrite coins in kr with h(c) - sha3_256(kr + 32, ct, SABER_BYTES_CCA_DEC); + sha3_256(kr + 32, c, SABER_BYTES_CCA_DEC); // overwrite coins in kr with h(c) PQCLEAN_SABER_CLEAN_cmov(kr, sk + SABER_SECRETKEYBYTES - SABER_KEYBYTES, SABER_KEYBYTES, fail); - // hash concatenation of pre-k and h(c) to k - sha3_256(ss, kr, 64); + sha3_256(k, kr, 64); // hash concatenation of pre-k and h(c) to k return (0); } diff --git a/crypto_kem/saber/clean/pack_unpack.c b/crypto_kem/saber/clean/pack_unpack.c index 06a74778..89a98951 100644 --- a/crypto_kem/saber/clean/pack_unpack.c +++ b/crypto_kem/saber/clean/pack_unpack.c @@ -1,254 +1,147 @@ +#include "SABER_params.h" #include "pack_unpack.h" +#include "poly.h" +#include -void PQCLEAN_SABER_CLEAN_pack_3bit(uint8_t *bytes, const uint16_t *data) { - uint32_t j; - uint32_t offset_data, offset_byte; - - for (j = 0; j < SABER_N / 8; j++) { - offset_byte = 3 * j; - offset_data = 8 * j; - bytes[offset_byte + 0] = (data[offset_data + 0] & 0x7) | - ((data[offset_data + 1] & 0x7) << 3) | - ((data[offset_data + 2] & 0x3) << 6); - bytes[offset_byte + 1] = ((data[offset_data + 2] >> 2 ) & 0x01) | - ((data[offset_data + 3] & 0x7) << 1) | - ((data[offset_data + 4] & 0x7) << 4) | - (((data[offset_data + 5]) & 0x01) << 7); - bytes[offset_byte + 2] = ((data[offset_data + 5] >> 1 ) & 0x03) | - ((data[offset_data + 6] & 0x7) << 2) | - ((data[offset_data + 7] & 0x7) << 5); - } -} - -void PQCLEAN_SABER_CLEAN_un_pack3bit(const uint8_t *bytes, uint16_t *data) { - uint32_t j; - uint32_t offset_data, offset_byte; - - for (j = 0; j < SABER_N / 8; j++) { - offset_byte = 3 * j; - offset_data = 8 * j; - data[offset_data + 0] = (bytes[offset_byte + 0]) & 0x07; - data[offset_data + 1] = ((bytes[offset_byte + 0]) >> 3 ) & 0x07; - data[offset_data + 2] = (((bytes[offset_byte + 0]) >> 6 ) & 0x03) | - (((bytes[offset_byte + 1]) & 0x01) << 2); - data[offset_data + 3] = ((bytes[offset_byte + 1]) >> 1 ) & 0x07; - data[offset_data + 4] = ((bytes[offset_byte + 1]) >> 4 ) & 0x07; - data[offset_data + 5] = (((bytes[offset_byte + 1]) >> 7 ) & 0x01) | - (((bytes[offset_byte + 2]) & 0x03) << 1); - data[offset_data + 6] = ((bytes[offset_byte + 2] >> 2) & 0x07); - data[offset_data + 7] = ((bytes[offset_byte + 2] >> 5) & 0x07); - } -} - -void PQCLEAN_SABER_CLEAN_pack_4bit(uint8_t *bytes, const uint16_t *data) { - uint32_t j; - uint32_t offset_data; - +void PQCLEAN_SABER_CLEAN_POLT2BS(uint8_t bytes[SABER_SCALEBYTES_KEM], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; for (j = 0; j < SABER_N / 2; j++) { - offset_data = 2 * j; - bytes[j] = (data[offset_data] & 0x0f) | - ((data[offset_data + 1] & 0x0f) << 4); + out[0] = (uint8_t) ((in[0] & 0x0f) | (in[1] << 4)); + in += 2; + out += 1; } } -void PQCLEAN_SABER_CLEAN_un_pack4bit(const unsigned char *bytes, uint16_t *ar) { - uint32_t j; - uint32_t offset_data; - +void PQCLEAN_SABER_CLEAN_BS2POLT(poly *data, const uint8_t bytes[SABER_SCALEBYTES_KEM]) { + /* This function does not reduce its output mod T */ + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; for (j = 0; j < SABER_N / 2; j++) { - offset_data = 2 * j; - ar[offset_data] = bytes[j] & 0x0f; - ar[offset_data + 1] = (bytes[j] >> 4) & 0x0f; + out[0] = in[0]; + out[1] = in[0] >> 4; + in += 1; + out += 2; } } -void PQCLEAN_SABER_CLEAN_pack_6bit(uint8_t *bytes, const uint16_t *data) { - uint32_t j; - uint32_t offset_data, offset_byte; - - for (j = 0; j < SABER_N / 4; j++) { - offset_byte = 3 * j; - offset_data = 4 * j; - bytes[offset_byte + 0] = (data[offset_data + 0] & 0x3f) | - ((data[offset_data + 1] & 0x03) << 6); - bytes[offset_byte + 1] = ((data[offset_data + 1] >> 2) & 0x0f) | - ((data[offset_data + 2] & 0x0f) << 4); - bytes[offset_byte + 2] = ((data[offset_data + 2] >> 4) & 0x03) | - ((data[offset_data + 3] & 0x3f) << 2); - } -} - - -void PQCLEAN_SABER_CLEAN_un_pack6bit(const unsigned char *bytes, uint16_t *data) { - uint32_t j; - uint32_t offset_data, offset_byte; - - for (j = 0; j < SABER_N / 4; j++) { - offset_byte = 3 * j; - offset_data = 4 * j; - data[offset_data + 0] = bytes[offset_byte + 0] & 0x3f; - data[offset_data + 1] = ((bytes[offset_byte + 0] >> 6) & 0x03) | - ((bytes[offset_byte + 1] & 0x0f) << 2); - data[offset_data + 2] = ((bytes[offset_byte + 1] & 0xff) >> 4) | - ((bytes[offset_byte + 2] & 0x03) << 4); - data[offset_data + 3] = ((bytes[offset_byte + 2] & 0xff) >> 2); - } -} - - -static void POLVECp2BS(uint8_t *bytes, uint16_t data[SABER_K][SABER_N]) { - uint32_t i, j; - uint32_t offset_data, offset_byte, offset_byte1; - - for (i = 0; i < SABER_K; i++) { - offset_byte1 = i * (SABER_N * 10) / 8; - for (j = 0; j < SABER_N / 4; j++) { - offset_byte = offset_byte1 + 5 * j; - offset_data = 4 * j; - bytes[offset_byte + 0] = (data[i][offset_data + 0] & (0xff)); - bytes[offset_byte + 1] = ((data[i][offset_data + 0] >> 8) & 0x03) | - ((data[i][offset_data + 1] & 0x3f) << 2); - bytes[offset_byte + 2] = ((data[i][offset_data + 1] >> 6) & 0x0f) | - ((data[i][offset_data + 2] & 0x0f) << 4); - bytes[offset_byte + 3] = ((data[i][offset_data + 2] >> 4) & 0x3f) | - ((data[i][offset_data + 3] & 0x03) << 6); - bytes[offset_byte + 4] = ((data[i][offset_data + 3] >> 2) & 0xff); - } - } -} - -static void BS2POLVECp(const unsigned char *bytes, uint16_t data[SABER_K][SABER_N]) { - uint32_t i, j; - uint32_t offset_data, offset_byte, offset_byte1; - - for (i = 0; i < SABER_K; i++) { - offset_byte1 = i * (SABER_N * 10) / 8; - for (j = 0; j < SABER_N / 4; j++) { - offset_byte = offset_byte1 + 5 * j; - offset_data = 4 * j; - data[i][offset_data + 0] = (bytes[offset_byte + 0] & (0xff)) | - ((bytes[offset_byte + 1] & 0x03) << 8); - data[i][offset_data + 1] = ((bytes[offset_byte + 1] >> 2) & (0x3f)) | - ((bytes[offset_byte + 2] & 0x0f) << 6); - data[i][offset_data + 2] = ((bytes[offset_byte + 2] >> 4) & (0x0f)) | - ((bytes[offset_byte + 3] & 0x3f) << 4); - data[i][offset_data + 3] = ((bytes[offset_byte + 3] >> 6) & (0x03)) | - ((bytes[offset_byte + 4] & 0xff) << 2); - } - } -} - - - -static void POLVECq2BS(uint8_t *bytes, uint16_t data[SABER_K][SABER_N]) { - uint32_t i, j; - uint32_t offset_data, offset_byte, offset_byte1; - - for (i = 0; i < SABER_K; i++) { - offset_byte1 = i * (SABER_N * 13) / 8; - for (j = 0; j < SABER_N / 8; j++) { - offset_byte = offset_byte1 + 13 * j; - offset_data = 8 * j; - bytes[offset_byte + 0] = (data[i][offset_data + 0] & (0xff)); - bytes[offset_byte + 1] = ((data[i][offset_data + 0] >> 8) & 0x1f) | - ((data[i][offset_data + 1] & 0x07) << 5); - bytes[offset_byte + 2] = ((data[i][offset_data + 1] >> 3) & 0xff); - bytes[offset_byte + 3] = ((data[i][offset_data + 1] >> 11) & 0x03) | - ((data[i][offset_data + 2] & 0x3f) << 2); - bytes[offset_byte + 4] = ((data[i][offset_data + 2] >> 6) & 0x7f) | - ((data[i][offset_data + 3] & 0x01) << 7); - bytes[offset_byte + 5] = ((data[i][offset_data + 3] >> 1) & 0xff); - bytes[offset_byte + 6] = ((data[i][offset_data + 3] >> 9) & 0x0f) | - ((data[i][offset_data + 4] & 0x0f) << 4); - bytes[offset_byte + 7] = ((data[i][offset_data + 4] >> 4) & 0xff); - bytes[offset_byte + 8] = ((data[i][offset_data + 4] >> 12) & 0x01) | - ((data[i][offset_data + 5] & 0x7f) << 1); - bytes[offset_byte + 9] = ((data[i][offset_data + 5] >> 7) & 0x3f) | - ((data[i][offset_data + 6] & 0x03) << 6); - bytes[offset_byte + 10] = ((data[i][offset_data + 6] >> 2) & 0xff); - bytes[offset_byte + 11] = ((data[i][offset_data + 6] >> 10) & 0x07) | - ((data[i][offset_data + 7] & 0x1f) << 3); - bytes[offset_byte + 12] = ((data[i][offset_data + 7] >> 5) & 0xff); - } - } -} - -static void BS2POLVECq(const unsigned char *bytes, uint16_t data[SABER_K][SABER_N]) { - uint32_t i, j; - uint32_t offset_data, offset_byte, offset_byte1; - - for (i = 0; i < SABER_K; i++) { - offset_byte1 = i * (SABER_N * 13) / 8; - for (j = 0; j < SABER_N / 8; j++) { - offset_byte = offset_byte1 + 13 * j; - offset_data = 8 * j; - data[i][offset_data + 0] = (bytes[offset_byte + 0] & (0xff)) | - ((bytes[offset_byte + 1] & 0x1f) << 8); - data[i][offset_data + 1] = (bytes[offset_byte + 1] >> 5 & (0x07)) | - ((bytes[offset_byte + 2] & 0xff) << 3) | - ((bytes[offset_byte + 3] & 0x03) << 11); - data[i][offset_data + 2] = (bytes[offset_byte + 3] >> 2 & (0x3f)) | - ((bytes[offset_byte + 4] & 0x7f) << 6); - data[i][offset_data + 3] = (bytes[offset_byte + 4] >> 7 & (0x01)) | - ((bytes[offset_byte + 5] & 0xff) << 1) | - ((bytes[offset_byte + 6] & 0x0f) << 9); - data[i][offset_data + 4] = (bytes[offset_byte + 6] >> 4 & (0x0f)) | - ((bytes[offset_byte + 7] & 0xff) << 4) | - ((bytes[offset_byte + 8] & 0x01) << 12); - data[i][offset_data + 5] = (bytes[offset_byte + 8] >> 1 & (0x7f)) | - ((bytes[offset_byte + 9] & 0x3f) << 7); - data[i][offset_data + 6] = (bytes[offset_byte + 9] >> 6 & (0x03)) | - ((bytes[offset_byte + 10] & 0xff) << 2) | - ((bytes[offset_byte + 11] & 0x07) << 10); - data[i][offset_data + 7] = (bytes[offset_byte + 11] >> 3 & (0x1f)) | - ((bytes[offset_byte + 12] & 0xff) << 5); - } - } -} - -//only BS2POLq no BS2POLp -void PQCLEAN_SABER_CLEAN_BS2POL(const unsigned char *bytes, uint16_t data[SABER_N]) { - uint32_t j; - uint32_t offset_data, offset_byte; - +static void POLq2BS(uint8_t bytes[SABER_POLYBYTES], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; for (j = 0; j < SABER_N / 8; j++) { - offset_byte = 13 * j; - offset_data = 8 * j; - data[offset_data + 0] = (bytes[offset_byte + 0] & (0xff)) | - ((bytes[offset_byte + 1] & 0x1f) << 8); - data[offset_data + 1] = (bytes[offset_byte + 1] >> 5 & (0x07)) | - ((bytes[offset_byte + 2] & 0xff) << 3) | - ((bytes[offset_byte + 3] & 0x03) << 11); - data[offset_data + 2] = (bytes[offset_byte + 3] >> 2 & (0x3f)) | - ((bytes[offset_byte + 4] & 0x7f) << 6); - data[offset_data + 3] = (bytes[offset_byte + 4] >> 7 & (0x01)) | - ((bytes[offset_byte + 5] & 0xff) << 1) | - ((bytes[offset_byte + 6] & 0x0f) << 9); - data[offset_data + 4] = (bytes[offset_byte + 6] >> 4 & (0x0f)) | - ((bytes[offset_byte + 7] & 0xff) << 4) | - ((bytes[offset_byte + 8] & 0x01) << 12); - data[offset_data + 5] = (bytes[offset_byte + 8] >> 1 & (0x7f)) | - ((bytes[offset_byte + 9] & 0x3f) << 7); - data[offset_data + 6] = (bytes[offset_byte + 9] >> 6 & (0x03)) | - ((bytes[offset_byte + 10] & 0xff) << 2) | - ((bytes[offset_byte + 11] & 0x07) << 10); - data[offset_data + 7] = (bytes[offset_byte + 11] >> 3 & (0x1f)) | - ((bytes[offset_byte + 12] & 0xff) << 5); + out[0] = (uint8_t) (in[0]); + out[1] = (uint8_t) (((in[0] >> 8) & 0x1f) | (in[1] << 5)); + out[2] = (uint8_t) (in[1] >> 3); + out[3] = (uint8_t) (((in[1] >> 11) & 0x03) | (in[2] << 2)); + out[4] = (uint8_t) (((in[2] >> 6) & 0x7f) | (in[3] << 7)); + out[5] = (uint8_t) (in[3] >> 1); + out[6] = (uint8_t) (((in[3] >> 9) & 0x0f) | (in[4] << 4)); + out[7] = (uint8_t) (in[4] >> 4); + out[8] = (uint8_t) (((in[4] >> 12) & 0x01) | (in[5] << 1)); + out[9] = (uint8_t) (((in[5] >> 7) & 0x3f) | (in[6] << 6)); + out[10] = (uint8_t) (in[6] >> 2); + out[11] = (uint8_t) (((in[6] >> 10) & 0x07) | (in[7] << 3)); + out[12] = (uint8_t) (in[7] >> 5); + in += 8; + out += 13; } } -void PQCLEAN_SABER_CLEAN_POLVEC2BS(uint8_t *bytes, uint16_t data[SABER_K][SABER_N], uint16_t modulus) { - if (modulus == 1024) { - POLVECp2BS(bytes, data); - } else if (modulus == 8192) { - POLVECq2BS(bytes, data); +static void BS2POLq(poly *data, const uint8_t bytes[SABER_POLYBYTES]) { + /* This function does not reduce its output mod Q */ + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; + for (j = 0; j < SABER_N / 8; j++) { + out[0] = (in[0]) | (in[1] << 8); + out[1] = (in[1] >> 5) | (in[2] << 3) | (in[3] << 11); + out[2] = (in[3] >> 2) | (in[4] << 6); + out[3] = (in[4] >> 7) | (in[5] << 1) | (in[6] << 9); + out[4] = (in[6] >> 4) | (in[7] << 4) | (in[8] << 12); + out[5] = (in[8] >> 1) | (in[9] << 7); + out[6] = (in[9] >> 6) | (in[10] << 2) | (in[11] << 10); + out[7] = (in[11] >> 3) | (in[12] << 5); + in += 13; + out += 8; } } -void PQCLEAN_SABER_CLEAN_BS2POLVEC(const unsigned char *bytes, uint16_t data[SABER_K][SABER_N], uint16_t modulus) { - if (modulus == 1024) { - BS2POLVECp(bytes, data); - } else if (modulus == 8192) { - BS2POLVECq(bytes, data); +static void POLp2BS(uint8_t bytes[SABER_POLYCOMPRESSEDBYTES], const poly *data) { + size_t j; + const uint16_t *in = data->coeffs; + uint8_t *out = bytes; + for (j = 0; j < SABER_N / 4; j++) { + out[0] = (uint8_t) (in[0]); + out[1] = (uint8_t) (((in[0] >> 8) & 0x03) | (in[1] << 2)); + out[2] = (uint8_t) (((in[1] >> 6) & 0x0f) | (in[2] << 4)); + out[3] = (uint8_t) (((in[2] >> 4) & 0x3f) | (in[3] << 6)); + out[4] = (uint8_t) (in[3] >> 2); + in += 4; + out += 5; + } +} + +static void BS2POLp(poly *data, const uint8_t bytes[SABER_POLYCOMPRESSEDBYTES]) { + size_t j; + const uint8_t *in = bytes; + uint16_t *out = data->coeffs; + for (j = 0; j < SABER_N / 4; j++) { + out[0] = in[0] | (in[1] << 8); + out[1] = (in[1] >> 2) | (in[2] << 6); + out[2] = (in[2] >> 4) | (in[3] << 4); + out[3] = (in[3] >> 6) | (in[4] << 2); + in += 5; + out += 4; + } +} + +void PQCLEAN_SABER_CLEAN_POLVECq2BS(uint8_t bytes[SABER_POLYVECBYTES], const poly data[SABER_L]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + POLq2BS(bytes + i * SABER_POLYBYTES, &data[i]); + } +} + +void PQCLEAN_SABER_CLEAN_BS2POLVECq(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECBYTES]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + BS2POLq(&data[i], bytes + i * SABER_POLYBYTES); + } +} + +void PQCLEAN_SABER_CLEAN_POLVECp2BS(uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES], const poly data[SABER_L]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + POLp2BS(bytes + i * SABER_POLYCOMPRESSEDBYTES, &data[i]); + } +} + +void PQCLEAN_SABER_CLEAN_BS2POLVECp(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES]) { + size_t i; + for (i = 0; i < SABER_L; i++) { + BS2POLp(&data[i], bytes + i * SABER_POLYCOMPRESSEDBYTES); + } +} + +void PQCLEAN_SABER_CLEAN_BS2POLmsg(poly *data, const uint8_t bytes[SABER_KEYBYTES]) { + size_t i, j; + for (j = 0; j < SABER_KEYBYTES; j++) { + for (i = 0; i < 8; i++) { + data->coeffs[j * 8 + i] = ((bytes[j] >> i) & 0x01); + } + } +} + +void PQCLEAN_SABER_CLEAN_POLmsg2BS(uint8_t bytes[SABER_KEYBYTES], const poly *data) { + size_t i, j; + memset(bytes, 0, SABER_KEYBYTES); + + for (j = 0; j < SABER_KEYBYTES; j++) { + for (i = 0; i < 8; i++) { + bytes[j] = bytes[j] | ((data->coeffs[j * 8 + i] & 0x01) << i); + } } } diff --git a/crypto_kem/saber/clean/pack_unpack.h b/crypto_kem/saber/clean/pack_unpack.h index 2431a217..fc6a3abf 100644 --- a/crypto_kem/saber/clean/pack_unpack.h +++ b/crypto_kem/saber/clean/pack_unpack.h @@ -1,28 +1,28 @@ #ifndef PACK_UNPACK_H #define PACK_UNPACK_H - #include "SABER_params.h" +#include "poly.h" #include #include +void PQCLEAN_SABER_CLEAN_POLT2BS(uint8_t bytes[SABER_SCALEBYTES_KEM], const poly *data); -void PQCLEAN_SABER_CLEAN_pack_3bit(uint8_t *bytes, const uint16_t *data); - -void PQCLEAN_SABER_CLEAN_un_pack3bit(const uint8_t *bytes, uint16_t *data); - -void PQCLEAN_SABER_CLEAN_pack_4bit(uint8_t *bytes, const uint16_t *data); - -void PQCLEAN_SABER_CLEAN_un_pack4bit(const unsigned char *bytes, uint16_t *ar); - -void PQCLEAN_SABER_CLEAN_pack_6bit(uint8_t *bytes, const uint16_t *data); - -void PQCLEAN_SABER_CLEAN_un_pack6bit(const unsigned char *bytes, uint16_t *data); +void PQCLEAN_SABER_CLEAN_BS2POLT(poly *data, const uint8_t bytes[SABER_SCALEBYTES_KEM]); -void PQCLEAN_SABER_CLEAN_BS2POL(const unsigned char *bytes, uint16_t data[SABER_N]); +void PQCLEAN_SABER_CLEAN_POLVECq2BS(uint8_t bytes[SABER_POLYVECBYTES], const poly data[SABER_L]); -void PQCLEAN_SABER_CLEAN_POLVEC2BS(uint8_t *bytes, uint16_t data[SABER_K][SABER_N], uint16_t modulus); +void PQCLEAN_SABER_CLEAN_POLVECp2BS(uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES], const poly data[SABER_L]); + + +void PQCLEAN_SABER_CLEAN_BS2POLVECq(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECBYTES]); + +void PQCLEAN_SABER_CLEAN_BS2POLVECp(poly data[SABER_L], const uint8_t bytes[SABER_POLYVECCOMPRESSEDBYTES]); + + +void PQCLEAN_SABER_CLEAN_BS2POLmsg(poly *data, const uint8_t bytes[SABER_KEYBYTES]); + +void PQCLEAN_SABER_CLEAN_POLmsg2BS(uint8_t bytes[SABER_KEYBYTES], const poly *data); -void PQCLEAN_SABER_CLEAN_BS2POLVEC(const unsigned char *bytes, uint16_t data[SABER_K][SABER_N], uint16_t modulus); #endif diff --git a/crypto_kem/saber/clean/poly.c b/crypto_kem/saber/clean/poly.c index 93f55fde..588d0c99 100644 --- a/crypto_kem/saber/clean/poly.c +++ b/crypto_kem/saber/clean/poly.c @@ -1,21 +1,57 @@ -/*--------------------------------------------------------------------- -This file has been adapted from the implementation -(available at, Public Domain https://github.com/pq-crystals/kyber) -of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" -by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, -Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle -----------------------------------------------------------------------*/ -#include "SABER_params.h" +#include "api.h" #include "cbd.h" #include "fips202.h" +#include "pack_unpack.h" #include "poly.h" +#include -void PQCLEAN_SABER_CLEAN_GenSecret(uint16_t r[SABER_K][SABER_N], const unsigned char *seed) { - uint8_t buf[SABER_MU * SABER_N * SABER_K / 8]; +void PQCLEAN_SABER_CLEAN_MatrixVectorMul(poly c[SABER_L], const poly A[SABER_L][SABER_L], const poly s[SABER_L], int16_t transpose) { + size_t i, j; + + if (transpose) { + for (i = 0; i < SABER_L; i++) { + PQCLEAN_SABER_CLEAN_poly_mul(&c[i], &A[0][i], &s[0], 0); + for (j = 1; j < SABER_L; j++) { + PQCLEAN_SABER_CLEAN_poly_mul(&c[i], &A[j][i], &s[j], 1); + } + } + } else { + for (i = 0; i < SABER_L; i++) { + PQCLEAN_SABER_CLEAN_poly_mul(&c[i], &A[i][0], &s[0], 0); + for (j = 1; j < SABER_L; j++) { + PQCLEAN_SABER_CLEAN_poly_mul(&c[i], &A[i][j], &s[j], 1); + } + } + } +} + +void PQCLEAN_SABER_CLEAN_InnerProd(poly *c, const poly b[SABER_L], const poly s[SABER_L]) { + size_t i; + + PQCLEAN_SABER_CLEAN_poly_mul(c, &b[0], &s[0], 0); + for (i = 1; i < SABER_L; i++) { + PQCLEAN_SABER_CLEAN_poly_mul(c, &b[i], &s[i], 1); + } +} + +void PQCLEAN_SABER_CLEAN_GenMatrix(poly A[SABER_L][SABER_L], const uint8_t seed[SABER_SEEDBYTES]) { + size_t i; + uint8_t buf[SABER_L * SABER_POLYVECBYTES]; + + shake128(buf, sizeof(buf), seed, SABER_SEEDBYTES); + + for (i = 0; i < SABER_L; i++) { + PQCLEAN_SABER_CLEAN_BS2POLVECq(A[i], buf + i * SABER_POLYVECBYTES); + } +} + +void PQCLEAN_SABER_CLEAN_GenSecret(poly s[SABER_L], const uint8_t seed[SABER_NOISESEEDBYTES]) { + size_t i; + uint8_t buf[SABER_L * SABER_POLYCOINBYTES]; shake128(buf, sizeof(buf), seed, SABER_NOISESEEDBYTES); - for (size_t i = 0; i < SABER_K; i++) { - PQCLEAN_SABER_CLEAN_cbd(r[i], buf + i * SABER_MU * SABER_N / 8); + for (i = 0; i < SABER_L; i++) { + PQCLEAN_SABER_CLEAN_cbd(s[i].coeffs, buf + i * SABER_POLYCOINBYTES); } } diff --git a/crypto_kem/saber/clean/poly.h b/crypto_kem/saber/clean/poly.h index 9d216804..d365b489 100644 --- a/crypto_kem/saber/clean/poly.h +++ b/crypto_kem/saber/clean/poly.h @@ -1,26 +1,23 @@ #ifndef POLY_H #define POLY_H - -/*--------------------------------------------------------------------- -This file has been adapted from the implementation -(available at, Public Domain https://github.com/pq-crystals/kyber) -of "CRYSTALS – Kyber: a CCA-secure module-lattice-based KEM" -by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, -Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle -----------------------------------------------------------------------*/ - - #include "SABER_params.h" #include -typedef struct { +typedef union { uint16_t coeffs[SABER_N]; } poly; -typedef struct { - poly vec[SABER_K]; -} polyvec; -void PQCLEAN_SABER_CLEAN_GenSecret(uint16_t r[SABER_K][SABER_N], const unsigned char *seed); +void PQCLEAN_SABER_CLEAN_MatrixVectorMul(poly c[SABER_L], const poly A[SABER_L][SABER_L], const poly s[SABER_L], int16_t transpose); + +void PQCLEAN_SABER_CLEAN_InnerProd(poly *c, const poly b[SABER_L], const poly s[SABER_L]); + +void PQCLEAN_SABER_CLEAN_GenMatrix(poly A[SABER_L][SABER_L], const uint8_t seed[SABER_SEEDBYTES]); + +void PQCLEAN_SABER_CLEAN_GenSecret(poly s[SABER_L], const uint8_t seed[SABER_NOISESEEDBYTES]); + + +void PQCLEAN_SABER_CLEAN_poly_mul(poly *c, const poly *a, const poly *b, int accumulate); + #endif diff --git a/crypto_kem/saber/clean/poly_mul.c b/crypto_kem/saber/clean/poly_mul.c index dc1cc779..0e03ff99 100644 --- a/crypto_kem/saber/clean/poly_mul.c +++ b/crypto_kem/saber/clean/poly_mul.c @@ -1,4 +1,4 @@ -#include "poly_mul.h" +#include "poly.h" #include #include @@ -11,13 +11,13 @@ #define OVERFLOWING_MUL(X, Y) ((uint16_t)((uint32_t)(X) * (uint32_t)(Y))) #define KARATSUBA_N 64 -static void karatsuba_simple(const uint16_t *a_1, const uint16_t *b_1, uint16_t *result_final) { +static void karatsuba_simple(uint16_t *result_final, const uint16_t *a_1, const uint16_t *b_1) { uint16_t d01[KARATSUBA_N / 2 - 1]; uint16_t d0123[KARATSUBA_N / 2 - 1]; uint16_t d23[KARATSUBA_N / 2 - 1]; uint16_t result_d01[KARATSUBA_N - 1]; - int32_t i, j; + size_t i, j; memset(result_d01, 0, (KARATSUBA_N - 1)*sizeof(uint16_t)); memset(d01, 0, (KARATSUBA_N / 2 - 1)*sizeof(uint16_t)); @@ -110,7 +110,7 @@ static void karatsuba_simple(const uint16_t *a_1, const uint16_t *b_1, uint16_t -static void toom_cook_4way (const uint16_t *a1, const uint16_t *b1, uint16_t *result) { +static void toom_cook_4way (uint16_t *result, const uint16_t *a1, const uint16_t *b1) { uint16_t inv3 = 43691, inv9 = 36409, inv15 = 61167; uint16_t aw1[N_SB], aw2[N_SB], aw3[N_SB], aw4[N_SB], aw5[N_SB], aw6[N_SB], aw7[N_SB]; @@ -181,13 +181,13 @@ static void toom_cook_4way (const uint16_t *a1, const uint16_t *b1, uint16_t *re // MULTIPLICATION - karatsuba_simple(aw1, bw1, w1); - karatsuba_simple(aw2, bw2, w2); - karatsuba_simple(aw3, bw3, w3); - karatsuba_simple(aw4, bw4, w4); - karatsuba_simple(aw5, bw5, w5); - karatsuba_simple(aw6, bw6, w6); - karatsuba_simple(aw7, bw7, w7); + karatsuba_simple(w1, aw1, bw1); + karatsuba_simple(w2, aw2, bw2); + karatsuba_simple(w3, aw3, bw3); + karatsuba_simple(w4, aw4, bw4); + karatsuba_simple(w5, aw5, bw5); + karatsuba_simple(w6, aw6, bw6); + karatsuba_simple(w7, aw7, bw7); // INTERPOLATION for (i = 0; i < N_SB_RES; ++i) { @@ -228,19 +228,21 @@ static void toom_cook_4way (const uint16_t *a1, const uint16_t *b1, uint16_t *re } } -void PQCLEAN_SABER_CLEAN_pol_mul(uint16_t *a, uint16_t *b, uint16_t *res, uint16_t p, uint32_t n) { - uint32_t i; - // normal multiplication - uint16_t c[512]; +/* res += a*b */ +void PQCLEAN_SABER_CLEAN_poly_mul(poly *c, const poly *a, const poly *b, const int accumulate) { + uint16_t C[2 * SABER_N] = {0}; + size_t i; - for (i = 0; i < 512; i++) { - c[i] = 0; - } + toom_cook_4way(C, a->coeffs, b->coeffs); - toom_cook_4way(a, b, c); - - // reduction - for (i = n; i < 2 * n; i++) { - res[i - n] = (c[i - n] - c[i]) & (p - 1); + /* reduction */ + if (accumulate == 0) { + for (i = SABER_N; i < 2 * SABER_N; i++) { + c->coeffs[i - SABER_N] = (C[i - SABER_N] - C[i]); + } + } else { + for (i = SABER_N; i < 2 * SABER_N; i++) { + c->coeffs[i - SABER_N] += (C[i - SABER_N] - C[i]); + } } } diff --git a/crypto_kem/saber/clean/poly_mul.h b/crypto_kem/saber/clean/poly_mul.h index f813be10..b28b04f6 100644 --- a/crypto_kem/saber/clean/poly_mul.h +++ b/crypto_kem/saber/clean/poly_mul.h @@ -1,9 +1,3 @@ -#ifndef POLYMUL_H -#define POLYMUL_H -#include "SABER_params.h" -#include -void PQCLEAN_SABER_CLEAN_pol_mul(uint16_t *a, uint16_t *b, uint16_t *res, uint16_t p, uint32_t n); -#endif diff --git a/crypto_kem/saber/clean/verify.c b/crypto_kem/saber/clean/verify.c index 81f30604..72f4dd34 100644 --- a/crypto_kem/saber/clean/verify.c +++ b/crypto_kem/saber/clean/verify.c @@ -1,3 +1,5 @@ +#include "verify.h" + /*------------------------------------------------- This file has been adapted from the implementation (available at https://github.com/pq-crystals/kyber) of @@ -5,26 +7,25 @@ This file has been adapted from the implementation by : Joppe Bos, Leo Ducas, Eike Kiltz, Tancrede Lepoint, Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle ----------------------------------------------------*/ -#include "verify.h" -#include + /* returns 0 for equal strings, 1 for non-equal strings */ -unsigned char PQCLEAN_SABER_CLEAN_verify(const unsigned char *a, const unsigned char *b, size_t len) { +uint8_t PQCLEAN_SABER_CLEAN_verify(const uint8_t *a, const uint8_t *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; + return (uint8_t) r; } /* b = 1 means mov, b = 0 means don't mov*/ -void PQCLEAN_SABER_CLEAN_cmov(unsigned char *r, const unsigned char *x, size_t len, unsigned char b) { +void PQCLEAN_SABER_CLEAN_cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b) { size_t i; b = -b; diff --git a/crypto_kem/saber/clean/verify.h b/crypto_kem/saber/clean/verify.h index cacb2ee6..f88fe396 100644 --- a/crypto_kem/saber/clean/verify.h +++ b/crypto_kem/saber/clean/verify.h @@ -1,6 +1,5 @@ #ifndef VERIFY_H #define VERIFY_H - /*------------------------------------------------- This file has been adapted from the implementation (available at https://github.com/pq-crystals/kyber) of @@ -13,9 +12,11 @@ Vadim Lyubashevsky, John M. Schanck, Peter Schwabe & Damien stehle #include /* returns 0 for equal strings, 1 for non-equal strings */ -unsigned char PQCLEAN_SABER_CLEAN_verify(const unsigned char *a, const unsigned char *b, size_t len); +uint8_t PQCLEAN_SABER_CLEAN_verify(const uint8_t *a, const uint8_t *b, size_t len); + /* b = 1 means mov, b = 0 means don't mov*/ -void PQCLEAN_SABER_CLEAN_cmov(unsigned char *r, const unsigned char *x, size_t len, unsigned char b); +void PQCLEAN_SABER_CLEAN_cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b); + #endif diff --git a/test/duplicate_consistency/firesaber_avx2.yml b/test/duplicate_consistency/firesaber_avx2.yml new file mode 100644 index 00000000..f5240334 --- /dev/null +++ b/test/duplicate_consistency/firesaber_avx2.yml @@ -0,0 +1,15 @@ +consistency_checks: + - source: + scheme: firesaber + implementation: clean + files: + - api.h + - cbd.h + - pack_unpack.h + - SABER_indcpa.h + - SABER_params.h + - verify.h + - cbd.c + - kem.c + - pack_unpack.c + - verify.c diff --git a/test/duplicate_consistency/firesaber_clean.yml b/test/duplicate_consistency/firesaber_clean.yml index 60a1a153..bcfed7c0 100644 --- a/test/duplicate_consistency/firesaber_clean.yml +++ b/test/duplicate_consistency/firesaber_clean.yml @@ -1,31 +1,15 @@ consistency_checks: -- source: - scheme: lightsaber - implementation: clean - files: - - cbd.h - - kem.c - - pack_unpack.c - - pack_unpack.h - - poly.c - - poly.h - - poly_mul.c - - poly_mul.h - - SABER_indcpa.h - - verify.c - - verify.h -- source: - scheme: saber - implementation: clean - files: - - cbd.h - - kem.c - - pack_unpack.c - - pack_unpack.h - - poly.c - - poly.h - - poly_mul.c - - poly_mul.h - - SABER_indcpa.h - - verify.c - - verify.h + - source: + scheme: firesaber + implementation: avx2 + files: + - api.h + - cbd.h + - pack_unpack.h + - SABER_indcpa.h + - SABER_params.h + - verify.h + - cbd.c + - kem.c + - pack_unpack.c + - verify.c diff --git a/test/duplicate_consistency/lightsaber_avx2.yml b/test/duplicate_consistency/lightsaber_avx2.yml new file mode 100644 index 00000000..dd6e9fed --- /dev/null +++ b/test/duplicate_consistency/lightsaber_avx2.yml @@ -0,0 +1,63 @@ +consistency_checks: + - source: + scheme: lightsaber + implementation: clean + files: + - api.h + - cbd.h + - pack_unpack.h + - SABER_indcpa.h + - SABER_params.h + - verify.h + - cbd.c + - kem.c + - pack_unpack.c + - verify.c + - source: + scheme: saber + implementation: clean + files: + - cbd.h + - pack_unpack.h + - SABER_indcpa.h + - verify.h + - kem.c + - verify.c + - source: + scheme: saber + implementation: avx2 + files: + - cbd.h + - pack_unpack.h + - poly.h + - SABER_indcpa.h + - verify.h + - kem.c + - poly.c + - poly_mul.c + - SABER_indcpa.c + - verify.c + - source: + scheme: firesaber + implementation: clean + files: + - cbd.h + - pack_unpack.h + - SABER_indcpa.h + - verify.h + - kem.c + - verify.c + - source: + scheme: firesaber + implementation: avx2 + files: + - cbd.h + - pack_unpack.h + - poly.h + - SABER_indcpa.h + - verify.h + - kem.c + - poly.c + - poly_mul.c + - SABER_indcpa.c + - verify.c diff --git a/test/duplicate_consistency/lightsaber_clean.yml b/test/duplicate_consistency/lightsaber_clean.yml index a4d483be..2f36ec86 100644 --- a/test/duplicate_consistency/lightsaber_clean.yml +++ b/test/duplicate_consistency/lightsaber_clean.yml @@ -1,31 +1,65 @@ consistency_checks: -- source: - scheme: saber - implementation: clean - files: - - cbd.h - - kem.c - - pack_unpack.c - - pack_unpack.h - - poly.c - - poly.h - - poly_mul.c - - poly_mul.h - - SABER_indcpa.h - - verify.c - - verify.h -- source: - scheme: firesaber - implementation: clean - files: - - cbd.h - - kem.c - - pack_unpack.c - - pack_unpack.h - - poly.c - - poly.h - - poly_mul.c - - poly_mul.h - - SABER_indcpa.h - - verify.c - - verify.h + - source: + scheme: lightsaber + implementation: avx2 + files: + - api.h + - cbd.h + - pack_unpack.h + - SABER_indcpa.h + - SABER_params.h + - verify.h + - cbd.c + - kem.c + - pack_unpack.c + - verify.c + - source: + scheme: saber + implementation: clean + files: + - cbd.h + - pack_unpack.h + - poly.h + - poly_mul.h + - SABER_indcpa.h + - verify.h + - kem.c + - poly.c + - poly_mul.c + - SABER_indcpa.c + - verify.c + - source: + scheme: saber + implementation: avx2 + files: + - cbd.h + - pack_unpack.h + - SABER_indcpa.h + - verify.h + - kem.c + - verify.c + - source: + scheme: firesaber + implementation: clean + files: + - cbd.h + - pack_unpack.h + - poly.h + - poly_mul.h + - SABER_indcpa.h + - verify.h + - kem.c + - poly.c + - poly_mul.c + - SABER_indcpa.c + - verify.c + - source: + scheme: firesaber + implementation: avx2 + files: + - cbd.h + - pack_unpack.h + - SABER_indcpa.h + - verify.h + - kem.c + - verify.c diff --git a/test/duplicate_consistency/saber_avx2.yml b/test/duplicate_consistency/saber_avx2.yml new file mode 100644 index 00000000..0b4b60d7 --- /dev/null +++ b/test/duplicate_consistency/saber_avx2.yml @@ -0,0 +1,39 @@ +consistency_checks: + - source: + scheme: saber + implementation: clean + files: + - api.h + - cbd.h + - pack_unpack.h + - SABER_indcpa.h + - SABER_params.h + - verify.h + - cbd.c + - kem.c + - pack_unpack.c + - verify.c + - source: + scheme: firesaber + implementation: clean + files: + - cbd.h + - pack_unpack.h + - SABER_indcpa.h + - verify.h + - kem.c + - verify.c + - source: + scheme: firesaber + implementation: avx2 + files: + - cbd.h + - pack_unpack.h + - poly.h + - SABER_indcpa.h + - verify.h + - kem.c + - poly.c + - poly_mul.c + - SABER_indcpa.c + - verify.c diff --git a/test/duplicate_consistency/saber_clean.yml b/test/duplicate_consistency/saber_clean.yml index 0e1b89dd..7f5ba121 100644 --- a/test/duplicate_consistency/saber_clean.yml +++ b/test/duplicate_consistency/saber_clean.yml @@ -1,31 +1,40 @@ consistency_checks: -- source: - scheme: lightsaber - implementation: clean - files: - - cbd.h - - kem.c - - pack_unpack.c - - pack_unpack.h - - poly.c - - poly.h - - poly_mul.c - - poly_mul.h - - SABER_indcpa.h - - verify.c - - verify.h -- source: - scheme: firesaber - implementation: clean - files: - - cbd.h - - kem.c - - pack_unpack.c - - pack_unpack.h - - poly.c - - poly.h - - poly_mul.c - - poly_mul.h - - SABER_indcpa.h - - verify.c - - verify.h + - source: + scheme: saber + implementation: avx2 + files: + - api.h + - cbd.h + - pack_unpack.h + - SABER_indcpa.h + - SABER_params.h + - verify.h + - cbd.c + - kem.c + - pack_unpack.c + - verify.c + - source: + scheme: firesaber + implementation: clean + files: + - cbd.h + - pack_unpack.h + - poly.h + - poly_mul.h + - SABER_indcpa.h + - verify.h + - kem.c + - poly.c + - poly_mul.c + - SABER_indcpa.c + - verify.c + - source: + scheme: firesaber + implementation: avx2 + files: + - cbd.h + - pack_unpack.h + - SABER_indcpa.h + - verify.h + - kem.c + - verify.c