diff --git a/Configure b/Configure index 2e9efaa5f3..b7cb4ac284 100755 --- a/Configure +++ b/Configure @@ -317,7 +317,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/objects/obj_dat.h b/crypto/objects/obj_dat.h index d1b1bc7faf..4a2b18ef10 100644 --- a/crypto/objects/obj_dat.h +++ b/crypto/objects/obj_dat.h @@ -10,7 +10,7 @@ */ /* Serialized OID's */ -static const unsigned char so[7762] = { +static const unsigned char so[7774] = { 0x2A,0x86,0x48,0x86,0xF7,0x0D, /* [ 0] OBJ_rsadsi */ 0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, /* [ 6] OBJ_pkcs */ 0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x02, /* [ 13] OBJ_md2 */ @@ -1076,9 +1076,10 @@ static const unsigned char so[7762] = { 0x2A,0x85,0x03,0x07,0x01,0x02,0x01,0x01,0x04, /* [ 7736] OBJ_id_tc26_gost_3410_2012_256_paramSetD */ 0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x0C, /* [ 7745] OBJ_hmacWithSHA512_224 */ 0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x0D, /* [ 7753] OBJ_hmacWithSHA512_256 */ + 0x2B,0x06,0x01,0x04,0x01,0x83,0x88,0x57,0x00,0x03,0x01,0x14, /* [ 7761] OBJ_SNTRUP761 */ }; -#define NUM_NID 1195 +#define NUM_NID 1196 static const ASN1_OBJECT nid_objs[NUM_NID] = { {"UNDEF", "undefined", NID_undef}, {"rsadsi", "RSA Data Security, Inc.", NID_rsadsi, 6, &so[0]}, @@ -2275,9 +2276,10 @@ static const ASN1_OBJECT nid_objs[NUM_NID] = { {"magma-mac", "magma-mac", NID_magma_mac}, {"hmacWithSHA512-224", "hmacWithSHA512-224", NID_hmacWithSHA512_224, 8, &so[7745]}, {"hmacWithSHA512-256", "hmacWithSHA512-256", NID_hmacWithSHA512_256, 8, &so[7753]}, + {"SNTRUP761", "SNTRUP761", NID_SNTRUP761, 12, &so[7761]}, }; -#define NUM_SN 1186 +#define NUM_SN 1187 static const unsigned int sn_objs[NUM_SN] = { 364, /* "AD_DVCS" */ 419, /* "AES-128-CBC" */ @@ -2555,6 +2557,7 @@ static const unsigned int sn_objs[NUM_SN] = { 167, /* "SMIME-CAPS" */ 100, /* "SN" */ 1006, /* "SNILS" */ + 1195, /* "SNTRUP761" */ 16, /* "ST" */ 143, /* "SXNetID" */ 1062, /* "SipHash" */ @@ -3467,7 +3470,7 @@ static const unsigned int sn_objs[NUM_SN] = { 1093, /* "x509ExtAdmission" */ }; -#define NUM_LN 1186 +#define NUM_LN 1187 static const unsigned int ln_objs[NUM_LN] = { 363, /* "AD Time Stamping" */ 405, /* "ANSI X9.62" */ @@ -3625,6 +3628,7 @@ static const unsigned int ln_objs[NUM_LN] = { 167, /* "S/MIME Capabilities" */ 1006, /* "SNILS" */ 387, /* "SNMPv2" */ + 1195, /* "SNTRUP761" */ 1025, /* "SSH Client" */ 1026, /* "SSH Server" */ 512, /* "Secure Electronic Transactions" */ @@ -4657,7 +4661,7 @@ static const unsigned int ln_objs[NUM_LN] = { 125, /* "zlib compression" */ }; -#define NUM_OBJ 1071 +#define NUM_OBJ 1072 static const unsigned int obj_objs[NUM_OBJ] = { 0, /* OBJ_undef 0 */ 181, /* OBJ_iso 1 */ @@ -5719,6 +5723,7 @@ static const unsigned int obj_objs[NUM_OBJ] = { 957, /* OBJ_jurisdictionCountryName 1 3 6 1 4 1 311 60 2 1 3 */ 1056, /* OBJ_blake2b512 1 3 6 1 4 1 1722 12 2 1 16 */ 1057, /* OBJ_blake2s256 1 3 6 1 4 1 1722 12 2 2 8 */ + 1195, /* OBJ_SNTRUP761 1 3 6 1 4 1 50263 0 3 1 20 */ 1159, /* OBJ_dstu4145be 1 2 804 2 1 1 1 1 3 1 1 1 1 */ 1160, /* OBJ_uacurve0 1 2 804 2 1 1 1 1 3 1 1 2 0 */ 1161, /* OBJ_uacurve1 1 2 804 2 1 1 1 1 3 1 1 2 1 */ diff --git a/crypto/objects/obj_mac.num b/crypto/objects/obj_mac.num index 1b6a9c61a1..ad7f563c91 100644 --- a/crypto/objects/obj_mac.num +++ b/crypto/objects/obj_mac.num @@ -1192,3 +1192,4 @@ magma_cfb 1191 magma_mac 1192 hmacWithSHA512_224 1193 hmacWithSHA512_256 1194 +SNTRUP761 1195 diff --git a/crypto/objects/objects.txt b/crypto/objects/objects.txt index c49d4c568b..75099d7379 100644 --- a/crypto/objects/objects.txt +++ b/crypto/objects/objects.txt @@ -1611,6 +1611,8 @@ id-pkinit 5 : pkInitKDC : Signing KDC Response 1 3 101 112 : ED25519 1 3 101 113 : ED448 +# NIST PQC KEM hacks +1 3 6 1 4 1 50263 0 3 1 20 : SNTRUP761 # NIDs for cipher key exchange : KxRSA : kx-rsa 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/fuzz/oids.txt b/fuzz/oids.txt index eda55e4e79..4c04c2f95a 100644 --- a/fuzz/oids.txt +++ b/fuzz/oids.txt @@ -1063,3 +1063,4 @@ OBJ_id_tc26_gost_3410_2012_256_paramSetC="\x2A\x85\x03\x07\x01\x02\x01\x01\x03" OBJ_id_tc26_gost_3410_2012_256_paramSetD="\x2A\x85\x03\x07\x01\x02\x01\x01\x04" OBJ_hmacWithSHA512_224="\x2A\x86\x48\x86\xF7\x0D\x02\x0C" OBJ_hmacWithSHA512_256="\x2A\x86\x48\x86\xF7\x0D\x02\x0D" +OBJ_SNTRUP761="\x2B\x06\x01\x04\x01\x83\x88\x57\x00\x03\x01\x14" 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" { diff --git a/include/openssl/obj_mac.h b/include/openssl/obj_mac.h index 483fc0509e..1d077dc3ec 100644 --- a/include/openssl/obj_mac.h +++ b/include/openssl/obj_mac.h @@ -5000,6 +5000,10 @@ #define NID_ED448 1088 #define OBJ_ED448 1L,3L,101L,113L +#define SN_SNTRUP761 "SNTRUP761" +#define NID_SNTRUP761 1195 +#define OBJ_SNTRUP761 1L,3L,6L,1L,4L,1L,50263L,0L,3L,1L,20L + #define SN_kx_rsa "KxRSA" #define LN_kx_rsa "kx-rsa" #define NID_kx_rsa 1037 diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index a987604bcd..45173c3f66 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -4694,13 +4694,13 @@ EVP_PKEY *ssl_generate_pkey(EVP_PKEY *pm) EVP_PKEY_CTX_free(pctx); return pkey; } -#ifndef OPENSSL_NO_EC + /* Generate a private key from a group ID */ EVP_PKEY *ssl_generate_pkey_group(SSL *s, uint16_t id) { + const TLS_GROUP_INFO *ginf = tls1_group_id_lookup(id); EVP_PKEY_CTX *pctx = NULL; EVP_PKEY *pkey = NULL; - const TLS_GROUP_INFO *ginf = tls1_group_id_lookup(id); uint16_t gtype; if (ginf == NULL) { @@ -4708,11 +4708,15 @@ EVP_PKEY *ssl_generate_pkey_group(SSL *s, uint16_t id) ERR_R_INTERNAL_ERROR); goto err; } - gtype = ginf->flags & TLS_CURVE_TYPE; - if (gtype == TLS_CURVE_CUSTOM) - pctx = EVP_PKEY_CTX_new_id(ginf->nid, NULL); - else - pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + gtype = ginf->flags & TLS_GROUP_TYPE; +# ifndef OPENSSL_NO_EC + { + if (gtype & (TLS_GROUP_CURVE_CUSTOM | TLS_GROUP_KEM)) + pctx = EVP_PKEY_CTX_new_id(ginf->nid, NULL); + else + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + } +# endif if (pctx == NULL) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_PKEY_GROUP, ERR_R_MALLOC_FAILURE); @@ -4723,12 +4727,16 @@ EVP_PKEY *ssl_generate_pkey_group(SSL *s, uint16_t id) ERR_R_EVP_LIB); goto err; } - if (gtype != TLS_CURVE_CUSTOM - && EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, ginf->nid) <= 0) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_PKEY_GROUP, - ERR_R_EVP_LIB); - goto err; +# ifndef OPENSSL_NO_EC + { + if (!(gtype & (TLS_GROUP_CURVE_CUSTOM | TLS_GROUP_KEM)) + && EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, ginf->nid) <= 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_PKEY_GROUP, + ERR_R_EVP_LIB); + goto err; + } } +# endif if (EVP_PKEY_keygen(pctx, &pkey) <= 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_PKEY_GROUP, ERR_R_EVP_LIB); @@ -4749,11 +4757,12 @@ EVP_PKEY *ssl_generate_param_group(uint16_t id) EVP_PKEY_CTX *pctx = NULL; EVP_PKEY *pkey = NULL; const TLS_GROUP_INFO *ginf = tls1_group_id_lookup(id); + int pkey_ctx_id; if (ginf == NULL) goto err; - if ((ginf->flags & TLS_CURVE_TYPE) == TLS_CURVE_CUSTOM) { + if ((ginf->flags & TLS_GROUP_TYPE) & (TLS_GROUP_CURVE_CUSTOM | TLS_GROUP_KEM)) { pkey = EVP_PKEY_new(); if (pkey != NULL && EVP_PKEY_set_type(pkey, ginf->nid)) return pkey; @@ -4761,13 +4770,18 @@ EVP_PKEY *ssl_generate_param_group(uint16_t id) return NULL; } - pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + pkey_ctx_id = EVP_PKEY_EC; + pctx = EVP_PKEY_CTX_new_id(pkey_ctx_id, NULL); if (pctx == NULL) goto err; if (EVP_PKEY_paramgen_init(pctx) <= 0) goto err; - if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, ginf->nid) <= 0) - goto err; +# ifndef OPENSSL_NO_EC + { + if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, ginf->nid) <= 0) + goto err; + } +# endif if (EVP_PKEY_paramgen(pctx, &pkey) <= 0) { EVP_PKEY_free(pkey); pkey = NULL; @@ -4777,7 +4791,6 @@ EVP_PKEY *ssl_generate_param_group(uint16_t id) EVP_PKEY_CTX_free(pctx); return pkey; } -#endif /* Derive secrets for ECDH/DH */ int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey, int gensecret) diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 8ddbde7729..132a76d35b 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -1508,14 +1508,19 @@ typedef struct sigalg_lookup_st { typedef struct tls_group_info_st { int nid; /* Curve NID */ int secbits; /* Bits of security (from SP800-57) */ - uint16_t flags; /* Flags: currently just group type */ + uint32_t flags; /* For group type and applicable TLS versions */ + uint16_t group_id; /* Group ID */ } TLS_GROUP_INFO; /* flags values */ -# define TLS_CURVE_TYPE 0x3 /* Mask for group type */ -# define TLS_CURVE_PRIME 0x0 -# define TLS_CURVE_CHAR2 0x1 -# define TLS_CURVE_CUSTOM 0x2 +# define TLS_GROUP_TYPE 0x0000000FU /* Mask for group type */ +# define TLS_GROUP_CURVE_PRIME 0x00000001U +# define TLS_GROUP_CURVE_CHAR2 0x00000002U +# define TLS_GROUP_CURVE_CUSTOM 0x00000004U +# define TLS_GROUP_KEM 0x00000008U +# define TLS_GROUP_ONLY_FOR_TLS1_3 0x00000010U + +# define TLS_GROUP_KEM_FOR_TLS1_3 (TLS_GROUP_KEM|TLS_GROUP_ONLY_FOR_TLS1_3) typedef struct cert_pkey_st CERT_PKEY; @@ -2522,6 +2527,7 @@ void tls1_get_formatlist(SSL *s, const unsigned char **pformats, size_t *num_formats); __owur int tls1_check_ec_tmp_key(SSL *s, unsigned long id); __owur EVP_PKEY *ssl_generate_pkey_group(SSL *s, uint16_t id); +__owur int tls_valid_group(SSL *s, uint16_t group_id, int version); __owur EVP_PKEY *ssl_generate_param_group(uint16_t id); # endif /* OPENSSL_NO_EC */ diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c index bcce0f1d95..ab0937d29d 100644 --- a/ssl/statem/extensions_clnt.c +++ b/ssl/statem/extensions_clnt.c @@ -174,36 +174,45 @@ EXT_RETURN tls_construct_ctos_supported_groups(SSL *s, WPACKET *pkt, { const uint16_t *pgroups = NULL; size_t num_groups = 0, i; + int min_version, max_version, reason; if (!use_ecc(s)) return EXT_RETURN_NOT_SENT; + reason = ssl_get_min_max_version(s, &min_version, &max_version, NULL); + if (reason != 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_CTOS_SUPPORTED_GROUPS, reason); + return EXT_RETURN_FAIL; + } + /* * Add TLS extension supported_groups to the ClientHello message */ - /* TODO(TLS1.3): Add support for DHE groups */ tls1_get_supported_groups(s, &pgroups, &num_groups); if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_supported_groups) /* Sub-packet for supported_groups extension */ || !WPACKET_start_sub_packet_u16(pkt) - || !WPACKET_start_sub_packet_u16(pkt)) { + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_set_flags(pkt, WPACKET_FLAGS_NON_ZERO_LENGTH)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CTOS_SUPPORTED_GROUPS, ERR_R_INTERNAL_ERROR); return EXT_RETURN_FAIL; } - /* Copy curve ID if supported */ + /* Copy group ID if supported */ for (i = 0; i < num_groups; i++) { uint16_t ctmp = pgroups[i]; - if (tls_curve_allowed(s, ctmp, SSL_SECOP_CURVE_SUPPORTED)) { + if (tls_valid_group(s, ctmp, max_version) + && tls_curve_allowed(s, ctmp, SSL_SECOP_CURVE_SUPPORTED)) { if (!WPACKET_put_bytes_u16(pkt, ctmp)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, - SSL_F_TLS_CONSTRUCT_CTOS_SUPPORTED_GROUPS, - ERR_R_INTERNAL_ERROR); - return EXT_RETURN_FAIL; - } + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_CTOS_SUPPORTED_GROUPS, + ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } } } if (!WPACKET_close(pkt) || !WPACKET_close(pkt)) { @@ -1788,6 +1797,7 @@ int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x, unsigned int group_id; PACKET encoded_pt; EVP_PKEY *ckey = s->s3->tmp.pkey, *skey = NULL; + const TLS_GROUP_INFO *ginf = NULL; /* Sanity check */ if (ckey == NULL || s->s3->peer_tmp != NULL) { @@ -1858,18 +1868,40 @@ int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x, return 0; } - skey = EVP_PKEY_new(); - if (skey == NULL || EVP_PKEY_copy_parameters(skey, ckey) <= 0) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_STOC_KEY_SHARE, - ERR_R_MALLOC_FAILURE); - return 0; - } - if (!EVP_PKEY_set1_tls_encodedpoint(skey, PACKET_data(&encoded_pt), - PACKET_remaining(&encoded_pt))) { - SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PARSE_STOC_KEY_SHARE, - SSL_R_BAD_ECPOINT); - EVP_PKEY_free(skey); - return 0; + if ((ginf = tls1_group_id_lookup(group_id)) != NULL + && (ginf->flags & TLS_GROUP_TYPE) == TLS_GROUP_KEM) { + EVP_PKEY_CTX *pctx = NULL; + + pctx = EVP_PKEY_CTX_new(ckey, NULL); + if (pctx == NULL || EVP_PKEY_decrypt_init(pctx) <= 0 + || EVP_PKEY_decrypt(pctx, NULL, NULL, + PACKET_data(&encoded_pt), + PACKET_remaining(&encoded_pt)) <= 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_STOC_KEY_SHARE, + ERR_R_INTERNAL_ERROR); + EVP_PKEY_CTX_free(pctx); + return 0; + } + + skey = ckey; + EVP_PKEY_up_ref(ckey); + + EVP_PKEY_CTX_free(pctx); + } else { + skey = EVP_PKEY_new(); + if (skey == NULL || EVP_PKEY_copy_parameters(skey, ckey) <= 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_STOC_KEY_SHARE, + ERR_R_MALLOC_FAILURE); + return 0; + } + + if (!EVP_PKEY_set1_tls_encodedpoint(skey, PACKET_data(&encoded_pt), + PACKET_remaining(&encoded_pt))) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PARSE_STOC_KEY_SHARE, + SSL_R_BAD_ECPOINT); + EVP_PKEY_free(skey); + return 0; + } } if (ssl_derive(s, ckey, skey, 1) == 0) { diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c index 3b07c6b940..06c4740751 100644 --- a/ssl/statem/extensions_srvr.c +++ b/ssl/statem/extensions_srvr.c @@ -1432,7 +1432,8 @@ EXT_RETURN tls_construct_stoc_supported_groups(SSL *s, WPACKET *pkt, for (i = 0; i < numgroups; i++) { uint16_t group = groups[i]; - if (tls_curve_allowed(s, group, SSL_SECOP_CURVE_SUPPORTED)) { + if (tls_valid_group(s, group, SSL_version(s)) + && tls_curve_allowed(s, group, SSL_SECOP_CURVE_SUPPORTED)) { if (first) { /* * Check if the client is already using our preferred group. If @@ -1683,9 +1684,10 @@ EXT_RETURN tls_construct_stoc_key_share(SSL *s, WPACKET *pkt, size_t chainidx) { #ifndef OPENSSL_NO_TLS1_3 - unsigned char *encodedPoint; + unsigned char *encodedPoint = NULL; size_t encoded_pt_len = 0; EVP_PKEY *ckey = s->s3->peer_tmp, *skey = NULL; + const TLS_GROUP_INFO *ginf = NULL; if (s->hello_retry_request == SSL_HRR_PENDING) { if (ckey != NULL) { @@ -1723,20 +1725,42 @@ EXT_RETURN tls_construct_stoc_key_share(SSL *s, WPACKET *pkt, return EXT_RETURN_FAIL; } - skey = ssl_generate_pkey(ckey); - if (skey == NULL) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, - ERR_R_MALLOC_FAILURE); - return EXT_RETURN_FAIL; - } + if ((ginf = tls1_group_id_lookup(s->s3->group_id)) != NULL + && (ginf->flags & TLS_GROUP_TYPE) == TLS_GROUP_KEM) { + EVP_PKEY_CTX *pctx = NULL; - /* Generate encoding of server key */ - encoded_pt_len = EVP_PKEY_get1_tls_encodedpoint(skey, &encodedPoint); - if (encoded_pt_len == 0) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, - ERR_R_EC_LIB); - EVP_PKEY_free(skey); - return EXT_RETURN_FAIL; + pctx = EVP_PKEY_CTX_new(ckey, NULL); + if (pctx == NULL || EVP_PKEY_encrypt_init(pctx) <= 0 + || EVP_PKEY_encrypt(pctx, NULL, &encoded_pt_len, NULL, 0) <= 0 + || (encodedPoint = OPENSSL_malloc(encoded_pt_len)) == NULL + || EVP_PKEY_encrypt(pctx, encodedPoint, &encoded_pt_len, + NULL, 0) <= 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, + ERR_R_INTERNAL_ERROR); + OPENSSL_free(encodedPoint); + EVP_PKEY_CTX_free(pctx); + return EXT_RETURN_FAIL; + } + + skey = ckey; + EVP_PKEY_up_ref(ckey); + + EVP_PKEY_CTX_free(pctx); + } else { + skey = ssl_generate_pkey(ckey); + if (skey == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, + ERR_R_MALLOC_FAILURE); + return EXT_RETURN_FAIL; + } + /* Generate encoding of server key */ + encoded_pt_len = EVP_PKEY_get1_tls_encodedpoint(skey, &encodedPoint); + if (encoded_pt_len == 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, + ERR_R_EC_LIB); + EVP_PKEY_free(skey); + return EXT_RETURN_FAIL; + } } if (!WPACKET_sub_memcpy_u16(pkt, encodedPoint, encoded_pt_len) diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index a254fd5a05..e396a113cb 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -131,44 +131,43 @@ int tls1_clear(SSL *s) return 1; } -#ifndef OPENSSL_NO_EC - /* * Table of curve information. - * Do not delete entries or reorder this array! It is used as a lookup - * table: the index of each entry is one less than the TLS curve id. */ static const TLS_GROUP_INFO nid_list[] = { - {NID_sect163k1, 80, TLS_CURVE_CHAR2}, /* sect163k1 (1) */ - {NID_sect163r1, 80, TLS_CURVE_CHAR2}, /* sect163r1 (2) */ - {NID_sect163r2, 80, TLS_CURVE_CHAR2}, /* sect163r2 (3) */ - {NID_sect193r1, 80, TLS_CURVE_CHAR2}, /* sect193r1 (4) */ - {NID_sect193r2, 80, TLS_CURVE_CHAR2}, /* sect193r2 (5) */ - {NID_sect233k1, 112, TLS_CURVE_CHAR2}, /* sect233k1 (6) */ - {NID_sect233r1, 112, TLS_CURVE_CHAR2}, /* sect233r1 (7) */ - {NID_sect239k1, 112, TLS_CURVE_CHAR2}, /* sect239k1 (8) */ - {NID_sect283k1, 128, TLS_CURVE_CHAR2}, /* sect283k1 (9) */ - {NID_sect283r1, 128, TLS_CURVE_CHAR2}, /* sect283r1 (10) */ - {NID_sect409k1, 192, TLS_CURVE_CHAR2}, /* sect409k1 (11) */ - {NID_sect409r1, 192, TLS_CURVE_CHAR2}, /* sect409r1 (12) */ - {NID_sect571k1, 256, TLS_CURVE_CHAR2}, /* sect571k1 (13) */ - {NID_sect571r1, 256, TLS_CURVE_CHAR2}, /* sect571r1 (14) */ - {NID_secp160k1, 80, TLS_CURVE_PRIME}, /* secp160k1 (15) */ - {NID_secp160r1, 80, TLS_CURVE_PRIME}, /* secp160r1 (16) */ - {NID_secp160r2, 80, TLS_CURVE_PRIME}, /* secp160r2 (17) */ - {NID_secp192k1, 80, TLS_CURVE_PRIME}, /* secp192k1 (18) */ - {NID_X9_62_prime192v1, 80, TLS_CURVE_PRIME}, /* secp192r1 (19) */ - {NID_secp224k1, 112, TLS_CURVE_PRIME}, /* secp224k1 (20) */ - {NID_secp224r1, 112, TLS_CURVE_PRIME}, /* secp224r1 (21) */ - {NID_secp256k1, 128, TLS_CURVE_PRIME}, /* secp256k1 (22) */ - {NID_X9_62_prime256v1, 128, TLS_CURVE_PRIME}, /* secp256r1 (23) */ - {NID_secp384r1, 192, TLS_CURVE_PRIME}, /* secp384r1 (24) */ - {NID_secp521r1, 256, TLS_CURVE_PRIME}, /* secp521r1 (25) */ - {NID_brainpoolP256r1, 128, TLS_CURVE_PRIME}, /* brainpoolP256r1 (26) */ - {NID_brainpoolP384r1, 192, TLS_CURVE_PRIME}, /* brainpoolP384r1 (27) */ - {NID_brainpoolP512r1, 256, TLS_CURVE_PRIME}, /* brainpool512r1 (28) */ - {EVP_PKEY_X25519, 128, TLS_CURVE_CUSTOM}, /* X25519 (29) */ - {EVP_PKEY_X448, 224, TLS_CURVE_CUSTOM}, /* X448 (30) */ +# ifndef OPENSSL_NO_EC + {NID_sect163k1, 80, TLS_GROUP_CURVE_CHAR2, 0x0001}, /* sect163k1 (1) */ + {NID_sect163r1, 80, TLS_GROUP_CURVE_CHAR2, 0x0002}, /* sect163r1 (2) */ + {NID_sect163r2, 80, TLS_GROUP_CURVE_CHAR2, 0x0003}, /* sect163r2 (3) */ + {NID_sect193r1, 80, TLS_GROUP_CURVE_CHAR2, 0x0004}, /* sect193r1 (4) */ + {NID_sect193r2, 80, TLS_GROUP_CURVE_CHAR2, 0x0005}, /* sect193r2 (5) */ + {NID_sect233k1, 112, TLS_GROUP_CURVE_CHAR2, 0x0006}, /* sect233k1 (6) */ + {NID_sect233r1, 112, TLS_GROUP_CURVE_CHAR2, 0x0007}, /* sect233r1 (7) */ + {NID_sect239k1, 112, TLS_GROUP_CURVE_CHAR2, 0x0008}, /* sect239k1 (8) */ + {NID_sect283k1, 128, TLS_GROUP_CURVE_CHAR2, 0x0009}, /* sect283k1 (9) */ + {NID_sect283r1, 128, TLS_GROUP_CURVE_CHAR2, 0x000A}, /* sect283r1 (10) */ + {NID_sect409k1, 192, TLS_GROUP_CURVE_CHAR2, 0x000B}, /* sect409k1 (11) */ + {NID_sect409r1, 192, TLS_GROUP_CURVE_CHAR2, 0x000C}, /* sect409r1 (12) */ + {NID_sect571k1, 256, TLS_GROUP_CURVE_CHAR2, 0x000D}, /* sect571k1 (13) */ + {NID_sect571r1, 256, TLS_GROUP_CURVE_CHAR2, 0x000E}, /* sect571r1 (14) */ + {NID_secp160k1, 80, TLS_GROUP_CURVE_PRIME, 0x000F}, /* secp160k1 (15) */ + {NID_secp160r1, 80, TLS_GROUP_CURVE_PRIME, 0x0010}, /* secp160r1 (16) */ + {NID_secp160r2, 80, TLS_GROUP_CURVE_PRIME, 0x0011}, /* secp160r2 (17) */ + {NID_secp192k1, 80, TLS_GROUP_CURVE_PRIME, 0x0012}, /* secp192k1 (18) */ + {NID_X9_62_prime192v1, 80, TLS_GROUP_CURVE_PRIME, 0x0013}, /* secp192r1 (19) */ + {NID_secp224k1, 112, TLS_GROUP_CURVE_PRIME, 0x0014}, /* secp224k1 (20) */ + {NID_secp224r1, 112, TLS_GROUP_CURVE_PRIME, 0x0015}, /* secp224r1 (21) */ + {NID_secp256k1, 128, TLS_GROUP_CURVE_PRIME, 0x0016}, /* secp256k1 (22) */ + {NID_X9_62_prime256v1, 128, TLS_GROUP_CURVE_PRIME, 0x0017}, /* secp256r1 (23) */ + {NID_secp384r1, 192, TLS_GROUP_CURVE_PRIME, 0x0018}, /* secp384r1 (24) */ + {NID_secp521r1, 256, TLS_GROUP_CURVE_PRIME, 0x0019}, /* secp521r1 (25) */ + {NID_brainpoolP256r1, 128, TLS_GROUP_CURVE_PRIME, 0x001A}, /* brainpoolP256r1 (26) */ + {NID_brainpoolP384r1, 192, TLS_GROUP_CURVE_PRIME, 0x001B}, /* brainpoolP384r1 (27) */ + {NID_brainpoolP512r1, 256, TLS_GROUP_CURVE_PRIME, 0x001C}, /* brainpool512r1 (28) */ + {EVP_PKEY_X25519, 128, TLS_GROUP_CURVE_CUSTOM, 0x001D}, /* X25519 (29) */ + {EVP_PKEY_X448, 224, TLS_GROUP_CURVE_CUSTOM, 0x001E}, /* X448 (30) */ +# endif /* OPENSSL_NO_EC */ + {NID_SNTRUP761, 128, TLS_GROUP_KEM_FOR_TLS1_3, 0xFE00}, }; static const unsigned char ecformats_default[] = { @@ -178,14 +177,19 @@ static const unsigned char ecformats_default[] = { }; /* The default curves */ -static const uint16_t eccurves_default[] = { +static const uint16_t supported_groups_default[] = { +# ifndef OPENSSL_NO_EC 29, /* X25519 (29) */ 23, /* secp256r1 (23) */ 30, /* X448 (30) */ 25, /* secp521r1 (25) */ 24, /* secp384r1 (24) */ +# endif /* OPENSSL_NO_EC */ + 0xFE00, /* sntrup761 (0xFE00) */ }; +#ifndef OPENSSL_NO_EC + static const uint16_t suiteb_curves[] = { TLSEXT_curve_P_256, TLSEXT_curve_P_384 @@ -193,18 +197,23 @@ static const uint16_t suiteb_curves[] = { const TLS_GROUP_INFO *tls1_group_id_lookup(uint16_t group_id) { - /* ECC curves from RFC 4492 and RFC 7027 */ - if (group_id < 1 || group_id > OSSL_NELEM(nid_list)) - return NULL; - return &nid_list[group_id - 1]; + size_t i; + + /* ECC curves from RFC 4492 and RFC 7027 FFDHE group from RFC 8446 */ + for (i = 0; i < OSSL_NELEM(nid_list); i++) { + if (nid_list[i].group_id == group_id) + return &nid_list[i]; + } + return NULL; } static uint16_t tls1_nid2group_id(int nid) { size_t i; + for (i = 0; i < OSSL_NELEM(nid_list); i++) { if (nid_list[i].nid == nid) - return (uint16_t)(i + 1); + return nid_list[i].group_id; } return 0; } @@ -236,8 +245,8 @@ void tls1_get_supported_groups(SSL *s, const uint16_t **pgroups, default: if (s->ext.supportedgroups == NULL) { - *pgroups = eccurves_default; - *pgroupslen = OSSL_NELEM(eccurves_default); + *pgroups = supported_groups_default; + *pgroupslen = OSSL_NELEM(supported_groups_default); } else { *pgroups = s->ext.supportedgroups; *pgroupslen = s->ext.supportedgroups_len; @@ -246,6 +255,17 @@ void tls1_get_supported_groups(SSL *s, const uint16_t **pgroups, } } +int tls_valid_group(SSL *s, uint16_t group_id, int version) +{ + const TLS_GROUP_INFO *ginfo = tls1_group_id_lookup(group_id); + + if (version < TLS1_3_VERSION) { + if ((ginfo->flags & TLS_GROUP_ONLY_FOR_TLS1_3) != 0) + return 0; + } + return 1; +} + /* See if curve is allowed by security callback */ int tls_curve_allowed(SSL *s, uint16_t curve, int op) { @@ -255,7 +275,7 @@ int tls_curve_allowed(SSL *s, uint16_t curve, int op) if (cinfo == NULL) return 0; # ifdef OPENSSL_NO_EC2M - if (cinfo->flags & TLS_CURVE_CHAR2) + if (cinfo->flags & TLS_GROUP_CURVE_CHAR2) return 0; # endif ctmp[0] = curve >> 8; @@ -341,10 +361,12 @@ int tls1_set_groups(uint16_t **pext, size_t *pextlen, uint16_t *glist; size_t i; /* - * Bitmap of groups included to detect duplicates: only works while group - * ids < 32 + * Bitmap of groups included to detect duplicates: two variables are added + * to detect duplicates as some values are more than 32. */ - unsigned long dup_list = 0; + unsigned long *dup_list = NULL; + unsigned long dup_list_egrp = 0; + unsigned long dup_list_dhgrp = 0; if (ngroups == 0) { SSLerr(SSL_F_TLS1_SET_GROUPS, SSL_R_BAD_LENGTH); @@ -357,20 +379,23 @@ int tls1_set_groups(uint16_t **pext, size_t *pextlen, for (i = 0; i < ngroups; i++) { unsigned long idmask; uint16_t id; - /* TODO(TLS1.3): Convert for DH groups */ id = tls1_nid2group_id(groups[i]); - idmask = 1L << id; - if (!id || (dup_list & idmask)) { - OPENSSL_free(glist); - return 0; - } - dup_list |= idmask; + if ((id & 0x00FF) >= (sizeof(unsigned long) * 8)) + goto err; + idmask = 1L << (id & 0x00FF); + dup_list = (id < 0x100) ? &dup_list_egrp : &dup_list_dhgrp; + if (!id || ((*dup_list) & idmask)) + goto err; + *dup_list |= idmask; glist[i] = id; } OPENSSL_free(*pext); *pext = glist; *pextlen = ngroups; return 1; +err: + OPENSSL_free(glist); + return 0; } # define MAX_CURVELIST OSSL_NELEM(nid_list)