From c8c80a0ec511aa66f4126d296793712585d29c7a Mon Sep 17 00:00:00 2001 From: Billy Brumley Date: Tue, 10 Dec 2019 16:16:41 +0200 Subject: [PATCH 5/8] [sntrup761] Experimental SNTRUP761 libcrypto support This adds support for sntrup761 in libcrypto. Co-authored-by: Nicola Tuveri --- Configure | 2 +- crypto/asn1/standard_methods.h | 1 + crypto/evp/pmeth_lib.c | 1 + crypto/sntrup761/Decode.c | 55 ++ crypto/sntrup761/Decode.h | 9 + crypto/sntrup761/Encode.c | 39 + crypto/sntrup761/Encode.h | 8 + crypto/sntrup761/aes256ctr.c | 39 + crypto/sntrup761/aes256ctr.h | 6 + crypto/sntrup761/api.h | 4 + crypto/sntrup761/build.info | 3 + crypto/sntrup761/crypto_kem.h | 15 + crypto/sntrup761/crypto_kem_sntrup761.h | 27 + crypto/sntrup761/int16.h | 7 + crypto/sntrup761/int32.c | 32 + crypto/sntrup761/int32.h | 19 + crypto/sntrup761/int8.h | 7 + crypto/sntrup761/kem.c | 902 ++++++++++++++++++++++++ crypto/sntrup761/params.h | 63 ++ crypto/sntrup761/paramsmenu.h | 8 + crypto/sntrup761/sntrup761_meth.c | 202 ++++++ crypto/sntrup761/uint16.h | 7 + crypto/sntrup761/uint32.c | 70 ++ crypto/sntrup761/uint32.h | 21 + crypto/sntrup761/uint32_sort.c | 34 + crypto/sntrup761/uint64.h | 8 + include/crypto/asn1.h | 1 + include/crypto/evp.h | 1 + include/openssl/evp.h | 1 + 29 files changed, 1591 insertions(+), 1 deletion(-) create mode 100644 crypto/sntrup761/Decode.c create mode 100644 crypto/sntrup761/Decode.h create mode 100644 crypto/sntrup761/Encode.c create mode 100644 crypto/sntrup761/Encode.h create mode 100644 crypto/sntrup761/aes256ctr.c create mode 100644 crypto/sntrup761/aes256ctr.h create mode 100644 crypto/sntrup761/api.h create mode 100644 crypto/sntrup761/build.info create mode 100644 crypto/sntrup761/crypto_kem.h create mode 100644 crypto/sntrup761/crypto_kem_sntrup761.h create mode 100644 crypto/sntrup761/int16.h create mode 100644 crypto/sntrup761/int32.c create mode 100644 crypto/sntrup761/int32.h create mode 100644 crypto/sntrup761/int8.h create mode 100644 crypto/sntrup761/kem.c create mode 100644 crypto/sntrup761/params.h create mode 100644 crypto/sntrup761/paramsmenu.h create mode 100644 crypto/sntrup761/sntrup761_meth.c create mode 100644 crypto/sntrup761/uint16.h create mode 100644 crypto/sntrup761/uint32.c create mode 100644 crypto/sntrup761/uint32.h create mode 100644 crypto/sntrup761/uint32_sort.c create mode 100644 crypto/sntrup761/uint64.h diff --git a/Configure b/Configure index faf57b155a..4fca1e9c63 100755 --- a/Configure +++ b/Configure @@ -327,7 +327,7 @@ $config{sdirs} = [ "bn", "ec", "rsa", "dsa", "dh", "sm2", "dso", "engine", "buffer", "bio", "stack", "lhash", "rand", "err", "evp", "asn1", "pem", "x509", "x509v3", "conf", "txt_db", "pkcs7", "pkcs12", "comp", "ocsp", "ui", - "cms", "ts", "srp", "cmac", "ct", "async", "kdf", "store" + "cms", "ts", "srp", "cmac", "ct", "async", "kdf", "store", "sntrup761" ]; # test/ subdirectories to build $config{tdirs} = [ "ossl_shim" ]; diff --git a/crypto/asn1/standard_methods.h b/crypto/asn1/standard_methods.h index e74de55ffe..792512c5da 100644 --- a/crypto/asn1/standard_methods.h +++ b/crypto/asn1/standard_methods.h @@ -57,5 +57,6 @@ static const EVP_PKEY_ASN1_METHOD *standard_methods[] = { #ifndef OPENSSL_NO_SM2 &sm2_asn1_meth, #endif + &sntrup761_asn1_meth, }; diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c index 603ccd8352..5443302f0f 100644 --- a/crypto/evp/pmeth_lib.c +++ b/crypto/evp/pmeth_lib.c @@ -67,6 +67,7 @@ static const EVP_PKEY_METHOD *standard_methods[] = { #ifndef OPENSSL_NO_SM2 &sm2_pkey_meth, #endif + &sntrup761_pkey_meth, }; DECLARE_OBJ_BSEARCH_CMP_FN(const EVP_PKEY_METHOD *, const EVP_PKEY_METHOD *, diff --git a/crypto/sntrup761/Decode.c b/crypto/sntrup761/Decode.c new file mode 100644 index 0000000000..558d05fea6 --- /dev/null +++ b/crypto/sntrup761/Decode.c @@ -0,0 +1,55 @@ +#include "uint16.h" +#include "uint32.h" +#include "Decode.h" + +extern void Decode(uint16 *out,const unsigned char *S,const uint16 *M,long long len) +{ + if (len == 1) { + if (M[0] == 1) + *out = 0; + else if (M[0] <= 256) + *out = uint32_mod_uint14(S[0],M[0]); + else + *out = uint32_mod_uint14(S[0]+(((uint16)S[1])<<8),M[0]); + } + if (len > 1) { + uint16 R2[(len+1)/2]; + uint16 M2[(len+1)/2]; + uint16 bottomr[len/2]; + uint32 bottomt[len/2]; + long long i; + for (i = 0;i < len-1;i += 2) { + uint32 m = M[i]*(uint32) M[i+1]; + if (m > 256*16383) { + bottomt[i/2] = 256*256; + bottomr[i/2] = S[0]+256*S[1]; + S += 2; + M2[i/2] = (((m+255)>>8)+255)>>8; + } else if (m >= 16384) { + bottomt[i/2] = 256; + bottomr[i/2] = S[0]; + S += 1; + M2[i/2] = (m+255)>>8; + } else { + bottomt[i/2] = 1; + bottomr[i/2] = 0; + M2[i/2] = m; + } + } + if (i < len) + M2[i/2] = M[i]; + Decode(R2,S,M2,(len+1)/2); + for (i = 0;i < len-1;i += 2) { + uint32 r = bottomr[i/2]; + uint32 r1; + uint16 r0; + r += bottomt[i/2]*R2[i/2]; + uint32_divmod_uint14(&r1,&r0,r,M[i]); + r1 = uint32_mod_uint14(r1,M[i+1]); /* only needed for invalid inputs */ + *out++ = r0; + *out++ = r1; + } + if (i < len) + *out++ = R2[i/2]; + } +} diff --git a/crypto/sntrup761/Decode.h b/crypto/sntrup761/Decode.h new file mode 100644 index 0000000000..cb94ba9453 --- /dev/null +++ b/crypto/sntrup761/Decode.h @@ -0,0 +1,9 @@ +#ifndef Decode_H +#define Decode_H + +/* Decode(R,s,M,len) */ +/* assumes 0 < M[i] < 16384 */ +/* produces 0 <= R[i] < M[i] */ +extern void Decode(uint16 *,const unsigned char *,const uint16 *,long long); + +#endif diff --git a/crypto/sntrup761/Encode.c b/crypto/sntrup761/Encode.c new file mode 100644 index 0000000000..77cef60f47 --- /dev/null +++ b/crypto/sntrup761/Encode.c @@ -0,0 +1,39 @@ +#include "uint16.h" +#include "uint32.h" +#include "Encode.h" + +/* 0 <= R[i] < M[i] < 16384 */ +void Encode(unsigned char *out,const uint16 *R,const uint16 *M,long long len) +{ + if (len == 1) { + uint16 r = R[0]; + uint16 m = M[0]; + while (m > 1) { + *out++ = r; + r >>= 8; + m = (m+255)>>8; + } + } + if (len > 1) { + uint16 R2[(len+1)/2]; + uint16 M2[(len+1)/2]; + long long i; + for (i = 0;i < len-1;i += 2) { + uint32 m0 = M[i]; + uint32 r = R[i]+R[i+1]*m0; + uint32 m = M[i+1]*m0; + while (m >= 16384) { + *out++ = r; + r >>= 8; + m = (m+255)>>8; + } + R2[i/2] = r; + M2[i/2] = m; + } + if (i < len) { + R2[i/2] = R[i]; + M2[i/2] = M[i]; + } + Encode(out,R2,M2,(len+1)/2); + } +} diff --git a/crypto/sntrup761/Encode.h b/crypto/sntrup761/Encode.h new file mode 100644 index 0000000000..3b175d8273 --- /dev/null +++ b/crypto/sntrup761/Encode.h @@ -0,0 +1,8 @@ +#ifndef Encode_H +#define Encode_H + +/* Encode(s,R,M,len) */ +/* assumes 0 <= R[i] < M[i] < 16384 */ +extern void Encode(unsigned char *,const uint16 *,const uint16 *,long long); + +#endif diff --git a/crypto/sntrup761/aes256ctr.c b/crypto/sntrup761/aes256ctr.c new file mode 100644 index 0000000000..339f1443aa --- /dev/null +++ b/crypto/sntrup761/aes256ctr.c @@ -0,0 +1,39 @@ +#include +#include +#include "aes256ctr.h" + +static int aes256ctr_xor( + unsigned char *out, + const unsigned char *in, + unsigned long long inlen, + const unsigned char *n, + const unsigned char *k +) +{ + EVP_CIPHER_CTX *x; + int ok; + int outl = 0; + + x = EVP_CIPHER_CTX_new(); + if (!x) return -111; + + ok = EVP_EncryptInit_ex(x,EVP_aes_256_ctr(),0,k,n); + if (ok == 1) ok = EVP_CIPHER_CTX_set_padding(x, 0); + if (ok == 1) ok = EVP_EncryptUpdate(x, out, &outl, in, inlen); + if (ok == 1) ok = EVP_EncryptFinal_ex(x, out, &outl); + + EVP_CIPHER_CTX_free(x); + return ok == 1 ? 0 : -111; +} + +int aes256ctr( + unsigned char *out, + unsigned long long outlen, + const unsigned char *n, + const unsigned char *k +) +{ + unsigned char temp[outlen]; + memset(temp, 0, outlen); + return aes256ctr_xor(out, temp, outlen, n, k); +} diff --git a/crypto/sntrup761/aes256ctr.h b/crypto/sntrup761/aes256ctr.h new file mode 100644 index 0000000000..ef0badb849 --- /dev/null +++ b/crypto/sntrup761/aes256ctr.h @@ -0,0 +1,6 @@ +#ifndef aes256ctr_H +#define aes256ctr_H + +int aes256ctr(unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); + +#endif diff --git a/crypto/sntrup761/api.h b/crypto/sntrup761/api.h new file mode 100644 index 0000000000..45f0963fab --- /dev/null +++ b/crypto/sntrup761/api.h @@ -0,0 +1,4 @@ +#define CRYPTO_SECRETKEYBYTES 1763 +#define CRYPTO_PUBLICKEYBYTES 1158 +#define CRYPTO_CIPHERTEXTBYTES 1039 +#define CRYPTO_BYTES 32 diff --git a/crypto/sntrup761/build.info b/crypto/sntrup761/build.info new file mode 100644 index 0000000000..4e901ee20f --- /dev/null +++ b/crypto/sntrup761/build.info @@ -0,0 +1,3 @@ +LIBS=../../libcrypto +SOURCE[../../libcrypto]=\ + sntrup761_meth.c aes256ctr.c Decode.c Encode.c int32.c kem.c uint32.c uint32_sort.c diff --git a/crypto/sntrup761/crypto_kem.h b/crypto/sntrup761/crypto_kem.h new file mode 100644 index 0000000000..0e117f202c --- /dev/null +++ b/crypto/sntrup761/crypto_kem.h @@ -0,0 +1,15 @@ +#ifndef crypto_kem_H +#define crypto_kem_H + +#include "crypto_kem_sntrup761.h" + +#define crypto_kem_keypair crypto_kem_sntrup761_keypair +#define crypto_kem_enc crypto_kem_sntrup761_enc +#define crypto_kem_dec crypto_kem_sntrup761_dec +#define crypto_kem_PUBLICKEYBYTES crypto_kem_sntrup761_PUBLICKEYBYTES +#define crypto_kem_SECRETKEYBYTES crypto_kem_sntrup761_SECRETKEYBYTES +#define crypto_kem_BYTES crypto_kem_sntrup761_BYTES +#define crypto_kem_CIPHERTEXTBYTES crypto_kem_sntrup761_CIPHERTEXTBYTES +#define crypto_kem_PRIMITIVE "sntrup761" + +#endif diff --git a/crypto/sntrup761/crypto_kem_sntrup761.h b/crypto/sntrup761/crypto_kem_sntrup761.h new file mode 100644 index 0000000000..dc222f11df --- /dev/null +++ b/crypto/sntrup761/crypto_kem_sntrup761.h @@ -0,0 +1,27 @@ +#ifndef crypto_kem_sntrup761_H +#define crypto_kem_sntrup761_H + +#define crypto_kem_sntrup761_ref_SECRETKEYBYTES 1763 +#define crypto_kem_sntrup761_ref_PUBLICKEYBYTES 1158 +#define crypto_kem_sntrup761_ref_CIPHERTEXTBYTES 1039 +#define crypto_kem_sntrup761_ref_BYTES 32 + +#ifdef __cplusplus +extern "C" { +#endif +extern int crypto_kem_sntrup761_ref_keypair(unsigned char *,unsigned char *); +extern int crypto_kem_sntrup761_ref_enc(unsigned char *,unsigned char *,const unsigned char *); +extern int crypto_kem_sntrup761_ref_dec(unsigned char *,const unsigned char *,const unsigned char *); +#ifdef __cplusplus +} +#endif + +#define crypto_kem_sntrup761_keypair crypto_kem_sntrup761_ref_keypair +#define crypto_kem_sntrup761_enc crypto_kem_sntrup761_ref_enc +#define crypto_kem_sntrup761_dec crypto_kem_sntrup761_ref_dec +#define crypto_kem_sntrup761_PUBLICKEYBYTES crypto_kem_sntrup761_ref_PUBLICKEYBYTES +#define crypto_kem_sntrup761_SECRETKEYBYTES crypto_kem_sntrup761_ref_SECRETKEYBYTES +#define crypto_kem_sntrup761_BYTES crypto_kem_sntrup761_ref_BYTES +#define crypto_kem_sntrup761_CIPHERTEXTBYTES crypto_kem_sntrup761_ref_CIPHERTEXTBYTES + +#endif diff --git a/crypto/sntrup761/int16.h b/crypto/sntrup761/int16.h new file mode 100644 index 0000000000..ce767aa289 --- /dev/null +++ b/crypto/sntrup761/int16.h @@ -0,0 +1,7 @@ +#ifndef INT16_H +#define INT16_H + +#include +typedef int16_t int16; + +#endif diff --git a/crypto/sntrup761/int32.c b/crypto/sntrup761/int32.c new file mode 100644 index 0000000000..1db1fd8f3c --- /dev/null +++ b/crypto/sntrup761/int32.c @@ -0,0 +1,32 @@ +#include "int32.h" +#include "uint32.h" + +void int32_divmod_uint14(int32 *q,uint16 *r,int32 x,uint16 m) +{ + uint32 uq,uq2; + uint16 ur,ur2; + uint32 mask; + + uint32_divmod_uint14(&uq,&ur,0x80000000+(uint32)x,m); + uint32_divmod_uint14(&uq2,&ur2,0x80000000,m); + ur -= ur2; uq -= uq2; + mask = -(uint32)(ur>>15); + ur += mask&m; uq += mask; + *r = ur; *q = uq; +} + +int32 int32_div_uint14(int32 x,uint16 m) +{ + int32 q; + uint16 r; + int32_divmod_uint14(&q,&r,x,m); + return q; +} + +uint16 int32_mod_uint14(int32 x,uint16 m) +{ + int32 q; + uint16 r; + int32_divmod_uint14(&q,&r,x,m); + return r; +} diff --git a/crypto/sntrup761/int32.h b/crypto/sntrup761/int32.h new file mode 100644 index 0000000000..b93c94cdd2 --- /dev/null +++ b/crypto/sntrup761/int32.h @@ -0,0 +1,19 @@ +#ifndef INT32_H +#define INT32_H + +#include "uint16.h" + +typedef int32_t int32; + +/* +assuming 1 <= m < 16384: +q = int32_div_uint14(x,m) means q = x/m +r = int32_mod_uint14(x,m) means r = x/m +int32_moddiv_uint14(&q,&r,x,m) means q = x/m, r = x%m +*/ + +extern int32 int32_div_uint14(int32,uint16); +extern uint16 int32_mod_uint14(int32,uint16); +extern void int32_divmod_uint14(int32 *,uint16 *,int32,uint16); + +#endif diff --git a/crypto/sntrup761/int8.h b/crypto/sntrup761/int8.h new file mode 100644 index 0000000000..8c259f5db1 --- /dev/null +++ b/crypto/sntrup761/int8.h @@ -0,0 +1,7 @@ +#ifndef INT8_H +#define INT8_H + +#include +typedef int8_t int8; + +#endif diff --git a/crypto/sntrup761/kem.c b/crypto/sntrup761/kem.c new file mode 100644 index 0000000000..f1fffe608b --- /dev/null +++ b/crypto/sntrup761/kem.c @@ -0,0 +1,902 @@ +#ifdef KAT +#include +#endif + +#include +#include +#define randombytes RAND_priv_bytes +//#include "randombytes.h" + +#include "params.h" + +#ifdef LPR +#include "aes256ctr.h" +#endif + +#include "int8.h" +#include "int16.h" +#include "int32.h" +#include "uint16.h" +#include "uint32.h" +#include "Encode.h" +#include "Decode.h" + +/* ----- masks */ + +#ifndef LPR + +/* return -1 if x!=0; else return 0 */ +static int int16_nonzero_mask(int16 x) +{ + uint16 u = x; /* 0, else 1...65535 */ + uint32 v = u; /* 0, else 1...65535 */ + v = -v; /* 0, else 2^32-65535...2^32-1 */ + v >>= 31; /* 0, else 1 */ + return -v; /* 0, else -1 */ +} + +#endif + +/* return -1 if x<0; otherwise return 0 */ +static int int16_negative_mask(int16 x) +{ + uint16 u = 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 */ +/* so ZZ_fromF3 is a no-op */ + +/* x must not be close to top int16 */ +static small F3_freeze(int16 x) +{ + return int32_mod_uint14(x+1,3)-1; +} + +/* ----- arithmetic mod q */ + +#define q12 ((q-1)/2) +typedef int16 Fq; +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +static Fq Fq_freeze(int32 x) +{ + return int32_mod_uint14(x+q12,q)-q12; +} + +#ifndef LPR + +static Fq Fq_recip(Fq a1) +{ + int i = 1; + Fq ai = a1; + + while (i < q-2) { + ai = Fq_freeze(a1*(int32)ai); + i += 1; + } + return ai; +} + +#endif + +/* ----- Top and Right */ + +#ifdef LPR +#define tau 16 + +static int8 Top(Fq C) +{ + return (tau1*(int32)(C+tau0)+16384)>>15; +} + +static Fq Right(int8 T) +{ + return Fq_freeze(tau3*(int32)T-tau2); +} +#endif + +/* ----- small polynomials */ + +#ifndef LPR + +/* 0 if Weightw_is(r), else -1 */ +static int Weightw_mask(small *r) +{ + int weight = 0; + int i; + + for (i = 0;i < p;++i) weight += r[i]&1; + return int16_nonzero_mask(weight-w); +} + +/* R3_fromR(R_fromRq(r)) */ +static void R3_fromRq(small *out,const Fq *r) +{ + int i; + for (i = 0;i < p;++i) out[i] = F3_freeze(r[i]); +} + +/* h = f*g in the ring R3 */ +static void R3_mult(small *h,const small *f,const small *g) +{ + small fg[p+p-1]; + small result; + int i,j; + + for (i = 0;i < p;++i) { + result = 0; + for (j = 0;j <= i;++j) result = F3_freeze(result+f[j]*g[i-j]); + fg[i] = result; + } + for (i = p;i < p+p-1;++i) { + result = 0; + for (j = i-p+1;j < p;++j) result = F3_freeze(result+f[j]*g[i-j]); + fg[i] = result; + } + + for (i = p+p-2;i >= p;--i) { + fg[i-p] = F3_freeze(fg[i-p]+fg[i]); + fg[i-p+1] = F3_freeze(fg[i-p+1]+fg[i]); + } + + for (i = 0;i < p;++i) h[i] = fg[i]; +} + +/* returns 0 if recip succeeded; else -1 */ +static int R3_recip(small *out,const small *in) +{ + small f[p+1],g[p+1],v[p+1],r[p+1]; + int i,loop,delta; + int sign,swap,t; + + for (i = 0;i < p+1;++i) v[i] = 0; + for (i = 0;i < p+1;++i) r[i] = 0; + r[0] = 1; + for (i = 0;i < p;++i) f[i] = 0; + f[0] = 1; f[p-1] = f[p] = -1; + for (i = 0;i < p;++i) g[p-1-i] = in[i]; + g[p] = 0; + + delta = 1; + + for (loop = 0;loop < 2*p-1;++loop) { + for (i = p;i > 0;--i) v[i] = v[i-1]; + v[0] = 0; + + sign = -g[0]*f[0]; + swap = int16_negative_mask(-delta) & int16_nonzero_mask(g[0]); + delta ^= swap&(delta^-delta); + delta += 1; + + for (i = 0;i < p+1;++i) { + t = swap&(f[i]^g[i]); f[i] ^= t; g[i] ^= t; + t = swap&(v[i]^r[i]); v[i] ^= t; r[i] ^= t; + } + + for (i = 0;i < p+1;++i) g[i] = F3_freeze(g[i]+sign*f[i]); + for (i = 0;i < p+1;++i) r[i] = F3_freeze(r[i]+sign*v[i]); + + for (i = 0;i < p;++i) g[i] = g[i+1]; + g[p] = 0; + } + + sign = f[0]; + for (i = 0;i < p;++i) out[i] = sign*v[p-1-i]; + + return int16_nonzero_mask(delta); +} + +#endif + +/* ----- polynomials mod q */ + +/* h = f*g in the ring Rq */ +static void Rq_mult_small(Fq *h,const Fq *f,const small *g) +{ + Fq fg[p+p-1]; + Fq result; + int i,j; + + for (i = 0;i < p;++i) { + result = 0; + for (j = 0;j <= i;++j) result = Fq_freeze(result+f[j]*(int32)g[i-j]); + fg[i] = result; + } + for (i = p;i < p+p-1;++i) { + result = 0; + for (j = i-p+1;j < p;++j) result = Fq_freeze(result+f[j]*(int32)g[i-j]); + fg[i] = result; + } + + for (i = p+p-2;i >= p;--i) { + fg[i-p] = Fq_freeze(fg[i-p]+fg[i]); + fg[i-p+1] = Fq_freeze(fg[i-p+1]+fg[i]); + } + + for (i = 0;i < p;++i) h[i] = fg[i]; +} + +#ifndef LPR + +/* h = 3f in Rq */ +static void Rq_mult3(Fq *h,const Fq *f) +{ + int i; + + for (i = 0;i < p;++i) h[i] = Fq_freeze(3*f[i]); +} + +/* out = 1/(3*in) in Rq */ +/* returns 0 if recip succeeded; else -1 */ +static int Rq_recip3(Fq *out,const small *in) +{ + Fq f[p+1],g[p+1],v[p+1],r[p+1]; + int i,loop,delta; + int swap,t; + int32 f0,g0; + Fq scale; + + for (i = 0;i < p+1;++i) v[i] = 0; + for (i = 0;i < p+1;++i) r[i] = 0; + r[0] = Fq_recip(3); + for (i = 0;i < p;++i) f[i] = 0; + f[0] = 1; f[p-1] = f[p] = -1; + for (i = 0;i < p;++i) g[p-1-i] = in[i]; + g[p] = 0; + + delta = 1; + + for (loop = 0;loop < 2*p-1;++loop) { + for (i = p;i > 0;--i) v[i] = v[i-1]; + v[0] = 0; + + swap = int16_negative_mask(-delta) & int16_nonzero_mask(g[0]); + delta ^= swap&(delta^-delta); + delta += 1; + + for (i = 0;i < p+1;++i) { + t = swap&(f[i]^g[i]); f[i] ^= t; g[i] ^= t; + t = swap&(v[i]^r[i]); v[i] ^= t; r[i] ^= t; + } + + f0 = f[0]; + g0 = g[0]; + for (i = 0;i < p+1;++i) g[i] = Fq_freeze(f0*g[i]-g0*f[i]); + for (i = 0;i < p+1;++i) r[i] = Fq_freeze(f0*r[i]-g0*v[i]); + + for (i = 0;i < p;++i) g[i] = g[i+1]; + g[p] = 0; + } + + scale = Fq_recip(f[0]); + for (i = 0;i < p;++i) out[i] = Fq_freeze(scale*(int32)v[p-1-i]); + + return int16_nonzero_mask(delta); +} + +#endif + +/* ----- rounded polynomials mod q */ + +static void Round(Fq *out,const Fq *a) +{ + int i; + for (i = 0;i < p;++i) out[i] = a[i]-F3_freeze(a[i]); +} + +/* ----- sorting to generate short polynomial */ + +static void Short_fromlist(small *out,const uint32 *in) +{ + uint32 L[p]; + 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; + uint32_sort(L,p); + for (i = 0;i < p;++i) out[i] = (L[i]&3)-1; +} + +/* ----- underlying hash function */ + +#define Hash_bytes 32 + +/* e.g., b = 0 means out = Hash0(in) */ +static void Hash(unsigned char *out,int b,const unsigned char *in,int inlen) +{ + unsigned char x[inlen+1]; + unsigned char h[64]; + int i; + + x[0] = b; + for (i = 0;i < inlen;++i) x[i+1] = in[i]; + SHA512(x,inlen+1,h); + 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[p]; + int i; + + for (i = 0;i < p;++i) L[i] = urandom32(); + Short_fromlist(out,L); +} + +#ifndef LPR + +static void Small_random(small *out) +{ + int i; + + for (i = 0;i < p;++i) out[i] = (((urandom32()&0x3fffffff)*3)>>30)-1; +} + +#endif + +/* ----- Streamlined NTRU Prime Core */ + +#ifndef LPR + +/* h,(f,ginv) = KeyGen() */ +static void KeyGen(Fq *h,small *f,small *ginv) +{ + small g[p]; + Fq finv[p]; + + for (;;) { + Small_random(g); + if (R3_recip(ginv,g) == 0) break; + } + Short_random(f); + Rq_recip3(finv,f); /* always works */ + Rq_mult_small(h,finv,g); +} + +/* c = Encrypt(r,h) */ +static void Encrypt(Fq *c,const small *r,const Fq *h) +{ + Fq hr[p]; + + Rq_mult_small(hr,h,r); + Round(c,hr); +} + +/* r = Decrypt(c,(f,ginv)) */ +static void Decrypt(small *r,const Fq *c,const small *f,const small *ginv) +{ + Fq cf[p]; + Fq cf3[p]; + small e[p]; + small ev[p]; + int mask; + int i; + + Rq_mult_small(cf,c,f); + Rq_mult3(cf3,cf); + R3_fromRq(e,cf3); + R3_mult(ev,e,ginv); + + mask = Weightw_mask(ev); /* 0 if weight w, else -1 */ + for (i = 0;i < w;++i) r[i] = ((ev[i]^1)&~mask)^1; + for (i = w;i < p;++i) r[i] = ev[i]&~mask; +} + +#endif + +/* ----- NTRU LPRime Core */ + +#ifdef LPR + +/* (G,A),a = KeyGen(G); leaves G unchanged */ +static void KeyGen(Fq *A,small *a,const Fq *G) +{ + Fq aG[p]; + + Short_random(a); + Rq_mult_small(aG,G,a); + Round(A,aG); +} + +/* B,T = Encrypt(r,(G,A),b) */ +static void Encrypt(Fq *B,int8 *T,const int8 *r,const Fq *G,const Fq *A,const small *b) +{ + Fq bG[p]; + Fq bA[p]; + int i; + + Rq_mult_small(bG,G,b); + Round(B,bG); + Rq_mult_small(bA,A,b); + for (i = 0;i < I;++i) T[i] = Top(Fq_freeze(bA[i]+r[i]*q12)); +} + +/* r = Decrypt((B,T),a) */ +static void Decrypt(int8 *r,const Fq *B,const int8 *T,const small *a) +{ + Fq aB[p]; + int i; + + Rq_mult_small(aB,B,a); + for (i = 0;i < I;++i) + r[i] = -int16_negative_mask(Fq_freeze(Right(T[i])-aB[i]+4*w+1)); +} + +#endif + +/* ----- encoding I-bit inputs */ + +#ifdef LPR + +#define Inputs_bytes (I/8) +typedef int8 Inputs[I]; /* passed by reference */ + +static void Inputs_encode(unsigned char *s,const Inputs r) +{ + int i; + for (i = 0;i < Inputs_bytes;++i) s[i] = 0; + for (i = 0;i < I;++i) s[i>>3] |= r[i]<<(i&7); +} + +#endif + +/* ----- Expand */ + +#ifdef LPR + +static const unsigned char aes_nonce[16] = {0}; + +static void Expand(uint32 *L,const unsigned char *k) +{ + aes256ctr((unsigned char *) L,4*p,aes_nonce,k); +} + +#endif + +/* ----- Seeds */ + +#ifdef LPR + +#define Seeds_bytes 32 + +static void Seeds_random(unsigned char *s) +{ + randombytes(s,Seeds_bytes); +} + +#endif + +/* ----- Generator, HashShort */ + +#ifdef LPR + +/* G = Generator(k) */ +static void Generator(Fq *G,const unsigned char *k) +{ + uint32 L[p]; + int i; + + Expand(L,k); + for (i = 0;i < p;++i) G[i] = uint32_mod_uint14(L[i],q)-q12; +} + +/* out = HashShort(r) */ +static void HashShort(small *out,const Inputs r) +{ + unsigned char s[Inputs_bytes]; + unsigned char h[Hash_bytes]; + uint32 L[p]; + + Inputs_encode(s,r); + Hash(h,5,s,sizeof s); + Expand(L,h); + Short_fromlist(out,L); +} + +#endif + +/* ----- NTRU LPRime Expand */ + +#ifdef LPR + +/* (S,A),a = XKeyGen() */ +static void XKeyGen(unsigned char *S,Fq *A,small *a) +{ + Fq G[p]; + + Seeds_random(S); + Generator(G,S); + KeyGen(A,a,G); +} + +/* B,T = XEncrypt(r,(S,A)) */ +static void XEncrypt(Fq *B,int8 *T,const int8 *r,const unsigned char *S,const Fq *A) +{ + Fq G[p]; + small b[p]; + + Generator(G,S); + HashShort(b,r); + Encrypt(B,T,r,G,A,b); +} + +#define XDecrypt Decrypt + +#endif + +/* ----- encoding small polynomials (including short polynomials) */ + +#define Small_bytes ((p+3)/4) + +/* these are the only functions that rely on p mod 4 = 1 */ + +static void Small_encode(unsigned char *s,const small *f) +{ + small x; + int i; + + for (i = 0;i < p/4;++i) { + x = *f++ + 1; + x += (*f++ + 1)<<2; + x += (*f++ + 1)<<4; + x += (*f++ + 1)<<6; + *s++ = x; + } + x = *f++ + 1; + *s++ = x; +} + +static void Small_decode(small *f,const unsigned char *s) +{ + unsigned char x; + int i; + + for (i = 0;i < p/4;++i) { + x = *s++; + *f++ = ((small)(x&3))-1; x >>= 2; + *f++ = ((small)(x&3))-1; x >>= 2; + *f++ = ((small)(x&3))-1; x >>= 2; + *f++ = ((small)(x&3))-1; + } + x = *s++; + *f++ = ((small)(x&3))-1; +} + +/* ----- encoding general polynomials */ + +#ifndef LPR + +static void Rq_encode(unsigned char *s,const Fq *r) +{ + uint16 R[p],M[p]; + int i; + + for (i = 0;i < p;++i) R[i] = r[i]+q12; + for (i = 0;i < p;++i) M[i] = q; + Encode(s,R,M,p); +} + +static void Rq_decode(Fq *r,const unsigned char *s) +{ + uint16 R[p],M[p]; + int i; + + for (i = 0;i < p;++i) M[i] = q; + Decode(R,s,M,p); + for (i = 0;i < p;++i) r[i] = ((Fq)R[i])-q12; +} + +#endif + +/* ----- encoding rounded polynomials */ + +static void Rounded_encode(unsigned char *s,const Fq *r) +{ + uint16 R[p],M[p]; + int i; + + for (i = 0;i < p;++i) R[i] = ((r[i]+q12)*10923)>>15; + for (i = 0;i < p;++i) M[i] = (q+2)/3; + Encode(s,R,M,p); +} + +static void Rounded_decode(Fq *r,const unsigned char *s) +{ + uint16 R[p],M[p]; + int i; + + for (i = 0;i < p;++i) M[i] = (q+2)/3; + Decode(R,s,M,p); + for (i = 0;i < p;++i) r[i] = R[i]*3-q12; +} + +/* ----- encoding top polynomials */ + +#ifdef LPR + +#define Top_bytes (I/2) + +static void Top_encode(unsigned char *s,const int8 *T) +{ + int i; + for (i = 0;i < Top_bytes;++i) + s[i] = T[2*i]+(T[2*i+1]<<4); +} + +static void Top_decode(int8 *T,const unsigned char *s) +{ + int i; + for (i = 0;i < Top_bytes;++i) { + T[2*i] = s[i]&15; + T[2*i+1] = s[i]>>4; + } +} + +#endif + +/* ----- Streamlined NTRU Prime Core plus encoding */ + +#ifndef LPR + +typedef small Inputs[p]; /* passed by reference */ +#define Inputs_random Short_random +#define Inputs_encode Small_encode +#define Inputs_bytes Small_bytes + +#define Ciphertexts_bytes Rounded_bytes +#define SecretKeys_bytes (2*Small_bytes) +#define PublicKeys_bytes Rq_bytes + +/* pk,sk = ZKeyGen() */ +static void ZKeyGen(unsigned char *pk,unsigned char *sk) +{ + Fq h[p]; + small f[p],v[p]; + + KeyGen(h,f,v); + Rq_encode(pk,h); + Small_encode(sk,f); sk += Small_bytes; + Small_encode(sk,v); +} + +/* C = ZEncrypt(r,pk) */ +static void ZEncrypt(unsigned char *C,const Inputs r,const unsigned char *pk) +{ + Fq h[p]; + Fq c[p]; + Rq_decode(h,pk); + Encrypt(c,r,h); + Rounded_encode(C,c); +} + +/* r = ZDecrypt(C,sk) */ +static void ZDecrypt(Inputs r,const unsigned char *C,const unsigned char *sk) +{ + small f[p],v[p]; + Fq c[p]; + + Small_decode(f,sk); sk += Small_bytes; + Small_decode(v,sk); + Rounded_decode(c,C); + Decrypt(r,c,f,v); +} + +#endif + +/* ----- NTRU LPRime Expand plus encoding */ + +#ifdef LPR + +#define Ciphertexts_bytes (Rounded_bytes+Top_bytes) +#define SecretKeys_bytes Small_bytes +#define PublicKeys_bytes (Seeds_bytes+Rounded_bytes) + +static void Inputs_random(Inputs r) +{ + unsigned char s[Inputs_bytes]; + int i; + + randombytes(s,sizeof s); + for (i = 0;i < I;++i) r[i] = 1&(s[i>>3]>>(i&7)); +} + +/* pk,sk = ZKeyGen() */ +static void ZKeyGen(unsigned char *pk,unsigned char *sk) +{ + Fq A[p]; + small a[p]; + + XKeyGen(pk,A,a); pk += Seeds_bytes; + Rounded_encode(pk,A); + Small_encode(sk,a); +} + +/* c = ZEncrypt(r,pk) */ +static void ZEncrypt(unsigned char *c,const Inputs r,const unsigned char *pk) +{ + Fq A[p]; + Fq B[p]; + int8 T[I]; + + Rounded_decode(A,pk+Seeds_bytes); + XEncrypt(B,T,r,pk,A); + Rounded_encode(c,B); c += Rounded_bytes; + Top_encode(c,T); +} + +/* r = ZDecrypt(C,sk) */ +static void ZDecrypt(Inputs r,const unsigned char *c,const unsigned char *sk) +{ + small a[p]; + Fq B[p]; + int8 T[I]; + + Small_decode(a,sk); + Rounded_decode(B,c); + Top_decode(T,c+Rounded_bytes); + XDecrypt(r,B,T,a); +} + +#endif + +/* ----- confirmation hash */ + +#define Confirm_bytes 32 + +/* h = HashConfirm(r,pk,cache); cache is Hash4(pk) */ +static void HashConfirm(unsigned char *h,const unsigned char *r,const unsigned char *pk,const unsigned char *cache) +{ +#ifndef LPR + unsigned char x[Hash_bytes*2]; + int i; + + Hash(x,3,r,Inputs_bytes); + for (i = 0;i < Hash_bytes;++i) x[Hash_bytes+i] = cache[i]; +#else + unsigned char x[Inputs_bytes+Hash_bytes]; + int i; + + for (i = 0;i < Inputs_bytes;++i) x[i] = r[i]; + for (i = 0;i < Hash_bytes;++i) x[Inputs_bytes+i] = cache[i]; +#endif + Hash(h,2,x,sizeof x); +} + +/* ----- session-key hash */ + +/* k = HashSession(b,y,z) */ +static void HashSession(unsigned char *k,int b,const unsigned char *y,const unsigned char *z) +{ +#ifndef LPR + unsigned char x[Hash_bytes+Ciphertexts_bytes+Confirm_bytes]; + int i; + + Hash(x,3,y,Inputs_bytes); + for (i = 0;i < Ciphertexts_bytes+Confirm_bytes;++i) x[Hash_bytes+i] = z[i]; +#else + unsigned char x[Inputs_bytes+Ciphertexts_bytes+Confirm_bytes]; + int i; + + for (i = 0;i < Inputs_bytes;++i) x[i] = y[i]; + for (i = 0;i < Ciphertexts_bytes+Confirm_bytes;++i) x[Inputs_bytes+i] = z[i]; +#endif + Hash(k,b,x,sizeof x); +} + +/* ----- Streamlined NTRU Prime and NTRU LPRime */ + +/* pk,sk = KEM_KeyGen() */ +static void KEM_KeyGen(unsigned char *pk,unsigned char *sk) +{ + int i; + + ZKeyGen(pk,sk); sk += SecretKeys_bytes; + for (i = 0;i < PublicKeys_bytes;++i) *sk++ = pk[i]; + randombytes(sk,Inputs_bytes); sk += Inputs_bytes; + Hash(sk,4,pk,PublicKeys_bytes); +} + +/* c,r_enc = 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) +{ + Inputs_encode(r_enc,r); +#ifdef KAT + { + int j; + printf("Hide r_enc: "); + for (j = 0;j < Inputs_bytes;++j) printf("%02x",r_enc[j]); + printf("\n"); + } +#endif + ZEncrypt(c,r,pk); c += Ciphertexts_bytes; + HashConfirm(c,r_enc,pk,cache); +} + +/* c,k = Encap(pk) */ +static void Encap(unsigned char *c,unsigned char *k,const unsigned char *pk) +{ + Inputs r; + unsigned char r_enc[Inputs_bytes]; + unsigned char cache[Hash_bytes]; + + Hash(cache,4,pk,PublicKeys_bytes); + Inputs_random(r); + Hide(c,r_enc,r,pk,cache); + HashSession(k,1,r_enc,c); +} + +/* 0 if matching ciphertext+confirm, else -1 */ +static int Ciphertexts_diff_mask(const unsigned char *c,const unsigned char *c2) +{ + uint16 differentbits = 0; + int len = Ciphertexts_bytes+Confirm_bytes; + + while (len-- > 0) differentbits |= (*c++)^(*c2++); + return (1&((differentbits-1)>>8))-1; +} + +/* k = Decap(c,sk) */ +static void Decap(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; + unsigned char r_enc[Inputs_bytes]; + unsigned char cnew[Ciphertexts_bytes+Confirm_bytes]; + int mask; + int i; + + ZDecrypt(r,c,sk); + Hide(cnew,r_enc,r,pk,cache); + mask = Ciphertexts_diff_mask(c,cnew); + for (i = 0;i < Inputs_bytes;++i) r_enc[i] ^= mask&(r_enc[i]^rho[i]); + HashSession(k,1+mask,r_enc,c); +} + +/* ----- crypto_kem API */ + +#include "crypto_kem.h" + +int crypto_kem_keypair(unsigned char *pk,unsigned char *sk) +{ + KEM_KeyGen(pk,sk); + return 0; +} + +int crypto_kem_enc(unsigned char *c,unsigned char *k,const unsigned char *pk) +{ + Encap(c,k,pk); + return 0; +} + +int crypto_kem_dec(unsigned char *k,const unsigned char *c,const unsigned char *sk) +{ + Decap(k,c,sk); + return 0; +} diff --git a/crypto/sntrup761/params.h b/crypto/sntrup761/params.h new file mode 100644 index 0000000000..569913e33a --- /dev/null +++ b/crypto/sntrup761/params.h @@ -0,0 +1,63 @@ +#ifndef params_H +#define params_H + +/* menu of parameter choices: */ + +#include "paramsmenu.h" + +/* what the menu means: */ + +#if defined(SIZE761) +#define p 761 +#define q 4591 +#define Rounded_bytes 1007 +#ifndef LPR +#define Rq_bytes 1158 +#define w 286 +#else +#define w 250 +#define tau0 2156 +#define tau1 114 +#define tau2 2007 +#define tau3 287 +#endif + +#elif defined(SIZE653) +#define p 653 +#define q 4621 +#define Rounded_bytes 865 +#ifndef LPR +#define Rq_bytes 994 +#define w 288 +#else +#define w 252 +#define tau0 2175 +#define tau1 113 +#define tau2 2031 +#define tau3 290 +#endif + +#elif defined(SIZE857) +#define p 857 +#define q 5167 +#define Rounded_bytes 1152 +#ifndef LPR +#define Rq_bytes 1322 +#define w 322 +#else +#define w 281 +#define tau0 2433 +#define tau1 101 +#define tau2 2265 +#define tau3 324 +#endif + +#else +#error "no parameter set defined" +#endif + +#ifdef LPR +#define I 256 +#endif + +#endif diff --git a/crypto/sntrup761/paramsmenu.h b/crypto/sntrup761/paramsmenu.h new file mode 100644 index 0000000000..e9cae978a3 --- /dev/null +++ b/crypto/sntrup761/paramsmenu.h @@ -0,0 +1,8 @@ +/* pick one of these three: */ +#define SIZE761 +#undef SIZE653 +#undef SIZE857 + +/* pick one of these two: */ +#define SNTRUP /* Streamlined NTRU Prime */ +#undef LPR /* NTRU LPRime */ diff --git a/crypto/sntrup761/sntrup761_meth.c b/crypto/sntrup761/sntrup761_meth.c new file mode 100644 index 0000000000..a279b942f6 --- /dev/null +++ b/crypto/sntrup761/sntrup761_meth.c @@ -0,0 +1,202 @@ +#include +#include "crypto/evp.h" +#include "crypto/asn1.h" +#include "crypto_kem.h" + +typedef struct { + unsigned char pubkey[crypto_kem_PUBLICKEYBYTES]; + unsigned char prvkey[crypto_kem_SECRETKEYBYTES]; + unsigned char ptskey[crypto_kem_BYTES]; +} SNTRUP761_KEY; + +static +void sntrup761_pkey_free(EVP_PKEY *pkey) +{ + SNTRUP761_KEY *key = NULL; + + if (pkey != NULL + && NULL != (key = EVP_PKEY_get0(pkey))) { + OPENSSL_secure_clear_free(key, sizeof(*key)); + } +} + +static int pkey_sntrup761_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) +{ + SNTRUP761_KEY *key = NULL; + + if ((key = OPENSSL_secure_zalloc(sizeof(*key))) == NULL + || crypto_kem_keypair(key->pubkey, key->prvkey) != 0 + || !EVP_PKEY_assign(pkey, EVP_PKEY_SNTRUP761, key)) { + OPENSSL_secure_free(key); + return 0; + } + + return 1; +} + +static int pkey_sntrup761_encrypt(EVP_PKEY_CTX *ctx, + unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen) +{ + SNTRUP761_KEY *key = NULL; + + if (out == NULL && outlen != NULL) { + *outlen = crypto_kem_CIPHERTEXTBYTES; + return 1; + } + if (in == NULL && inlen == 0 && out != NULL && outlen != NULL) { + if (ctx->pkey == NULL || ctx->pkey->pkey.ptr == NULL) + return 0; + key = (SNTRUP761_KEY *)(ctx->pkey->pkey.ptr); + *outlen = crypto_kem_CIPHERTEXTBYTES; + return crypto_kem_enc(out, key->ptskey, key->pubkey) == 0; + } + return 0; +} + +static int pkey_sntrup761_decrypt(EVP_PKEY_CTX *ctx, + unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen) +{ + SNTRUP761_KEY *key = NULL; + + if (out == NULL && outlen == NULL && in != NULL && inlen == crypto_kem_CIPHERTEXTBYTES) { + if (ctx->pkey == NULL || ctx->pkey->pkey.ptr == NULL) + return 0; + key = (SNTRUP761_KEY *)(ctx->pkey->pkey.ptr); + return crypto_kem_dec(key->ptskey, in, key->prvkey) == 0; + } + return 0; +} + +static int pkey_sntrup761_derive(EVP_PKEY_CTX *ctx, unsigned char *key, + size_t *keylen) +{ + SNTRUP761_KEY *k = NULL; + + if (keylen == NULL) + return 0; + if (key == NULL) { + *keylen = crypto_kem_BYTES; + return 1; + } + k = (SNTRUP761_KEY *)(ctx->pkey->pkey.ptr); + memcpy(key, k->ptskey, crypto_kem_BYTES); + OPENSSL_cleanse(k->ptskey, crypto_kem_BYTES); + *keylen = crypto_kem_BYTES; + return 1; +} + +static int pkey_sntrup761_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) +{ + /* Only need to handle peer key for derivation */ + if (type == EVP_PKEY_CTRL_PEER_KEY) + return 1; + return -2; +} + +/* BEGIN ameth stuff */ + +static int sntrup761_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2) +{ + SNTRUP761_KEY *key = NULL; + + switch (op) { + case ASN1_PKEY_CTRL_SET1_TLS_ENCPT: + if ((key = OPENSSL_zalloc(sizeof(*key))) == NULL) + return 0; + memcpy(key->pubkey, arg2, arg1); + return EVP_PKEY_assign(pkey, EVP_PKEY_SNTRUP761, key); + case ASN1_PKEY_CTRL_GET1_TLS_ENCPT: + if (pkey->pkey.ptr == NULL) + return 0; + key = (SNTRUP761_KEY *)(pkey->pkey.ptr); + unsigned char **ppt = arg2; + *ppt = OPENSSL_memdup(key->pubkey, crypto_kem_PUBLICKEYBYTES); + return (*ppt == NULL) ? 0 : crypto_kem_PUBLICKEYBYTES; + default: + return -2; + } +} + +/* "parameters" are always equal */ +static int sntrup761_param_cmp(const EVP_PKEY *a, const EVP_PKEY *b) +{ + return 1; +} + +/* END ameth stuff */ + +const EVP_PKEY_ASN1_METHOD sntrup761_asn1_meth = { + EVP_PKEY_SNTRUP761, /* pkey_id */ + EVP_PKEY_SNTRUP761, /* pkey_base_id */ + 0, /*pkey_flags */ + "SNTRUP761", /* pem_str */ + "OpenSSL SNTRUP761 algorithm", /* info */ + 0, /* pub_decode */ + 0, /* pub_encode */ + 0, /* pub_cmp */ + 0, /* pub_print */ + 0, /* priv_decode */ + 0, /* priv_encode */ + 0, /* priv_print */ + 0, /* pkey_size */ + 0, /* pkey_bits */ + 0, /* pkey_security_bits */ + 0, /* param_decode */ + 0, /* param_encode */ + 0, /* param_missing */ + 0, /* param_copy */ + sntrup761_param_cmp, /* param_cmp */ + 0, /* param_print */ + 0, /* sig_print */ + sntrup761_pkey_free, /* pkey_free */ + sntrup761_ctrl, /* pkey_ctrl */ + 0, /* old_priv_decode */ + 0, /* old_priv_encode */ + 0, /* item_verify */ + 0, /* item_sign */ + 0, /* siginf_set */ + 0, /* pkey_check */ + 0, /* pkey_public_check */ + 0, /* pkey_param_check */ + 0, /* set_priv_key */ + 0, /* set_pub_key */ + 0, /* get_priv_key */ + 0 /* get_pub_key */ +}; + +const EVP_PKEY_METHOD sntrup761_pkey_meth = { + EVP_PKEY_SNTRUP761, /* pkey_id */ + 0, /* flags */ + 0, /* init */ + 0, /* copy */ + 0, /* cleanup */ + 0, /* paramgen_init */ + 0, /* paramgen */ + 0, /* keygen_init */ + pkey_sntrup761_keygen, /* keygen */ + 0, /* sign_init */ + 0, /* sign */ + 0, /* verify_init */ + 0, /* verify */ + 0, /* verify_recover_init */ + 0, /* verify_recover */ + 0, /* signctx_init */ + 0, /* signctx */ + 0, /* verifyctx_init */ + 0, /* verifyctx */ + 0, /* encrypt_init */ + pkey_sntrup761_encrypt, /* encrypt */ + 0, /* decrypt_init */ + pkey_sntrup761_decrypt, /* decrypt */ + 0, /* derive_init */ + pkey_sntrup761_derive, /* derive */ + pkey_sntrup761_ctrl, /* ctrl */ + 0, /* ctrl_str */ + 0, /* digestsign */ + 0, /* digestverify */ + 0, /* check */ + 0, /* public_check */ + 0 /* param_check */ +}; diff --git a/crypto/sntrup761/uint16.h b/crypto/sntrup761/uint16.h new file mode 100644 index 0000000000..5a0e815e95 --- /dev/null +++ b/crypto/sntrup761/uint16.h @@ -0,0 +1,7 @@ +#ifndef UINT16_H +#define UINT16_H + +#include +typedef uint16_t uint16; + +#endif diff --git a/crypto/sntrup761/uint32.c b/crypto/sntrup761/uint32.c new file mode 100644 index 0000000000..1876fcd952 --- /dev/null +++ b/crypto/sntrup761/uint32.c @@ -0,0 +1,70 @@ +#include "uint32.h" +#include "uint64.h" + +/* +CPU division instruction typically takes time depending on x. +This software is designed to take time independent of x. +Time still varies depending on m; user must ensure that m is constant. +Time also varies on CPUs where multiplication is variable-time. +There could be more CPU issues. +There could also be compiler issues. +*/ + +void uint32_divmod_uint14(uint32 *q,uint16 *r,uint32 x,uint16 m) +{ + uint32 v = 0x80000000; + uint32 qpart; + uint32 mask; + + v /= m; + + /* caller guarantees m > 0 */ + /* caller guarantees m < 16384 */ + /* vm <= 2^31 <= vm+m-1 */ + /* xvm <= 2^31 x <= xvm+x(m-1) */ + + *q = 0; + + qpart = (x*(uint64)v)>>31; + /* 2^31 qpart <= xv <= 2^31 qpart + 2^31-1 */ + /* 2^31 qpart m <= xvm <= 2^31 qpart m + (2^31-1)m */ + /* 2^31 qpart m <= 2^31 x <= 2^31 qpart m + (2^31-1)m + x(m-1) */ + /* 0 <= 2^31 newx <= (2^31-1)m + x(m-1) */ + /* 0 <= newx <= (1-1/2^31)m + x(m-1)/2^31 */ + /* 0 <= newx <= (1-1/2^31)(2^14-1) + (2^32-1)((2^14-1)-1)/2^31 */ + + x -= qpart*m; *q += qpart; + /* x <= 49146 */ + + qpart = (x*(uint64)v)>>31; + /* 0 <= newx <= (1-1/2^31)m + x(m-1)/2^31 */ + /* 0 <= newx <= m + 49146(2^14-1)/2^31 */ + /* 0 <= newx <= m + 0.4 */ + /* 0 <= newx <= m */ + + x -= qpart*m; *q += qpart; + /* x <= m */ + + x -= m; *q += 1; + mask = -(x>>31); + x += mask&(uint32)m; *q += mask; + /* x < m */ + + *r = x; +} + +uint32 uint32_div_uint14(uint32 x,uint16 m) +{ + uint32 q; + uint16 r; + uint32_divmod_uint14(&q,&r,x,m); + return q; +} + +uint16 uint32_mod_uint14(uint32 x,uint16 m) +{ + uint32 q; + uint16 r; + uint32_divmod_uint14(&q,&r,x,m); + return r; +} diff --git a/crypto/sntrup761/uint32.h b/crypto/sntrup761/uint32.h new file mode 100644 index 0000000000..defbb588bf --- /dev/null +++ b/crypto/sntrup761/uint32.h @@ -0,0 +1,21 @@ +#ifndef UINT32_H +#define UINT32_H + +#include "uint16.h" + +typedef uint32_t uint32; + +/* +assuming 1 <= m < 16384: +q = uint32_div_uint14(x,m) means q = x/m +r = uint32_mod_uint14(x,m) means r = x/m +uint32_moddiv_uint14(&q,&r,x,m) means q = x/m, r = x%m +*/ + +extern uint32 uint32_div_uint14(uint32,uint16); +extern uint16 uint32_mod_uint14(uint32,uint16); +extern void uint32_divmod_uint14(uint32 *,uint16 *,uint32,uint16); + +extern void uint32_sort(uint32 *,int); + +#endif diff --git a/crypto/sntrup761/uint32_sort.c b/crypto/sntrup761/uint32_sort.c new file mode 100644 index 0000000000..074dbbd86d --- /dev/null +++ b/crypto/sntrup761/uint32_sort.c @@ -0,0 +1,34 @@ +#include "uint32.h" + +static void minmax(uint32 *x,uint32 *y) +{ + uint32 xi = *x; + uint32 yi = *y; + uint32 xy = xi ^ yi; + uint32 c = yi - xi; + c ^= xy & (c ^ yi ^ 0x80000000); + c >>= 31; + c = -c; + c &= xy; + *x = xi ^ c; + *y = yi ^ c; +} + +void uint32_sort(uint32 *x,int n) +{ + int top,p,q,i; + + if (n < 2) return; + top = 1; + while (top < n - top) top += top; + + for (p = top;p > 0;p >>= 1) { + for (i = 0;i < n - p;++i) + if (!(i & p)) + minmax(x + i,x + i + p); + for (q = top;q > p;q >>= 1) + for (i = 0;i < n - q;++i) + if (!(i & p)) + minmax(x + i + p,x + i + q); + } +} diff --git a/crypto/sntrup761/uint64.h b/crypto/sntrup761/uint64.h new file mode 100644 index 0000000000..695fd3c270 --- /dev/null +++ b/crypto/sntrup761/uint64.h @@ -0,0 +1,8 @@ +#ifndef UINT64_H +#define UINT64_H + +#include + +typedef uint64_t uint64; + +#endif diff --git a/include/crypto/asn1.h b/include/crypto/asn1.h index 9c9b4d8974..ca7f20ce69 100644 --- a/include/crypto/asn1.h +++ b/include/crypto/asn1.h @@ -76,6 +76,7 @@ extern const EVP_PKEY_ASN1_METHOD ecx25519_asn1_meth; extern const EVP_PKEY_ASN1_METHOD ecx448_asn1_meth; extern const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth; extern const EVP_PKEY_ASN1_METHOD ed448_asn1_meth; +extern const EVP_PKEY_ASN1_METHOD sntrup761_asn1_meth; extern const EVP_PKEY_ASN1_METHOD sm2_asn1_meth; extern const EVP_PKEY_ASN1_METHOD poly1305_asn1_meth; diff --git a/include/crypto/evp.h b/include/crypto/evp.h index d86aed36f0..69c01bad49 100644 --- a/include/crypto/evp.h +++ b/include/crypto/evp.h @@ -99,6 +99,7 @@ extern const EVP_PKEY_METHOD dhx_pkey_meth; extern const EVP_PKEY_METHOD dsa_pkey_meth; extern const EVP_PKEY_METHOD ec_pkey_meth; extern const EVP_PKEY_METHOD sm2_pkey_meth; +extern const EVP_PKEY_METHOD sntrup761_pkey_meth; extern const EVP_PKEY_METHOD ecx25519_pkey_meth; extern const EVP_PKEY_METHOD ecx448_pkey_meth; extern const EVP_PKEY_METHOD ed25519_pkey_meth; diff --git a/include/openssl/evp.h b/include/openssl/evp.h index a411f3f2f9..94912430bd 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -62,6 +62,7 @@ # define EVP_PKEY_ED25519 NID_ED25519 # define EVP_PKEY_X448 NID_X448 # define EVP_PKEY_ED448 NID_ED448 +# define EVP_PKEY_SNTRUP761 NID_SNTRUP761 #ifdef __cplusplus extern "C" { -- 2.34.0