1
1
mirror of https://github.com/henrydcase/pqc.git synced 2024-11-30 03:11:43 +00:00
pqcrypto/crypto_kem/sntrup653/avx2/kem.c

262 lines
7.3 KiB
C

#include "api.h"
#include "crypto_sort_uint32.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
/* ----- arithmetic mod 3 */
typedef int8 small;
/* F3 is always represented as -1,0,1 */
/* ----- arithmetic mod q */
typedef int16 Fq;
/* always represented as -(q-1)/2...(q-1)/2 */
/* ----- small polynomials */
/* R3_fromR(R_fromRq(r)) */
static void R3_fromRq(small *out, const Fq *r) {
crypto_encode_pxfreeze3((unsigned char *) out, (unsigned char *) r);
}
/* h = f*g in the ring R3 */
static void R3_mult(small *h, const small *f, const small *g) {
crypto_core_mult3((unsigned char *) h, (const unsigned char *) f, (const unsigned char *) g);
}
/* ----- 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);
}
/* h = 3f in Rq */
static void Rq_mult3(Fq *h, const Fq *f) {
crypto_encode_pxint16((unsigned char *) h, f);
crypto_core_scale3((unsigned char *) h, (const unsigned char *) h);
crypto_decode_pxint16(h, (const unsigned char *) h);
}
/* out = 1/(3*in) in Rq */
/* caller must have 2p+1 bytes free in out, not just 2p */
static void Rq_recip3(Fq *out, const small *in) {
crypto_core_inv((unsigned char *) out, (const unsigned char *) in);
/* could check byte 2*p for failure; but, in context, inv always works */
crypto_decode_pxint16(out, (unsigned char *) out);
}
/* ----- 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 uint32 urandom32(void) {
unsigned char c[4];
uint32 out[4];
randombytes(c, 4);
out[0] = (uint32)c[0];
out[1] = ((uint32)c[1]) << 8;
out[2] = ((uint32)c[2]) << 16;
out[3] = ((uint32)c[3]) << 24;
return out[0] + out[1] + out[2] + out[3];
}
static void Short_random(small *out) {
uint32 L[ppadsort];
int i;
for (i = 0; i < ppadsort; ++i) {
L[i] = urandom32();
}
for (i = 0; i < w; ++i) {
L[i] = L[i] & (uint32) - 2;
}
for (i = w; i < p; ++i) {
L[i] = (L[i] & (uint32) - 3) | 1;
}
for (i = p; i < ppadsort; ++i) {
L[i] = 0xffffffff;
}
PQCLEAN_SNTRUP653_AVX2_crypto_sort_uint32(L, ppadsort);
for (i = 0; i < p; ++i) {
out[i] = (small) ((L[i] & 3) - 1);
}
}
static void Small_random(small *out) {
uint32 L[p];
int i;
for (i = 0; i < p; ++i) {
L[i] = urandom32();
}
for (i = 0; i < p; ++i) {
out[i] = (small) ((((L[i] & 0x3fffffff) * 3) >> 30) - 1);
}
}
/* ----- Streamlined NTRU Prime */
typedef small Inputs[p]; /* passed by reference */
#define Ciphertexts_bytes Rounded_bytes
#define SecretKeys_bytes (2*Small_bytes)
#define PublicKeys_bytes Rq_bytes
#define Confirm_bytes 32
/* c,r_enc[1:] = Hide(r,pk,cache); cache is Hash4(pk) */
/* also set r_enc[0]=3 */
/* also set x[0]=2, and x[1:1+Hash_bytes] = Hash3(r_enc) */
/* also overwrite x[1+Hash_bytes:1+2*Hash_bytes] */
static void Hide(unsigned char *x, unsigned char *c, unsigned char *r_enc, const Inputs r, const unsigned char *pk, const unsigned char *cache) {
Fq h[p];
int i;
Small_encode(r_enc + 1, r);
Rq_decode(h, pk);
Rq_mult_small(h, r);
Round_and_encode(c, h);
r_enc[0] = 3;
Hash(x + 1, r_enc, 1 + Small_bytes);
for (i = 0; i < Hash_bytes; ++i) {
x[1 + Hash_bytes + i] = cache[i];
}
x[0] = 2;
Hash(c + Ciphertexts_bytes, x, 1 + Hash_bytes * 2);
}
int PQCLEAN_SNTRUP653_AVX2_crypto_kem_keypair(unsigned char *pk, unsigned char *sk) {
small g[p];
for (;;) {
Small_random(g);
{
small v[p + 1];
crypto_core_inv3((unsigned char *) v, (const unsigned char *) g);
if (v[p] == 0) {
Small_encode(sk + Small_bytes, v);
break;
}
}
}
{
small f[p];
Short_random(f);
Small_encode(sk, f);
{
Fq h[p + 1];
Rq_recip3(h, f); /* always works */
Rq_mult_small(h, g);
Rq_encode(pk, h);
}
}
{
int i;
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 + Small_bytes, sk + SecretKeys_bytes - 1, 1 + PublicKeys_bytes);
sk[SecretKeys_bytes - 1] = sksave;
randombytes(sk + SecretKeys_bytes + PublicKeys_bytes, Small_bytes);
}
return 0;
}
int PQCLEAN_SNTRUP653_AVX2_crypto_kem_enc(unsigned char *c, unsigned char *k, const unsigned char *pk) {
unsigned char cache[Hash_bytes];
int i;
{
unsigned char y[1 + PublicKeys_bytes]; /* XXX: can eliminate with incremental hashing */
for (i = 0; i < PublicKeys_bytes; ++i) {
y[1 + i] = pk[i];
}
y[0] = 4;
Hash(cache, y, sizeof y);
}
{
Inputs r;
Short_random(r);
{
unsigned char r_enc[Small_bytes + 1];
unsigned char x[1 + Hash_bytes + Ciphertexts_bytes + Confirm_bytes];
Hide(x, c, r_enc, r, pk, cache);
for (i = 0; i < Ciphertexts_bytes + Confirm_bytes; ++i) {
x[1 + Hash_bytes + i] = c[i];
}
x[0] = 1;
Hash(k, x, sizeof x);
}
}
return 0;
}
int PQCLEAN_SNTRUP653_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 + Small_bytes;
int mask, i;
Inputs r;
{
Fq d[p];
Rounded_decode(d, c);
{
small f[p];
Small_decode(f, sk);
Rq_mult_small(d, f);
Rq_mult3(d, d);
}
{
small e[p];
small v[p];
R3_fromRq(e, d);
Small_decode(v, sk + Small_bytes);
R3_mult(r, e, v);
}
crypto_core_wforce((unsigned char *) r, (unsigned char *) r);
}
{
unsigned char r_enc[1 + Small_bytes];
unsigned char cnew[Ciphertexts_bytes + Confirm_bytes];
unsigned char x[1 + Hash_bytes + Ciphertexts_bytes + Confirm_bytes];
/* XXX: can use incremental hashing to reduce x size */
Hide(x, cnew, r_enc, r, pk, cache);
mask = crypto_verify_clen(c, cnew);
for (i = 0; i < Small_bytes; ++i) {
r_enc[i + 1] ^= (unsigned char) (mask & (r_enc[i + 1] ^ rho[i]));
}
Hash(x + 1, r_enc, 1 + Small_bytes); /* XXX: can instead do cmov on cached hash of rho */
for (i = 0; i < Ciphertexts_bytes + Confirm_bytes; ++i) {
x[1 + Hash_bytes + i] = c[i];
}
x[0] = (unsigned char) (1 + mask);
Hash(k, x, sizeof x);
}
return 0;
}