pqc/crypto_kem/ntrulpr761/avx2/kem.c

288 lines
7.5 KiB
C

#include "api.h"
#include "crypto_sort_uint32.h"
#include "crypto_stream_aes256ctr.h"
#include "params.h"
#include "randombytes.h"
#include "sha2.h"
#define int8 int8_t
#define int16 int16_t
#define int32 int32_t
#define uint16 uint16_t
#define uint32 uint32_t
#define uint64 uint64_t
/* ----- masks */
/* return -1 if x<0; otherwise return 0 */
static int int16_negative_mask(int16 x) {
uint16 u = (uint16) x;
u >>= 15;
return -(int) u;
/* alternative with gcc -fwrapv: */
/* x>>15 compiles to CPU's arithmetic right shift */
}
/* ----- arithmetic mod 3 */
typedef int8 small;
/* F3 is always represented as -1,0,1 */
/* ----- arithmetic mod q */
#define q12 ((q-1)/2)
typedef int16 Fq;
/* works for -14000000 < x < 14000000 if q in 4591, 4621, 5167 */
/* assumes twos complement; use, e.g., gcc -fwrapv */
static Fq Fq_freeze(int32 x) {
x -= (int32) (q * ((q18 * x) >> 18));
x -= (int32) (q * ((q27 * x + 67108864) >> 27));
return (Fq) x;
}
/* works for all uint32 x */
static Fq Fq_bigfreeze(uint32 x) {
x -= (uint32) (q * ((x * (uint64)q31) >> 31));
x -= (uint32) (q * ((x * (uint64)q31) >> 31));
x -= q;
x += (~(x >> 31) + 1) & (uint32)q;
return (Fq) x;
}
/* ----- Top and Right */
static int8 Top(Fq C) {
return (int8) ((tau1 * (int32)(C + tau0) + 16384) >> 15);
}
static Fq Right(int8 T) {
return Fq_freeze(tau3 * (int32)T - tau2);
}
/* ----- polynomials mod q */
/* h = h*g in the ring Rq */
static void Rq_mult_small(Fq *h, const small *g) {
crypto_encode_pxint16((unsigned char *) h, h);
crypto_core_mult((unsigned char *) h, (const unsigned char *) h, (const unsigned char *) g);
crypto_decode_pxint16(h, (const unsigned char *) h);
}
/* ----- sorting to generate short polynomial */
static void Short_fromlist(small *out, const uint32 *in) {
uint32 L[ppadsort];
int i;
for (i = 0; i < w; ++i) {
L[i] = in[i] & (uint32) - 2;
}
for (i = w; i < p; ++i) {
L[i] = (in[i] & (uint32) - 3) | 1;
}
for (i = p; i < ppadsort; ++i) {
L[i] = 0xffffffff;
}
PQCLEAN_NTRULPR761_AVX2_crypto_sort_uint32(L, ppadsort);
for (i = 0; i < p; ++i) {
out[i] = (small) ((L[i] & 3) - 1);
}
}
/* ----- underlying hash function */
#define Hash_bytes 32
static void Hash(unsigned char *out, const unsigned char *in, int inlen) {
unsigned char h[64];
int i;
sha512(h, in, (size_t) inlen);
for (i = 0; i < 32; ++i) {
out[i] = h[i];
}
}
/* ----- higher-level randomness */
static void Short_random(small *out) {
uint32 L[p];
randombytes((unsigned char *) L, sizeof L);
crypto_decode_pxint32(L, (unsigned char *) L);
Short_fromlist(out, L);
}
/* ----- Inputs, Generator */
typedef int8 Inputs[I]; /* passed by reference */
static const unsigned char aes_nonce[16] = {0};
/* G = Generator(pk) */
static void Generator(Fq *G, const unsigned char *pk) {
uint32 L[p];
int i;
PQCLEAN_NTRULPR761_AVX2_crypto_stream_aes256ctr((unsigned char *) L, 4 * p, aes_nonce, pk);
crypto_decode_pxint32(L, (unsigned char *) L);
for (i = 0; i < p; ++i) {
G[i] = Fq_bigfreeze(L[i]) - q12;
}
}
/* ----- NTRU LPRime */
#define Seeds_bytes 32
#define Ciphertexts_bytes (Rounded_bytes+Top_bytes)
#define SecretKeys_bytes Small_bytes
#define PublicKeys_bytes (Seeds_bytes+Rounded_bytes)
#define Confirm_bytes 32
/* c,r_enc[1:] = Hide(r,pk,cache); cache is Hash4(pk) */
static void Hide(unsigned char *c, unsigned char *r_enc, const Inputs r, const unsigned char *pk, const unsigned char *cache) {
small b[p];
int i;
Inputs_encode(r_enc + 1, r);
{
unsigned char h[Hash_bytes];
uint32 L[p];
{
unsigned char s[1 + Inputs_bytes];
Inputs_encode(s + 1, r);
s[0] = 5;
Hash(h, s, sizeof s);
}
PQCLEAN_NTRULPR761_AVX2_crypto_stream_aes256ctr((unsigned char *) L, 4 * p, aes_nonce, h);
crypto_decode_pxint32(L, (unsigned char *) L);
Short_fromlist(b, L);
}
{
Fq bG[p];
Generator(bG, pk);
Rq_mult_small(bG, b);
Round_and_encode(c, bG);
c += Rounded_bytes;
}
{
Fq bA[p];
int8 T[I];
Rounded_decode(bA, pk + Seeds_bytes);
Rq_mult_small(bA, b);
for (i = 0; i < I; ++i) {
T[i] = Top(Fq_freeze(bA[i] + r[i] * q12));
}
Top_encode(c, T);
c += Top_bytes;
}
{
unsigned char x[1 + Inputs_bytes + Hash_bytes];
for (i = 0; i < Inputs_bytes; ++i) {
x[1 + i] = r_enc[1 + i];
}
for (i = 0; i < Hash_bytes; ++i) {
x[1 + Inputs_bytes + i] = cache[i];
}
x[0] = 2;
Hash(c, x, sizeof x);
}
}
int PQCLEAN_NTRULPR761_AVX2_crypto_kem_keypair(unsigned char *pk, unsigned char *sk) {
Fq aG[p];
int i;
randombytes(pk, Seeds_bytes);
Generator(aG, pk);
{
small a[p];
Short_random(a);
Rq_mult_small(aG, a);
Small_encode(sk, a);
}
Round_and_encode(pk + Seeds_bytes, aG);
{
unsigned char sksave = sk[SecretKeys_bytes - 1];
for (i = 0; i < PublicKeys_bytes; ++i) {
sk[SecretKeys_bytes + i] = pk[i];
}
sk[SecretKeys_bytes - 1] = 4;
Hash(sk + SecretKeys_bytes + PublicKeys_bytes + Inputs_bytes, sk + SecretKeys_bytes - 1, 1 + PublicKeys_bytes);
sk[SecretKeys_bytes - 1] = sksave;
randombytes(sk + SecretKeys_bytes + PublicKeys_bytes, Inputs_bytes);
}
return 0;
}
int PQCLEAN_NTRULPR761_AVX2_crypto_kem_enc(unsigned char *c, unsigned char *k, const unsigned char *pk) {
int i;
unsigned char cache[Hash_bytes];
{
unsigned char y[1 + PublicKeys_bytes];
for (i = 0; i < PublicKeys_bytes; ++i) {
y[1 + i] = pk[i];
}
y[0] = 4;
Hash(cache, y, sizeof y);
}
Inputs r;
{
unsigned char s[Inputs_bytes];
randombytes(s, sizeof s);
Inputs_decode(r, s);
}
{
unsigned char x[1 + Inputs_bytes + Ciphertexts_bytes + Confirm_bytes];
Hide(c, x, r, pk, cache);
for (i = 0; i < Ciphertexts_bytes + Confirm_bytes; ++i) {
x[1 + Inputs_bytes + i] = c[i];
}
x[0] = 1;
Hash(k, x, sizeof x);
}
return 0;
}
int PQCLEAN_NTRULPR761_AVX2_crypto_kem_dec(unsigned char *k, const unsigned char *c, const unsigned char *sk) {
const unsigned char *pk = sk + SecretKeys_bytes;
const unsigned char *rho = pk + PublicKeys_bytes;
const unsigned char *cache = rho + Inputs_bytes;
Inputs r;
int i;
{
Fq aB[p];
Rounded_decode(aB, c);
{
small a[p];
Small_decode(a, sk);
Rq_mult_small(aB, a);
}
{
int8 T[I];
Top_decode(T, c + Rounded_bytes);
for (i = 0; i < I; ++i) {
r[i] = (int8) - int16_negative_mask(Fq_freeze(Right(T[i]) - aB[i] + 4 * w + 1));
}
}
}
{
unsigned char cnew[Ciphertexts_bytes + Confirm_bytes];
int mask;
unsigned char x[1 + Inputs_bytes + Ciphertexts_bytes + Confirm_bytes];
Hide(cnew, x, r, pk, cache);
mask = crypto_verify_clen(c, cnew);
for (i = 0; i < Inputs_bytes; ++i) {
x[1 + i] ^= (unsigned char) (mask & (x[1 + i] ^ rho[i]));
}
for (i = 0; i < Ciphertexts_bytes + Confirm_bytes; ++i) {
x[1 + Inputs_bytes + i] = c[i];
}
x[0] = (unsigned char) (1 + mask);
Hash(k, x, sizeof x);
}
return 0;
}