From 25d8d321ee91ee3d970af8f8fbacf8383ffb3a14 Mon Sep 17 00:00:00 2001 From: Keelan10 Date: Mon, 12 Dec 2022 14:44:30 +0400 Subject: [PATCH 1/5] Implement NTRU Prime --- kem/ntruprime/doc.go | 8 + kem/ntruprime/gen.go | 139 +++ kem/ntruprime/internal/Decode.go | 70 ++ kem/ntruprime/internal/Divmod.go | 104 ++ kem/ntruprime/internal/Encode.go | 40 + kem/ntruprime/kat_test.go | 160 +++ kem/ntruprime/ntrulpr1013/ntruprime.go | 867 ++++++++++++++++ kem/ntruprime/ntrulpr1277/ntruprime.go | 867 ++++++++++++++++ kem/ntruprime/ntrulpr653/ntruprime.go | 867 ++++++++++++++++ kem/ntruprime/ntrulpr761/ntruprime.go | 867 ++++++++++++++++ kem/ntruprime/ntrulpr857/ntruprime.go | 867 ++++++++++++++++ kem/ntruprime/ntrulpr953/ntruprime.go | 867 ++++++++++++++++ kem/ntruprime/sntrup1013/ntruprime.go | 956 +++++++++++++++++ kem/ntruprime/sntrup1277/ntruprime.go | 956 +++++++++++++++++ kem/ntruprime/sntrup653/ntruprime.go | 956 +++++++++++++++++ kem/ntruprime/sntrup761/ntruprime.go | 956 +++++++++++++++++ kem/ntruprime/sntrup857/ntruprime.go | 956 +++++++++++++++++ kem/ntruprime/sntrup953/ntruprime.go | 956 +++++++++++++++++ kem/ntruprime/templates/ntrulpr.templ.go | 872 ++++++++++++++++ kem/ntruprime/templates/sntrup.templ.go | 965 ++++++++++++++++++ kem/schemes/schemes.go | 15 + pke/ntruprime/gen.go | 115 +++ pke/ntruprime/kem/kem.go | 101 ++ pke/ntruprime/kem/schemes/sntrup/schemes.go | 49 + pke/ntruprime/ntrulpr1013/params.go | 31 + pke/ntruprime/ntrulpr1277/params.go | 31 + pke/ntruprime/ntrulpr653/params.go | 31 + pke/ntruprime/ntrulpr761/params.go | 31 + pke/ntruprime/ntrulpr857/params.go | 31 + pke/ntruprime/ntrulpr953/params.go | 31 + pke/ntruprime/sntrup1013/params.go | 25 + pke/ntruprime/sntrup1277/params.go | 25 + pke/ntruprime/sntrup653/params.go | 25 + pke/ntruprime/sntrup761/params.go | 25 + pke/ntruprime/sntrup857/params.go | 25 + pke/ntruprime/sntrup953/params.go | 25 + .../templates/ntrulpr.params.templ.go | 35 + .../templates/sntrup.params.templ.go | 28 + 38 files changed, 13975 insertions(+) create mode 100644 kem/ntruprime/doc.go create mode 100644 kem/ntruprime/gen.go create mode 100644 kem/ntruprime/internal/Decode.go create mode 100644 kem/ntruprime/internal/Divmod.go create mode 100644 kem/ntruprime/internal/Encode.go create mode 100644 kem/ntruprime/kat_test.go create mode 100644 kem/ntruprime/ntrulpr1013/ntruprime.go create mode 100644 kem/ntruprime/ntrulpr1277/ntruprime.go create mode 100644 kem/ntruprime/ntrulpr653/ntruprime.go create mode 100644 kem/ntruprime/ntrulpr761/ntruprime.go create mode 100644 kem/ntruprime/ntrulpr857/ntruprime.go create mode 100644 kem/ntruprime/ntrulpr953/ntruprime.go create mode 100644 kem/ntruprime/sntrup1013/ntruprime.go create mode 100644 kem/ntruprime/sntrup1277/ntruprime.go create mode 100644 kem/ntruprime/sntrup653/ntruprime.go create mode 100644 kem/ntruprime/sntrup761/ntruprime.go create mode 100644 kem/ntruprime/sntrup857/ntruprime.go create mode 100644 kem/ntruprime/sntrup953/ntruprime.go create mode 100644 kem/ntruprime/templates/ntrulpr.templ.go create mode 100644 kem/ntruprime/templates/sntrup.templ.go create mode 100644 pke/ntruprime/gen.go create mode 100644 pke/ntruprime/kem/kem.go create mode 100644 pke/ntruprime/kem/schemes/sntrup/schemes.go create mode 100644 pke/ntruprime/ntrulpr1013/params.go create mode 100644 pke/ntruprime/ntrulpr1277/params.go create mode 100644 pke/ntruprime/ntrulpr653/params.go create mode 100644 pke/ntruprime/ntrulpr761/params.go create mode 100644 pke/ntruprime/ntrulpr857/params.go create mode 100644 pke/ntruprime/ntrulpr953/params.go create mode 100644 pke/ntruprime/sntrup1013/params.go create mode 100644 pke/ntruprime/sntrup1277/params.go create mode 100644 pke/ntruprime/sntrup653/params.go create mode 100644 pke/ntruprime/sntrup761/params.go create mode 100644 pke/ntruprime/sntrup857/params.go create mode 100644 pke/ntruprime/sntrup953/params.go create mode 100644 pke/ntruprime/templates/ntrulpr.params.templ.go create mode 100644 pke/ntruprime/templates/sntrup.params.templ.go diff --git a/kem/ntruprime/doc.go b/kem/ntruprime/doc.go new file mode 100644 index 000000000..2eb57bbe3 --- /dev/null +++ b/kem/ntruprime/doc.go @@ -0,0 +1,8 @@ +//go:generate go run gen.go + +// Package ntruprime implements the NTRU Prime IND-CCA2 secure +// key encapsulation mechanism (KEM) as submitted to round 3 of the NIST PQC +// competition and described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package ntruprime diff --git a/kem/ntruprime/gen.go b/kem/ntruprime/gen.go new file mode 100644 index 000000000..20277ab08 --- /dev/null +++ b/kem/ntruprime/gen.go @@ -0,0 +1,139 @@ +//go:build ignore +// +build ignore + +package main + +import ( + "bytes" + "go/format" + "io/ioutil" + "strings" + "text/template" +) + +type Instance struct { + Name string + Hash string +} + +func (m Instance) Pkg() string { + return strings.ToLower(m.Name) +} + +var ( + SInstances = []Instance{ + {Name: "SNTRUP761"}, + {Name: "SNTRUP653"}, + {Name: "SNTRUP857"}, + {Name: "SNTRUP953"}, + {Name: "SNTRUP1013"}, + {Name: "SNTRUP1277"}, + } + LPRInstances = []Instance{ + {Name: "NTRULPR761"}, + {Name: "NTRULPR653"}, + {Name: "NTRULPR857"}, + {Name: "NTRULPR953"}, + {Name: "NTRULPR1013"}, + {Name: "NTRULPR1277"}, + } + TemplateWarning = "// Code generated from" +) + +func main() { + generateStreamlinedPackageFiles() + generateLPRPackageFiles() +} + +func generateStreamlinedPackageFiles() { + template, err := template.ParseFiles("templates/sntrup.templ.go") + if err != nil { + panic(err) + } + + for _, mode := range SInstances { + buf := new(bytes.Buffer) + err := template.Execute(buf, mode) + if err != nil { + panic(err) + } + + // Formating output code + code, err := format.Source(buf.Bytes()) + if err != nil { + panic("error formating code") + } + + res := string(code) + offset := strings.Index(res, TemplateWarning) + if offset == -1 { + panic("Missing template warning in pkg.templ.go") + } + err = ioutil.WriteFile(mode.Pkg()+"/ntruprime.go", []byte(res[offset:]), 0o644) + if err != nil { + panic(err) + } + } +} + +func generateLPRPackageFiles() { + template, err := template.ParseFiles("templates/ntrulpr.templ.go") + if err != nil { + panic(err) + } + + for _, mode := range LPRInstances { + buf := new(bytes.Buffer) + err := template.Execute(buf, mode) + if err != nil { + panic(err) + } + + // Formating output code + code, err := format.Source(buf.Bytes()) + if err != nil { + panic("error formating code") + } + + res := string(code) + offset := strings.Index(res, TemplateWarning) + if offset == -1 { + panic("Missing template warning in pkg.templ.go") + } + err = ioutil.WriteFile(mode.Pkg()+"/ntruprime.go", []byte(res[offset:]), 0o644) + if err != nil { + panic(err) + } + } +} + +func generateKAT() { + template, err := template.ParseFiles("templates/kat.templ.go") + if err != nil { + panic(err) + } + + for _, mode := range SInstances { + buf := new(bytes.Buffer) + err := template.Execute(buf, mode) + if err != nil { + panic(err) + } + + // Formating output code + code, err := format.Source(buf.Bytes()) + if err != nil { + panic("error formating code") + } + + res := string(code) + offset := strings.Index(res, TemplateWarning) + if offset == -1 { + panic("Missing template warning in pkg.templ.go") + } + err = ioutil.WriteFile(mode.Pkg()+"/kat_test.go", []byte(res[offset:]), 0o644) + if err != nil { + panic(err) + } + } +} diff --git a/kem/ntruprime/internal/Decode.go b/kem/ntruprime/internal/Decode.go new file mode 100644 index 000000000..38721af9d --- /dev/null +++ b/kem/ntruprime/internal/Decode.go @@ -0,0 +1,70 @@ +package internal + +/* Decode(R,s,M,len) */ +/* assumes 0 < M[i] < 16384 */ +/* produces 0 <= R[i] < M[i] */ +func Decode(out []uint16, S []uint8, M []uint16, len int) { + index := 0 + if len == 1 { + if M[0] == 1 { + out[index] = 0 + } else if M[0] <= 256 { + out[index] = Uint32_mod_uint14(uint32(S[0]), M[0]) + } else { + out[index] = Uint32_mod_uint14(uint32(uint16(S[0])+((uint16(S[1]))<<8)), M[0]) + } + } + if len > 1 { + R2 := make([]uint16, (len+1)/2) + M2 := make([]uint16, (len+1)/2) + bottomr := make([]uint16, len/2) + bottomt := make([]uint32, len/2) + i := 0 + for i = 0; i < len-1; i += 2 { + m := uint32(M[i]) * uint32(M[i+1]) + + if m > 256*16383 { + bottomt[i/2] = 256 * 256 + bottomr[i/2] = uint16(S[0]) + 256*uint16(S[1]) + S = S[2:] + M2[i/2] = uint16((((m + 255) >> 8) + 255) >> 8) + } else if m >= 16384 { + bottomt[i/2] = 256 + bottomr[i/2] = uint16(S[0]) + S = S[1:] + M2[i/2] = uint16((m + 255) >> 8) + + } else { + bottomt[i/2] = 1 + bottomr[i/2] = 0 + M2[i/2] = uint16(m) + } + } + if i < len { + M2[i/2] = M[i] + } + + Decode(R2, S, M2, (len+1)/2) + + for i = 0; i < len-1; i += 2 { + var r uint32 = uint32(bottomr[i/2]) + var r1 uint32 + var r0 uint16 + + r += bottomt[i/2] * uint32(R2[i/2]) + Uint32_divmod_uint14(&r1, &r0, r, M[i]) + r1 = uint32(Uint32_mod_uint14(r1, M[i+1])) /* only needed for invalid inputs */ + + out[index] = r0 + index++ + out[index] = uint16(r1) + index++ + + } + if i < len { + out[index] = R2[i/2] + index++ + } + + } +} diff --git a/kem/ntruprime/internal/Divmod.go b/kem/ntruprime/internal/Divmod.go new file mode 100644 index 000000000..c0622c417 --- /dev/null +++ b/kem/ntruprime/internal/Divmod.go @@ -0,0 +1,104 @@ +package internal + +/* +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. +*/ +// q, r = x/m +// Returns quotient and remainder +func Uint32_divmod_uint14(q *uint32, r *uint16, x uint32, m uint16) { + var v uint32 = 0x80000000 + + v /= uint32(m) + + *q = 0 + + var qpart uint32 = uint32(uint64(x) * uint64(v) >> 31) + + x -= qpart * uint32(m) + *q += qpart + + qpart = uint32(uint64(x) * uint64(v) >> 31) + x -= qpart * uint32(m) + *q += qpart + + x -= uint32(m) + *q += 1 + var mask uint32 = -(x >> 31) + x += mask & uint32(m) + *q += mask + + *r = uint16(x) + +} + +// Returns the quotient of x/m +func Uint32_div_uint14(x uint32, m uint16) uint32 { + var q uint32 + var r uint16 + Uint32_divmod_uint14(&q, &r, x, m) + return q +} + +// Returns the remainder of x/m +func Uint32_mod_uint14(x uint32, m uint16) uint16 { + var q uint32 + var r uint16 + Uint32_divmod_uint14(&q, &r, x, m) + return r +} + +// Calculates quotient and remainder +func Int32_divmod_uint14(q *int32, r *uint16, x int32, m uint16) { + var uq, uq2 uint32 + var ur, ur2 uint16 + var mask uint32 + + 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 += uint16(mask & uint32(m)) + uq += mask + *r = ur + *q = int32(uq) +} + +// Returns quotient of x/m +func Int32_div_uint14(x int32, m uint16) int32 { + var q int32 + var r uint16 + Int32_divmod_uint14(&q, &r, x, m) + return q +} + +// Returns remainder of x/m +func Int32_mod_uint14(x int32, m uint16) uint16 { + var q int32 + var r uint16 + Int32_divmod_uint14(&q, &r, x, m) + return r + +} + +// Returns -1 if x!=0; else return 0 +func Int16_nonzero_mask(x int16) int { + var u uint16 = uint16(x) /* 0, else 1...65535 */ + var v uint32 = uint32(u) /* 0, else 1...65535 */ + v = -v /* 0, else 2^32-65535...2^32-1 */ + v >>= 31 /* 0, else 1 */ + return -int(v) /* 0, else -1 */ +} + +// Returns -1 if x<0; otherwise return 0 +func Int16_negative_mask(x int16) int { + var u uint16 = uint16(x) + u >>= 15 + return -(int)(u) +} diff --git a/kem/ntruprime/internal/Encode.go b/kem/ntruprime/internal/Encode.go new file mode 100644 index 000000000..68257455e --- /dev/null +++ b/kem/ntruprime/internal/Encode.go @@ -0,0 +1,40 @@ +package internal + +/* 0 <= R[i] < M[i] < 16384 */ +func Encode(out []uint8, R []uint16, M []uint16, len int) { + if len == 1 { + r := R[0] + m := M[0] + for m > 1 { + out[0] = uint8(r) + out = out[1:] + r >>= 8 + m = (m + 255) >> 8 + } + } + if len > 1 { + R2 := make([]uint16, (len+1)/2) + M2 := make([]uint16, (len+1)/2) + var i int = 0 + + for i = 0; i < len-1; i += 2 { + m0 := uint32(M[i]) + r := uint32(R[i]) + uint32(R[i+1])*m0 + m := uint32(M[i+1]) * m0 + for m >= 16384 { + out[0] = uint8(r) + out = out[1:] + + r >>= 8 + m = (m + 255) >> 8 + } + R2[i/2] = uint16(r) + M2[i/2] = uint16(m) + } + if i < len { + R2[i/2] = R[i] + M2[i/2] = M[i] + } + Encode(out, R2, M2, (len+1)/2) + } +} diff --git a/kem/ntruprime/kat_test.go b/kem/ntruprime/kat_test.go new file mode 100644 index 000000000..bee29fe9b --- /dev/null +++ b/kem/ntruprime/kat_test.go @@ -0,0 +1,160 @@ +package ntruprime + +import ( + "bytes" + "crypto/sha512" + "fmt" + "testing" + + "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/kem/schemes" + sntrupSchemes "github.com/cloudflare/circl/pke/ntruprime/kem/schemes/sntrup" +) + +func TestPQCgenStreamlinedKATKem(t *testing.T) { + kats := []struct { + name string + want string + }{ + // Computed from reference implementation + {"sntrup653", "82249a46c1bc538e980a2335764c81f70701e6374eed3e1d0457e18c57ec2cee64280dcc75504c2648eb3e37ab3eee37955c1114d851f755a28cc997aba781c8"}, + {"sntrup761", "1a687f42261c47fe4421b35c5d9faf035433fcb2101458680c66c8d54caafec5fb767ea7725d6681ab100912ef06c38d88862a5d2d86786af2989b7dad33813a"}, + {"sntrup857", "79473d6c709dbbc99528886bf2c1d033c409dab1755299154f33232bc57ba1fbe91322fcb741df5252d575a77aa5ca000d52a44c17f1ab64a299884d0f101519"}, + {"sntrup953", "6fe0cf3b8cb62a3011c1870ec9eb3cd8825c06993a213e01ecd0f21f5dee670838fe1c89dd120086a09e8227496a00e22188c8f947618a35764c5a24726ce16c"}, + {"sntrup1013", "195a38eb843fdda53241f65b641ab925f61fb1cf5b0fffcb5891115da121a85174a796d69c75b86c4e92193453155aef9d27ce53aa268076617be55ee6f5da4f"}, + {"sntrup1277", "ada8a0cbe6b077dc563874fd372f60779bbee1524f576c2931cf9c804163b9632163610d6e380f889170cdf4d9928de0782368a43413f2b6976897ba0e19a828"}, + } + + for _, kat := range kats { + kat := kat + t.Run(kat.name, func(t *testing.T) { + testPQCgenStreamlinedKATKem(t, kat.name, kat.want) + }) + } +} + +func TestPQCgenLPRKATKem(t *testing.T) { + kats := []struct { + name string + want string + p int + }{ + // Computed from reference implementation + {"ntrulpr653", "30b750e9bcf5a14d0dc10a1a4f0ff4269f7ff7a5b8b835fe7d50d45de3653bbb33c3943fc50759175ba7fef92fd601ac705d7658d3f15a8a7610973ef098e849", 653}, + {"ntrulpr761", "35f9b8191aef509766019015b7af11dd2afaadf7fca827a9b0a80f7318b7e8325345c64d5b5562ee321465378102850297fbbd70fe78c5bd711e382015189e5a", 761}, + {"ntrulpr857", "919c675a5b1f642d97b866a284c633f52ad309a1f24a5713fa2f7839a84d07091b2c5a80841ce73a2090cc0ce707d9f262772f730d15905ab238a7be1c1e1e3e", 857}, + {"ntrulpr953", "2ae003933ca87d873956969977b3d7b5133e42df0868a0cbb77067cf9144ce18b0e4342ba850e2f4d46257aaea23f1e290448e3a34e6774f9594230343de7038", 953}, + {"ntrulpr1013", "5c054bab923095d3dc4250e5e71923c98b7e3bc778aa4a2a4235b8751106eac2cf0e41dc413d1b6fc7bdc8301a46ca206b19b6301c554cf643d473a55a5940a1", 1013}, + {"ntrulpr1277", "1ec1702ff090324385fdd98a7f1c1adfe80503593e3531c2c3ed7547df47da38fcdd8dedf142d2b426b3f98015b5c8fe3688b41808c513bdada66a15b7f727ab", 1277}, + } + + for _, kat := range kats { + kat := kat + t.Run(kat.name, func(t *testing.T) { + testPQCgenLPRKATKem(t, kat.name, kat.want, kat.p) + }) + } +} + +func testPQCgenLPRKATKem(t *testing.T, name, expected string, p int) { + scheme := schemes.ByName(name) + if scheme == nil { + t.Fatal() + } + + var seed [48]byte + kseed := make([]byte, scheme.SeedSize()) + eseed := make([]byte, scheme.EncapsulationSeedSize()) + seedBytes := 32 + + for i := 0; i < 48; i++ { + seed[i] = byte(i) + } + + g1 := nist.NewDRBG(&seed) + + f := sha512.New() + + fmt.Fprintf(f, "# kem/%s\n\n", name) + + for i := 0; i < 100; i++ { + g1.Fill(seed[:]) + + fmt.Fprintf(f, "count = %d\n", i) + fmt.Fprintf(f, "seed = %X\n", seed) + + g2 := nist.NewDRBG(&seed) + + g2.Fill(kseed[:seedBytes]) + for i := 0; i < p; i++ { + g2.Fill(kseed[seedBytes+i*4 : seedBytes+i*4+4]) + } + g2.Fill(kseed[seedBytes+p*4:]) + + pk, sk := scheme.DeriveKeyPair(kseed) + ppk, _ := pk.MarshalBinary() + psk, _ := sk.MarshalBinary() + + g2.Fill(eseed) + ct, ss1, err := scheme.EncapsulateDeterministically(pk, eseed) + if err != nil { + t.Fatal(err) + } + ss2, _ := scheme.Decapsulate(sk, ct) + + if !bytes.Equal(ss1[:], ss2[:]) { + t.Fatal() + } + fmt.Fprintf(f, "pk = %X\n", ppk) + fmt.Fprintf(f, "sk = %X\n", psk) + fmt.Fprintf(f, "ct = %X\n", ct) + fmt.Fprintf(f, "ss = %X\n\n", ss1) + } + if fmt.Sprintf("%x", f.Sum(nil)) != expected { + t.Fatal() + } +} + +func testPQCgenStreamlinedKATKem(t *testing.T, name, expected string) { + scheme := sntrupSchemes.ByName(name) + if scheme == nil { + t.Fatal() + } + + var seed [48]byte + + for i := 0; i < 48; i++ { + seed[i] = byte(i) + } + + g1 := nist.NewDRBG(&seed) + f := sha512.New() + + fmt.Fprintf(f, "# kem/%s\n\n", name) + for i := 0; i < 100; i++ { + g1.Fill(seed[:]) + + fmt.Fprintf(f, "count = %d\n", i) + fmt.Fprintf(f, "seed = %X\n", seed) + + g2 := nist.NewDRBG(&seed) + + pk, sk := scheme.DeriveKeyPairFromGen(&g2) + ppk, _ := pk.MarshalBinary() + psk, _ := sk.MarshalBinary() + + ct, ss1, _ := scheme.EncapsulateDeterministicallyFromGen(pk, &g2) + ss2, _ := scheme.Decapsulate(sk, ct) + if !bytes.Equal(ss1, ss2) { + t.Fatal() + } + fmt.Fprintf(f, "pk = %X\n", ppk) + fmt.Fprintf(f, "sk = %X\n", psk) + fmt.Fprintf(f, "ct = %X\n", ct) + fmt.Fprintf(f, "ss = %X\n\n", ss1) + + } + if fmt.Sprintf("%x", f.Sum(nil)) != expected { + t.Fatal() + } +} diff --git a/kem/ntruprime/ntrulpr1013/ntruprime.go b/kem/ntruprime/ntrulpr1013/ntruprime.go new file mode 100644 index 000000000..229c608ff --- /dev/null +++ b/kem/ntruprime/ntrulpr1013/ntruprime.go @@ -0,0 +1,867 @@ +// Code generated from ntrulpr.templ.go. DO NOT EDIT. + +// Package ntrulpr1013 implements the IND-CCA2 secure key encapsulation mechanism +// ntrulpr1013 as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package ntrulpr1013 + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + cryptoRand "crypto/rand" + "crypto/sha512" + + "github.com/cloudflare/circl/kem" + "github.com/cloudflare/circl/kem/ntruprime/internal" + ntrup "github.com/cloudflare/circl/pke/ntruprime/ntrulpr1013" +) + +const ( + p = ntrup.P + q = ntrup.Q + q12 = ((q - 1) / 2) + roundedBytes = ntrup.RoundedBytes + + w = ntrup.W + tau0 = ntrup.Tau0 + tau1 = ntrup.Tau1 + tau2 = ntrup.Tau2 + tau3 = ntrup.Tau3 + + I = ntrup.I + + hashBytes = 32 + + smallBytes = ((p + 3) / 4) + + inputsBytes = I / 8 + seedBytes = 32 + ciphertextsBytes = roundedBytes + topBytes + secretKeysBytes = smallBytes + publicKeysBytes = seedBytes + roundedBytes + + confirmBytes = 32 + + tau = 16 + topBytes = I / 2 +) + +const ( + // Size of seed for NewKeyFromSeed + KeySeedSize = seedBytes + p*4 + inputsBytes + + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = inputsBytes + + // Size of the established shared key. + SharedKeySize = ntrup.SharedKeySize + + // Size of the encapsulated shared key. + CiphertextSize = ntrup.CiphertextSize + + // Size of a packed public key. + PublicKeySize = ntrup.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = ntrup.PrivateKeySize +) + +type ( + small int8 + Fq int16 +) + +// arithmetic operations over GF(3) + +// A polynomial of R has all of its coefficients in (-1,0,1) +// F3 is always represented as -1,0,1 +// so ZZ_fromF3 is a no-op + +// x must not be close to top int16 +func f3Freeze(x int16) small { + return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 +} + +/* ----- arithmetic mod q */ +// GF (q) +// type Fq int16 + +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +func fqFreeze(x int32) Fq { + return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) +} + +func top(C Fq) int8 { + return int8((tau1*(int32)(C+tau0) + 16384) >> 15) +} + +func right(T int8) Fq { + return fqFreeze(tau3*int32(T) - tau2) +} + +// Polynomials mod q + +// h = f*g in the ring Rq */ +func rqMultSmall(f []Fq, g []small) (h []Fq) { + h = make([]Fq, p) + fg := make([]Fq, p+p-1) + var result Fq + + for i := 0; i < p; i++ { + result = 0 + for j := 0; j <= i; j++ { + result = fqFreeze(int32(result) + int32(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 = fqFreeze(int32(result) + int32(f[j])*(int32)(g[i-j])) + } + fg[i] = result + } + + for i := p + p - 2; i >= p; i-- { + fg[i-p] = fqFreeze(int32(fg[i-p] + fg[i])) + fg[i-p+1] = fqFreeze(int32(fg[i-p+1] + fg[i])) + + } + + for i := 0; i < p; i++ { + h[i] = fg[i] + } + return h +} + +// Rounding all coefficients of a polynomial to the nearest multiple of 3 +// Rounded polynomials mod q +func round(a []Fq) []Fq { + out := make([]Fq, p) + for i := 0; i < p; i++ { + out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) + } + return out +} + +// Returns (min(x, y), max(x, y)), executes in constant time +func minmax(x, y *uint32) { + var xi uint32 = *x + var yi uint32 = *y + var xy uint32 = xi ^ yi + var c uint32 = yi - xi + c ^= xy & (c ^ yi ^ 0x80000000) + c >>= 31 + c = -c + c &= xy + *x = xi ^ c + *y = yi ^ c +} + +// Sorts the array of unsigned integers +func cryptoSortUint32(x []uint32, n int) { + if n < 2 { + return + } + top := 1 + + for top < n-top { + top += top + } + + for p := top; p > 0; p >>= 1 { + for i := 0; i < n-p; i++ { + if i&p == 0 { + minmax(&x[i], &x[i+p]) + } + } + for q := top; q > p; q >>= 1 { + for i := 0; i < n-q; i++ { + if i&p == 0 { + minmax(&x[i+p], &x[i+q]) + } + } + } + } +} + +// Sorting to generate short polynomial +func shortFromList(in []int32) []small { + out := make([]small, p) + L := make([]uint32, p) + + var neg2, neg3 int = -2, -3 + + for i := 0; i < w; i++ { + L[i] = uint32(in[i]) & uint32((neg2)) + } + + for i := w; i < p; i++ { + L[i] = (uint32(in[i]) & uint32((neg3))) | 1 + } + + cryptoSortUint32(L, p) + + for i := 0; i < p; i++ { + out[i] = small((L[i] & 3) - 1) + } + return out +} + +// Underlying hash function + +// The input byte array, in, is prepended by the byte b +// and its SHA-512 hash is calculated +// Only the first 32 bytes of the hash are returned +// e.g., b = 0 means out = Hash0(in) +func hashPrefix(out []byte, b int, in []byte, inlen int) { + x := make([]byte, inlen+1) + h := make([]byte, 64) + + x[0] = byte(b) + copy(x[1:], in) + + hash := sha512.New() + hash.Write([]byte(x)) + h = hash.Sum(nil) + + copy(out, h[:32]) + +} + +// Higher level randomness +// Returns a random unsigned integer +// generator can be passed for deterministic number generation +func urandom32(seed []byte) uint32 { + + c := make([]byte, 4) + var out [4]uint32 + + if seed != nil { + copy(c, seed) + } else { + cryptoRand.Read(c) + + } + + 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] +} + +// Generates a random short polynomial +func shortRandom(seed []byte) []small { + + L := make([]uint32, p) + + if seed != nil { + for i := 0; i < p; i++ { + L[i] = urandom32(seed[i*4 : i*4+4]) + } + } else { + for i := 0; i < p; i++ { + L[i] = urandom32(nil) + } + } + + // Converts uint32 array to int32 array + L_int32 := make([]int32, p) + for i := 0; i < len(L); i++ { + L_int32[i] = int32(L[i]) + } + out := shortFromList(L_int32) + + return out + +} + +// NTRU LPRime Core + +// (G,A),a = keyGen(G); leaves G unchanged +func keyGen(A []Fq, a []small, G []Fq, seed []byte) { + + copy(a, shortRandom(seed)) + aG := rqMultSmall(G, a) + copy(A, round(aG)) +} + +// B,T = encrypt(r,(G,A),b) +func encrypt(r []int8, G []Fq, A []Fq, b []small) ([]Fq, []int8) { + T := make([]int8, I) + bG := rqMultSmall(G, b) + B := round(bG) + bA := rqMultSmall(A, b) + + for i := 0; i < I; i++ { + T[i] = top(fqFreeze(int32(bA[i]) + int32(r[i])*q12)) + } + + return B, T +} + +// r = decrypt((B,T),a) +func decrypt(B []Fq, T []int8, a []small) []int8 { + + r := make([]int8, I) + + aB := rqMultSmall(B, a) + + for i := 0; i < I; i++ { + r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) + } + + return r +} + +// Encoding I-bit inputs +type Inputs [I]int8 + +func inputsEncode(s []byte, r Inputs) { + + for i := 0; i < I; i++ { + s[i>>3] |= byte(r[i] << (i & 7)) + } + +} + +// Expand + +func expand(L []uint32, k []byte) { + temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) + aes_nonce := make([]byte, aes.BlockSize) // IV + + block, err := aes.NewCipher(k[:32]) + if err != nil { + panic(err) + } + + ciphertext := make([]byte, len(temp)) // same length as plaintext + + stream := cipher.NewCTR(block, aes_nonce) + stream.XORKeyStream(ciphertext, temp) + + // convert byte to uint32 + for i := 0; i < len(temp); i++ { + L[i] = uint32(ciphertext[i]) + } + + for i := 0; i < p; i++ { + var L0 uint32 = L[4*i] + var L1 uint32 = L[4*i+1] + var L2 uint32 = L[4*i+2] + var L3 uint32 = L[4*i+3] + L[i] = L0 + (L1 << 8) + (L2 << 16) + (L3 << 24) + } +} + +// generator, hashShort +// G = generator(k) +func generator(k []byte) (G []Fq) { + G = make([]Fq, p) + L := make([]uint32, 4*p) + expand(L, k) + for i := 0; i < p; i++ { + G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) + } + return G +} + +// out = hashShort(r) +func hashShort(r Inputs) (out []small) { + s := make([]byte, inputsBytes) + inputsEncode(s, r) + h := make([]byte, hashBytes) + L := make([]uint32, 4*p) + L_int32 := make([]int32, p) + + hashPrefix(h, 5, s, len(s)) + + expand(L, h) + + // convert []uint32 to []int32 + for i := 0; i < p; i++ { + L_int32[i] = int32(L[i]) + } + out = shortFromList(L_int32) + return out +} + +// NTRU LPRime expand + +// (S,A),a = xKeyGen() +func xKeyGen(S []byte, A []Fq, a []small, seed []byte) { + + if seed == nil { + cryptoRand.Read(S) + } else { + copy(S, seed[:seedBytes]) + seed = seed[seedBytes:] + } + + G := generator(S) + + keyGen(A, a, G, seed) +} + +// B,T = xEncrypt(r,(S,A)) +func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { + G := generator(S) + + // convert []int8 to Inputs + var r_inputs Inputs + for i := 0; i < len(r); i++ { + r_inputs[i] = r[i] + } + + b := hashShort(r_inputs) + + return encrypt(r, G, A, b) +} + +// Encoding small polynomials (including short polynomials) + +// Transform polynomial in R to bytes +// these are the only functions that rely on p mod 4 = 1 */ +func smallEncode(s []byte, f []small) { + var x small + var index int = 0 + for i := 0; i < p/4; i++ { + x = f[index] + 1 + index++ + + x += (f[index] + 1) << 2 + index++ + x += (f[index] + 1) << 4 + index++ + x += (f[index] + 1) << 6 + index++ + + s[0] = byte(x) + s = s[1:] + } + x = f[index] + 1 + + s[0] = byte(x) +} + +// Transform bytes into polynomial in R +func smallDecode(f []small, s []byte) { + var index int = 0 + var x byte + + for i := 0; i < p/4; i++ { + x = s[0] + s = s[1:] + + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + index++ + } + x = s[0] + f[index] = ((small)(x & 3)) - 1 +} + +// Encoding rounded polynomials + +// Transform rounded polynomials to bytes +func roundedEncode(s []byte, r []Fq) { + + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16((int32((r[i])+q12) * 10923) >> 15) + M[i] = (q + 2) / 3 + } + internal.Encode(s, R, M, p) +} + +// Transform bytes to rounded polynomials +func roundedDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = (q + 2) / 3 + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = Fq(R[i]*3 - q12) + } + +} + +// Encoding top polynomials + +func topEncode(T []int8) (s []byte) { + s = make([]byte, topBytes) + for i := 0; i < topBytes; i++ { + s[i] = byte(T[2*i] + (T[2*i+1] << 4)) + + } + return s +} + +func topDecode(s []byte) (T []int8) { + + T = make([]int8, 2*topBytes+1) + + for i := 0; i < topBytes; i++ { + T[2*i] = int8(s[i] & 15) + T[2*i+1] = int8(s[i] >> 4) + } + return T +} + +// Streamlined NTRU Prime Core plus encoding + +func inputsRandom(seed []byte) (r Inputs) { + + s := make([]byte, inputsBytes) + + if seed != nil { + copy(s, seed) + } else { + cryptoRand.Read(s) + } + + for i := 0; i < I; i++ { + r[i] = int8(1 & (s[i>>3] >> (i & 7))) + } + + return r +} + +// Generates public key and private key +// pk,sk = zKeyGen() +func zKeyGen(pk []byte, sk []byte, seed []byte) { + A := make([]Fq, p) + a := make([]small, p) + + xKeyGen(pk, A, a, seed) + + pk = pk[seedBytes:] + roundedEncode(pk, A) + + smallEncode(sk, a) +} + +// c = zEncrypt(r,pk) +func zEncrypt(c []byte, r Inputs, pk []byte) { + A := make([]Fq, p) + + roundedDecode(A, pk[seedBytes:]) + B, T := xEncrypt(r[:], pk[:seedBytes], A) + roundedEncode(c, B) + c = c[roundedBytes:] + + copy(c, topEncode(T)) +} + +// r = zDecrypt(C,sk) +func zDecrypt(r *Inputs, c []byte, sk []byte) { + a := make([]small, p) + B := make([]Fq, p) + + smallDecode(a, sk) + roundedDecode(B, c) + T := topDecode(c[roundedBytes:]) + copy(r[:], decrypt(B, T, a)) +} + +// Confirmation hash + +// h = hashConfirm(r,pk,cache); cache is Hash4(pk) +func hashConfirm(h []byte, r []byte, pk []byte, cache []byte) { + x := make([]byte, inputsBytes+hashBytes) + + copy(x, r) + copy(x[inputsBytes:], cache) + + hashPrefix(h, 2, x, len(x)) + +} + +// Session-key hash + +// k = hashSession(b,y,z) +func hashSession(k []byte, b int, y []byte, z []byte) { + x := make([]byte, inputsBytes+ciphertextsBytes+confirmBytes) + copy(x[:inputsBytes], y) + copy(x[inputsBytes:], z) + + hashPrefix(k, b, x, len(x)) +} + +// Streamlined NTRU Prime + +// pk,sk = kemKeyGen() +func kemKeyGen(pk []byte, sk []byte, seed []byte) { + + if seed == nil { + seed = make([]byte, KeySeedSize) + cryptoRand.Read(seed) + } + + if len(seed) != KeySeedSize { + panic("seed must be of length KeySeedSize") + } + + zKeyGen(pk, sk, seed[:seedBytes+p*4]) + seed = seed[seedBytes+p*4:] + + sk = sk[secretKeysBytes:] + + copy(sk, pk) + sk = sk[publicKeysBytes:] + + if seed != nil { + copy(sk[:inputsBytes], seed) + + } else { + cryptoRand.Read(sk[:inputsBytes]) + } + sk = sk[inputsBytes:] + hashPrefix(sk, 4, pk, publicKeysBytes) + +} + +// c,r_enc = hide(r,pk,cache); cache is Hash4(pk) +func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { + inputsEncode(r_enc, r) + + zEncrypt(c, r, pk) + c = c[ciphertextsBytes:] + hashConfirm(c, r_enc, pk, cache) + +} + +// Takes as input a public key +// Returns ciphertext and shared key +// c,k = encap(pk) +func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { + + if seed != nil && len(seed) != EncapsulationSeedSize { + panic("seed must be of length EncapsulationSeedSize") + } + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + r_enc := make([]byte, inputsBytes) + cache := make([]byte, hashBytes) + + hashPrefix(cache, 4, pk.pk[:], publicKeysBytes) + r := inputsRandom(seed) + hide(c, r_enc, r, pk.pk[:], cache) + hashSession(k, 1, r_enc, c) +} + +// Returns 0 if matching ciphertext+confirm, else -1 +func ciphertextsDiffMask(c []byte, c2 []byte) int { + var differentbits uint16 = 0 + var len int = ciphertextsBytes + confirmBytes + + for i := 0; i < len; i++ { + differentbits |= uint16((c[i]) ^ (c2[i])) + } + return int((1 & ((differentbits - 1) >> 8)) - 1) + +} + +// Returns shared key from ciphertext and private key +// k = decap(c,sk) +func (priv *PrivateKey) DecapsulateTo(ss []byte, ct []byte) { + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(ss) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + sk := priv.sk[:] + pk := sk[secretKeysBytes:] + rho := pk[publicKeysBytes:] + cache := rho[inputsBytes:] + var r Inputs + + r_enc := make([]byte, inputsBytes) + cnew := make([]byte, ciphertextsBytes+confirmBytes) + + zDecrypt(&r, ct, sk) + hide(cnew, r_enc, r, pk, cache) + var mask int = ciphertextsDiffMask(ct, cnew) + + for i := 0; i < inputsBytes; i++ { + r_enc[i] ^= byte(mask & int(r_enc[i]^rho[i])) + } + hashSession(ss, 1+mask, r_enc, ct) +} + +// The structure of the private key is given by the following segments: +// The secret key, the public key, entropy and the hash of the public key +type PrivateKey struct { + sk [PrivateKeySize]byte +} + +type PublicKey struct { + pk [PublicKeySize]byte +} + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "ntrulpr1013" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + copy(ret[:], sk.sk[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + return bytes.Equal(sk.sk[:], oth.sk[:]) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + return bytes.Equal(pk.pk[:], oth.pk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + var pk [PublicKeySize]byte + skey, _ := sk.MarshalBinary() + ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] + copy(pk[:], ppk[:]) + return &PublicKey{pk: pk} +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + copy(ret[:], pk.pk[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + kemKeyGen(pk[:], sk[:], nil) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk}, nil + +} + +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + + kemKeyGen(pk[:], sk[:], seed) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk} +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, nil) + return ct, ss, nil + +} + +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) (ct, ss []byte, err error) { + + if len(seed) != EncapsulationSeedSize { + return nil, nil, kem.ErrSeedSize + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, seed) + return ct, ss, nil +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + ssk, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + ss := [SharedKeySize]byte{} + + ssk.DecapsulateTo(ss[:], ct) + + return ss[:], nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + pk := [PublicKeySize]byte{} + copy(pk[:], buf) + return &PublicKey{pk: pk}, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + sk := [PrivateKeySize]byte{} + copy(sk[:], buf) + return &PrivateKey{sk: sk}, nil +} diff --git a/kem/ntruprime/ntrulpr1277/ntruprime.go b/kem/ntruprime/ntrulpr1277/ntruprime.go new file mode 100644 index 000000000..8c983e926 --- /dev/null +++ b/kem/ntruprime/ntrulpr1277/ntruprime.go @@ -0,0 +1,867 @@ +// Code generated from ntrulpr.templ.go. DO NOT EDIT. + +// Package ntrulpr1277 implements the IND-CCA2 secure key encapsulation mechanism +// ntrulpr1277 as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package ntrulpr1277 + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + cryptoRand "crypto/rand" + "crypto/sha512" + + "github.com/cloudflare/circl/kem" + "github.com/cloudflare/circl/kem/ntruprime/internal" + ntrup "github.com/cloudflare/circl/pke/ntruprime/ntrulpr1277" +) + +const ( + p = ntrup.P + q = ntrup.Q + q12 = ((q - 1) / 2) + roundedBytes = ntrup.RoundedBytes + + w = ntrup.W + tau0 = ntrup.Tau0 + tau1 = ntrup.Tau1 + tau2 = ntrup.Tau2 + tau3 = ntrup.Tau3 + + I = ntrup.I + + hashBytes = 32 + + smallBytes = ((p + 3) / 4) + + inputsBytes = I / 8 + seedBytes = 32 + ciphertextsBytes = roundedBytes + topBytes + secretKeysBytes = smallBytes + publicKeysBytes = seedBytes + roundedBytes + + confirmBytes = 32 + + tau = 16 + topBytes = I / 2 +) + +const ( + // Size of seed for NewKeyFromSeed + KeySeedSize = seedBytes + p*4 + inputsBytes + + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = inputsBytes + + // Size of the established shared key. + SharedKeySize = ntrup.SharedKeySize + + // Size of the encapsulated shared key. + CiphertextSize = ntrup.CiphertextSize + + // Size of a packed public key. + PublicKeySize = ntrup.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = ntrup.PrivateKeySize +) + +type ( + small int8 + Fq int16 +) + +// arithmetic operations over GF(3) + +// A polynomial of R has all of its coefficients in (-1,0,1) +// F3 is always represented as -1,0,1 +// so ZZ_fromF3 is a no-op + +// x must not be close to top int16 +func f3Freeze(x int16) small { + return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 +} + +/* ----- arithmetic mod q */ +// GF (q) +// type Fq int16 + +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +func fqFreeze(x int32) Fq { + return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) +} + +func top(C Fq) int8 { + return int8((tau1*(int32)(C+tau0) + 16384) >> 15) +} + +func right(T int8) Fq { + return fqFreeze(tau3*int32(T) - tau2) +} + +// Polynomials mod q + +// h = f*g in the ring Rq */ +func rqMultSmall(f []Fq, g []small) (h []Fq) { + h = make([]Fq, p) + fg := make([]Fq, p+p-1) + var result Fq + + for i := 0; i < p; i++ { + result = 0 + for j := 0; j <= i; j++ { + result = fqFreeze(int32(result) + int32(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 = fqFreeze(int32(result) + int32(f[j])*(int32)(g[i-j])) + } + fg[i] = result + } + + for i := p + p - 2; i >= p; i-- { + fg[i-p] = fqFreeze(int32(fg[i-p] + fg[i])) + fg[i-p+1] = fqFreeze(int32(fg[i-p+1] + fg[i])) + + } + + for i := 0; i < p; i++ { + h[i] = fg[i] + } + return h +} + +// Rounding all coefficients of a polynomial to the nearest multiple of 3 +// Rounded polynomials mod q +func round(a []Fq) []Fq { + out := make([]Fq, p) + for i := 0; i < p; i++ { + out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) + } + return out +} + +// Returns (min(x, y), max(x, y)), executes in constant time +func minmax(x, y *uint32) { + var xi uint32 = *x + var yi uint32 = *y + var xy uint32 = xi ^ yi + var c uint32 = yi - xi + c ^= xy & (c ^ yi ^ 0x80000000) + c >>= 31 + c = -c + c &= xy + *x = xi ^ c + *y = yi ^ c +} + +// Sorts the array of unsigned integers +func cryptoSortUint32(x []uint32, n int) { + if n < 2 { + return + } + top := 1 + + for top < n-top { + top += top + } + + for p := top; p > 0; p >>= 1 { + for i := 0; i < n-p; i++ { + if i&p == 0 { + minmax(&x[i], &x[i+p]) + } + } + for q := top; q > p; q >>= 1 { + for i := 0; i < n-q; i++ { + if i&p == 0 { + minmax(&x[i+p], &x[i+q]) + } + } + } + } +} + +// Sorting to generate short polynomial +func shortFromList(in []int32) []small { + out := make([]small, p) + L := make([]uint32, p) + + var neg2, neg3 int = -2, -3 + + for i := 0; i < w; i++ { + L[i] = uint32(in[i]) & uint32((neg2)) + } + + for i := w; i < p; i++ { + L[i] = (uint32(in[i]) & uint32((neg3))) | 1 + } + + cryptoSortUint32(L, p) + + for i := 0; i < p; i++ { + out[i] = small((L[i] & 3) - 1) + } + return out +} + +// Underlying hash function + +// The input byte array, in, is prepended by the byte b +// and its SHA-512 hash is calculated +// Only the first 32 bytes of the hash are returned +// e.g., b = 0 means out = Hash0(in) +func hashPrefix(out []byte, b int, in []byte, inlen int) { + x := make([]byte, inlen+1) + h := make([]byte, 64) + + x[0] = byte(b) + copy(x[1:], in) + + hash := sha512.New() + hash.Write([]byte(x)) + h = hash.Sum(nil) + + copy(out, h[:32]) + +} + +// Higher level randomness +// Returns a random unsigned integer +// generator can be passed for deterministic number generation +func urandom32(seed []byte) uint32 { + + c := make([]byte, 4) + var out [4]uint32 + + if seed != nil { + copy(c, seed) + } else { + cryptoRand.Read(c) + + } + + 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] +} + +// Generates a random short polynomial +func shortRandom(seed []byte) []small { + + L := make([]uint32, p) + + if seed != nil { + for i := 0; i < p; i++ { + L[i] = urandom32(seed[i*4 : i*4+4]) + } + } else { + for i := 0; i < p; i++ { + L[i] = urandom32(nil) + } + } + + // Converts uint32 array to int32 array + L_int32 := make([]int32, p) + for i := 0; i < len(L); i++ { + L_int32[i] = int32(L[i]) + } + out := shortFromList(L_int32) + + return out + +} + +// NTRU LPRime Core + +// (G,A),a = keyGen(G); leaves G unchanged +func keyGen(A []Fq, a []small, G []Fq, seed []byte) { + + copy(a, shortRandom(seed)) + aG := rqMultSmall(G, a) + copy(A, round(aG)) +} + +// B,T = encrypt(r,(G,A),b) +func encrypt(r []int8, G []Fq, A []Fq, b []small) ([]Fq, []int8) { + T := make([]int8, I) + bG := rqMultSmall(G, b) + B := round(bG) + bA := rqMultSmall(A, b) + + for i := 0; i < I; i++ { + T[i] = top(fqFreeze(int32(bA[i]) + int32(r[i])*q12)) + } + + return B, T +} + +// r = decrypt((B,T),a) +func decrypt(B []Fq, T []int8, a []small) []int8 { + + r := make([]int8, I) + + aB := rqMultSmall(B, a) + + for i := 0; i < I; i++ { + r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) + } + + return r +} + +// Encoding I-bit inputs +type Inputs [I]int8 + +func inputsEncode(s []byte, r Inputs) { + + for i := 0; i < I; i++ { + s[i>>3] |= byte(r[i] << (i & 7)) + } + +} + +// Expand + +func expand(L []uint32, k []byte) { + temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) + aes_nonce := make([]byte, aes.BlockSize) // IV + + block, err := aes.NewCipher(k[:32]) + if err != nil { + panic(err) + } + + ciphertext := make([]byte, len(temp)) // same length as plaintext + + stream := cipher.NewCTR(block, aes_nonce) + stream.XORKeyStream(ciphertext, temp) + + // convert byte to uint32 + for i := 0; i < len(temp); i++ { + L[i] = uint32(ciphertext[i]) + } + + for i := 0; i < p; i++ { + var L0 uint32 = L[4*i] + var L1 uint32 = L[4*i+1] + var L2 uint32 = L[4*i+2] + var L3 uint32 = L[4*i+3] + L[i] = L0 + (L1 << 8) + (L2 << 16) + (L3 << 24) + } +} + +// generator, hashShort +// G = generator(k) +func generator(k []byte) (G []Fq) { + G = make([]Fq, p) + L := make([]uint32, 4*p) + expand(L, k) + for i := 0; i < p; i++ { + G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) + } + return G +} + +// out = hashShort(r) +func hashShort(r Inputs) (out []small) { + s := make([]byte, inputsBytes) + inputsEncode(s, r) + h := make([]byte, hashBytes) + L := make([]uint32, 4*p) + L_int32 := make([]int32, p) + + hashPrefix(h, 5, s, len(s)) + + expand(L, h) + + // convert []uint32 to []int32 + for i := 0; i < p; i++ { + L_int32[i] = int32(L[i]) + } + out = shortFromList(L_int32) + return out +} + +// NTRU LPRime expand + +// (S,A),a = xKeyGen() +func xKeyGen(S []byte, A []Fq, a []small, seed []byte) { + + if seed == nil { + cryptoRand.Read(S) + } else { + copy(S, seed[:seedBytes]) + seed = seed[seedBytes:] + } + + G := generator(S) + + keyGen(A, a, G, seed) +} + +// B,T = xEncrypt(r,(S,A)) +func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { + G := generator(S) + + // convert []int8 to Inputs + var r_inputs Inputs + for i := 0; i < len(r); i++ { + r_inputs[i] = r[i] + } + + b := hashShort(r_inputs) + + return encrypt(r, G, A, b) +} + +// Encoding small polynomials (including short polynomials) + +// Transform polynomial in R to bytes +// these are the only functions that rely on p mod 4 = 1 */ +func smallEncode(s []byte, f []small) { + var x small + var index int = 0 + for i := 0; i < p/4; i++ { + x = f[index] + 1 + index++ + + x += (f[index] + 1) << 2 + index++ + x += (f[index] + 1) << 4 + index++ + x += (f[index] + 1) << 6 + index++ + + s[0] = byte(x) + s = s[1:] + } + x = f[index] + 1 + + s[0] = byte(x) +} + +// Transform bytes into polynomial in R +func smallDecode(f []small, s []byte) { + var index int = 0 + var x byte + + for i := 0; i < p/4; i++ { + x = s[0] + s = s[1:] + + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + index++ + } + x = s[0] + f[index] = ((small)(x & 3)) - 1 +} + +// Encoding rounded polynomials + +// Transform rounded polynomials to bytes +func roundedEncode(s []byte, r []Fq) { + + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16((int32((r[i])+q12) * 10923) >> 15) + M[i] = (q + 2) / 3 + } + internal.Encode(s, R, M, p) +} + +// Transform bytes to rounded polynomials +func roundedDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = (q + 2) / 3 + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = Fq(R[i]*3 - q12) + } + +} + +// Encoding top polynomials + +func topEncode(T []int8) (s []byte) { + s = make([]byte, topBytes) + for i := 0; i < topBytes; i++ { + s[i] = byte(T[2*i] + (T[2*i+1] << 4)) + + } + return s +} + +func topDecode(s []byte) (T []int8) { + + T = make([]int8, 2*topBytes+1) + + for i := 0; i < topBytes; i++ { + T[2*i] = int8(s[i] & 15) + T[2*i+1] = int8(s[i] >> 4) + } + return T +} + +// Streamlined NTRU Prime Core plus encoding + +func inputsRandom(seed []byte) (r Inputs) { + + s := make([]byte, inputsBytes) + + if seed != nil { + copy(s, seed) + } else { + cryptoRand.Read(s) + } + + for i := 0; i < I; i++ { + r[i] = int8(1 & (s[i>>3] >> (i & 7))) + } + + return r +} + +// Generates public key and private key +// pk,sk = zKeyGen() +func zKeyGen(pk []byte, sk []byte, seed []byte) { + A := make([]Fq, p) + a := make([]small, p) + + xKeyGen(pk, A, a, seed) + + pk = pk[seedBytes:] + roundedEncode(pk, A) + + smallEncode(sk, a) +} + +// c = zEncrypt(r,pk) +func zEncrypt(c []byte, r Inputs, pk []byte) { + A := make([]Fq, p) + + roundedDecode(A, pk[seedBytes:]) + B, T := xEncrypt(r[:], pk[:seedBytes], A) + roundedEncode(c, B) + c = c[roundedBytes:] + + copy(c, topEncode(T)) +} + +// r = zDecrypt(C,sk) +func zDecrypt(r *Inputs, c []byte, sk []byte) { + a := make([]small, p) + B := make([]Fq, p) + + smallDecode(a, sk) + roundedDecode(B, c) + T := topDecode(c[roundedBytes:]) + copy(r[:], decrypt(B, T, a)) +} + +// Confirmation hash + +// h = hashConfirm(r,pk,cache); cache is Hash4(pk) +func hashConfirm(h []byte, r []byte, pk []byte, cache []byte) { + x := make([]byte, inputsBytes+hashBytes) + + copy(x, r) + copy(x[inputsBytes:], cache) + + hashPrefix(h, 2, x, len(x)) + +} + +// Session-key hash + +// k = hashSession(b,y,z) +func hashSession(k []byte, b int, y []byte, z []byte) { + x := make([]byte, inputsBytes+ciphertextsBytes+confirmBytes) + copy(x[:inputsBytes], y) + copy(x[inputsBytes:], z) + + hashPrefix(k, b, x, len(x)) +} + +// Streamlined NTRU Prime + +// pk,sk = kemKeyGen() +func kemKeyGen(pk []byte, sk []byte, seed []byte) { + + if seed == nil { + seed = make([]byte, KeySeedSize) + cryptoRand.Read(seed) + } + + if len(seed) != KeySeedSize { + panic("seed must be of length KeySeedSize") + } + + zKeyGen(pk, sk, seed[:seedBytes+p*4]) + seed = seed[seedBytes+p*4:] + + sk = sk[secretKeysBytes:] + + copy(sk, pk) + sk = sk[publicKeysBytes:] + + if seed != nil { + copy(sk[:inputsBytes], seed) + + } else { + cryptoRand.Read(sk[:inputsBytes]) + } + sk = sk[inputsBytes:] + hashPrefix(sk, 4, pk, publicKeysBytes) + +} + +// c,r_enc = hide(r,pk,cache); cache is Hash4(pk) +func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { + inputsEncode(r_enc, r) + + zEncrypt(c, r, pk) + c = c[ciphertextsBytes:] + hashConfirm(c, r_enc, pk, cache) + +} + +// Takes as input a public key +// Returns ciphertext and shared key +// c,k = encap(pk) +func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { + + if seed != nil && len(seed) != EncapsulationSeedSize { + panic("seed must be of length EncapsulationSeedSize") + } + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + r_enc := make([]byte, inputsBytes) + cache := make([]byte, hashBytes) + + hashPrefix(cache, 4, pk.pk[:], publicKeysBytes) + r := inputsRandom(seed) + hide(c, r_enc, r, pk.pk[:], cache) + hashSession(k, 1, r_enc, c) +} + +// Returns 0 if matching ciphertext+confirm, else -1 +func ciphertextsDiffMask(c []byte, c2 []byte) int { + var differentbits uint16 = 0 + var len int = ciphertextsBytes + confirmBytes + + for i := 0; i < len; i++ { + differentbits |= uint16((c[i]) ^ (c2[i])) + } + return int((1 & ((differentbits - 1) >> 8)) - 1) + +} + +// Returns shared key from ciphertext and private key +// k = decap(c,sk) +func (priv *PrivateKey) DecapsulateTo(ss []byte, ct []byte) { + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(ss) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + sk := priv.sk[:] + pk := sk[secretKeysBytes:] + rho := pk[publicKeysBytes:] + cache := rho[inputsBytes:] + var r Inputs + + r_enc := make([]byte, inputsBytes) + cnew := make([]byte, ciphertextsBytes+confirmBytes) + + zDecrypt(&r, ct, sk) + hide(cnew, r_enc, r, pk, cache) + var mask int = ciphertextsDiffMask(ct, cnew) + + for i := 0; i < inputsBytes; i++ { + r_enc[i] ^= byte(mask & int(r_enc[i]^rho[i])) + } + hashSession(ss, 1+mask, r_enc, ct) +} + +// The structure of the private key is given by the following segments: +// The secret key, the public key, entropy and the hash of the public key +type PrivateKey struct { + sk [PrivateKeySize]byte +} + +type PublicKey struct { + pk [PublicKeySize]byte +} + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "ntrulpr1277" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + copy(ret[:], sk.sk[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + return bytes.Equal(sk.sk[:], oth.sk[:]) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + return bytes.Equal(pk.pk[:], oth.pk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + var pk [PublicKeySize]byte + skey, _ := sk.MarshalBinary() + ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] + copy(pk[:], ppk[:]) + return &PublicKey{pk: pk} +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + copy(ret[:], pk.pk[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + kemKeyGen(pk[:], sk[:], nil) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk}, nil + +} + +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + + kemKeyGen(pk[:], sk[:], seed) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk} +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, nil) + return ct, ss, nil + +} + +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) (ct, ss []byte, err error) { + + if len(seed) != EncapsulationSeedSize { + return nil, nil, kem.ErrSeedSize + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, seed) + return ct, ss, nil +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + ssk, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + ss := [SharedKeySize]byte{} + + ssk.DecapsulateTo(ss[:], ct) + + return ss[:], nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + pk := [PublicKeySize]byte{} + copy(pk[:], buf) + return &PublicKey{pk: pk}, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + sk := [PrivateKeySize]byte{} + copy(sk[:], buf) + return &PrivateKey{sk: sk}, nil +} diff --git a/kem/ntruprime/ntrulpr653/ntruprime.go b/kem/ntruprime/ntrulpr653/ntruprime.go new file mode 100644 index 000000000..2c02d3df9 --- /dev/null +++ b/kem/ntruprime/ntrulpr653/ntruprime.go @@ -0,0 +1,867 @@ +// Code generated from ntrulpr.templ.go. DO NOT EDIT. + +// Package ntrulpr653 implements the IND-CCA2 secure key encapsulation mechanism +// ntrulpr653 as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package ntrulpr653 + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + cryptoRand "crypto/rand" + "crypto/sha512" + + "github.com/cloudflare/circl/kem" + "github.com/cloudflare/circl/kem/ntruprime/internal" + ntrup "github.com/cloudflare/circl/pke/ntruprime/ntrulpr653" +) + +const ( + p = ntrup.P + q = ntrup.Q + q12 = ((q - 1) / 2) + roundedBytes = ntrup.RoundedBytes + + w = ntrup.W + tau0 = ntrup.Tau0 + tau1 = ntrup.Tau1 + tau2 = ntrup.Tau2 + tau3 = ntrup.Tau3 + + I = ntrup.I + + hashBytes = 32 + + smallBytes = ((p + 3) / 4) + + inputsBytes = I / 8 + seedBytes = 32 + ciphertextsBytes = roundedBytes + topBytes + secretKeysBytes = smallBytes + publicKeysBytes = seedBytes + roundedBytes + + confirmBytes = 32 + + tau = 16 + topBytes = I / 2 +) + +const ( + // Size of seed for NewKeyFromSeed + KeySeedSize = seedBytes + p*4 + inputsBytes + + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = inputsBytes + + // Size of the established shared key. + SharedKeySize = ntrup.SharedKeySize + + // Size of the encapsulated shared key. + CiphertextSize = ntrup.CiphertextSize + + // Size of a packed public key. + PublicKeySize = ntrup.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = ntrup.PrivateKeySize +) + +type ( + small int8 + Fq int16 +) + +// arithmetic operations over GF(3) + +// A polynomial of R has all of its coefficients in (-1,0,1) +// F3 is always represented as -1,0,1 +// so ZZ_fromF3 is a no-op + +// x must not be close to top int16 +func f3Freeze(x int16) small { + return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 +} + +/* ----- arithmetic mod q */ +// GF (q) +// type Fq int16 + +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +func fqFreeze(x int32) Fq { + return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) +} + +func top(C Fq) int8 { + return int8((tau1*(int32)(C+tau0) + 16384) >> 15) +} + +func right(T int8) Fq { + return fqFreeze(tau3*int32(T) - tau2) +} + +// Polynomials mod q + +// h = f*g in the ring Rq */ +func rqMultSmall(f []Fq, g []small) (h []Fq) { + h = make([]Fq, p) + fg := make([]Fq, p+p-1) + var result Fq + + for i := 0; i < p; i++ { + result = 0 + for j := 0; j <= i; j++ { + result = fqFreeze(int32(result) + int32(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 = fqFreeze(int32(result) + int32(f[j])*(int32)(g[i-j])) + } + fg[i] = result + } + + for i := p + p - 2; i >= p; i-- { + fg[i-p] = fqFreeze(int32(fg[i-p] + fg[i])) + fg[i-p+1] = fqFreeze(int32(fg[i-p+1] + fg[i])) + + } + + for i := 0; i < p; i++ { + h[i] = fg[i] + } + return h +} + +// Rounding all coefficients of a polynomial to the nearest multiple of 3 +// Rounded polynomials mod q +func round(a []Fq) []Fq { + out := make([]Fq, p) + for i := 0; i < p; i++ { + out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) + } + return out +} + +// Returns (min(x, y), max(x, y)), executes in constant time +func minmax(x, y *uint32) { + var xi uint32 = *x + var yi uint32 = *y + var xy uint32 = xi ^ yi + var c uint32 = yi - xi + c ^= xy & (c ^ yi ^ 0x80000000) + c >>= 31 + c = -c + c &= xy + *x = xi ^ c + *y = yi ^ c +} + +// Sorts the array of unsigned integers +func cryptoSortUint32(x []uint32, n int) { + if n < 2 { + return + } + top := 1 + + for top < n-top { + top += top + } + + for p := top; p > 0; p >>= 1 { + for i := 0; i < n-p; i++ { + if i&p == 0 { + minmax(&x[i], &x[i+p]) + } + } + for q := top; q > p; q >>= 1 { + for i := 0; i < n-q; i++ { + if i&p == 0 { + minmax(&x[i+p], &x[i+q]) + } + } + } + } +} + +// Sorting to generate short polynomial +func shortFromList(in []int32) []small { + out := make([]small, p) + L := make([]uint32, p) + + var neg2, neg3 int = -2, -3 + + for i := 0; i < w; i++ { + L[i] = uint32(in[i]) & uint32((neg2)) + } + + for i := w; i < p; i++ { + L[i] = (uint32(in[i]) & uint32((neg3))) | 1 + } + + cryptoSortUint32(L, p) + + for i := 0; i < p; i++ { + out[i] = small((L[i] & 3) - 1) + } + return out +} + +// Underlying hash function + +// The input byte array, in, is prepended by the byte b +// and its SHA-512 hash is calculated +// Only the first 32 bytes of the hash are returned +// e.g., b = 0 means out = Hash0(in) +func hashPrefix(out []byte, b int, in []byte, inlen int) { + x := make([]byte, inlen+1) + h := make([]byte, 64) + + x[0] = byte(b) + copy(x[1:], in) + + hash := sha512.New() + hash.Write([]byte(x)) + h = hash.Sum(nil) + + copy(out, h[:32]) + +} + +// Higher level randomness +// Returns a random unsigned integer +// generator can be passed for deterministic number generation +func urandom32(seed []byte) uint32 { + + c := make([]byte, 4) + var out [4]uint32 + + if seed != nil { + copy(c, seed) + } else { + cryptoRand.Read(c) + + } + + 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] +} + +// Generates a random short polynomial +func shortRandom(seed []byte) []small { + + L := make([]uint32, p) + + if seed != nil { + for i := 0; i < p; i++ { + L[i] = urandom32(seed[i*4 : i*4+4]) + } + } else { + for i := 0; i < p; i++ { + L[i] = urandom32(nil) + } + } + + // Converts uint32 array to int32 array + L_int32 := make([]int32, p) + for i := 0; i < len(L); i++ { + L_int32[i] = int32(L[i]) + } + out := shortFromList(L_int32) + + return out + +} + +// NTRU LPRime Core + +// (G,A),a = keyGen(G); leaves G unchanged +func keyGen(A []Fq, a []small, G []Fq, seed []byte) { + + copy(a, shortRandom(seed)) + aG := rqMultSmall(G, a) + copy(A, round(aG)) +} + +// B,T = encrypt(r,(G,A),b) +func encrypt(r []int8, G []Fq, A []Fq, b []small) ([]Fq, []int8) { + T := make([]int8, I) + bG := rqMultSmall(G, b) + B := round(bG) + bA := rqMultSmall(A, b) + + for i := 0; i < I; i++ { + T[i] = top(fqFreeze(int32(bA[i]) + int32(r[i])*q12)) + } + + return B, T +} + +// r = decrypt((B,T),a) +func decrypt(B []Fq, T []int8, a []small) []int8 { + + r := make([]int8, I) + + aB := rqMultSmall(B, a) + + for i := 0; i < I; i++ { + r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) + } + + return r +} + +// Encoding I-bit inputs +type Inputs [I]int8 + +func inputsEncode(s []byte, r Inputs) { + + for i := 0; i < I; i++ { + s[i>>3] |= byte(r[i] << (i & 7)) + } + +} + +// Expand + +func expand(L []uint32, k []byte) { + temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) + aes_nonce := make([]byte, aes.BlockSize) // IV + + block, err := aes.NewCipher(k[:32]) + if err != nil { + panic(err) + } + + ciphertext := make([]byte, len(temp)) // same length as plaintext + + stream := cipher.NewCTR(block, aes_nonce) + stream.XORKeyStream(ciphertext, temp) + + // convert byte to uint32 + for i := 0; i < len(temp); i++ { + L[i] = uint32(ciphertext[i]) + } + + for i := 0; i < p; i++ { + var L0 uint32 = L[4*i] + var L1 uint32 = L[4*i+1] + var L2 uint32 = L[4*i+2] + var L3 uint32 = L[4*i+3] + L[i] = L0 + (L1 << 8) + (L2 << 16) + (L3 << 24) + } +} + +// generator, hashShort +// G = generator(k) +func generator(k []byte) (G []Fq) { + G = make([]Fq, p) + L := make([]uint32, 4*p) + expand(L, k) + for i := 0; i < p; i++ { + G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) + } + return G +} + +// out = hashShort(r) +func hashShort(r Inputs) (out []small) { + s := make([]byte, inputsBytes) + inputsEncode(s, r) + h := make([]byte, hashBytes) + L := make([]uint32, 4*p) + L_int32 := make([]int32, p) + + hashPrefix(h, 5, s, len(s)) + + expand(L, h) + + // convert []uint32 to []int32 + for i := 0; i < p; i++ { + L_int32[i] = int32(L[i]) + } + out = shortFromList(L_int32) + return out +} + +// NTRU LPRime expand + +// (S,A),a = xKeyGen() +func xKeyGen(S []byte, A []Fq, a []small, seed []byte) { + + if seed == nil { + cryptoRand.Read(S) + } else { + copy(S, seed[:seedBytes]) + seed = seed[seedBytes:] + } + + G := generator(S) + + keyGen(A, a, G, seed) +} + +// B,T = xEncrypt(r,(S,A)) +func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { + G := generator(S) + + // convert []int8 to Inputs + var r_inputs Inputs + for i := 0; i < len(r); i++ { + r_inputs[i] = r[i] + } + + b := hashShort(r_inputs) + + return encrypt(r, G, A, b) +} + +// Encoding small polynomials (including short polynomials) + +// Transform polynomial in R to bytes +// these are the only functions that rely on p mod 4 = 1 */ +func smallEncode(s []byte, f []small) { + var x small + var index int = 0 + for i := 0; i < p/4; i++ { + x = f[index] + 1 + index++ + + x += (f[index] + 1) << 2 + index++ + x += (f[index] + 1) << 4 + index++ + x += (f[index] + 1) << 6 + index++ + + s[0] = byte(x) + s = s[1:] + } + x = f[index] + 1 + + s[0] = byte(x) +} + +// Transform bytes into polynomial in R +func smallDecode(f []small, s []byte) { + var index int = 0 + var x byte + + for i := 0; i < p/4; i++ { + x = s[0] + s = s[1:] + + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + index++ + } + x = s[0] + f[index] = ((small)(x & 3)) - 1 +} + +// Encoding rounded polynomials + +// Transform rounded polynomials to bytes +func roundedEncode(s []byte, r []Fq) { + + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16((int32((r[i])+q12) * 10923) >> 15) + M[i] = (q + 2) / 3 + } + internal.Encode(s, R, M, p) +} + +// Transform bytes to rounded polynomials +func roundedDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = (q + 2) / 3 + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = Fq(R[i]*3 - q12) + } + +} + +// Encoding top polynomials + +func topEncode(T []int8) (s []byte) { + s = make([]byte, topBytes) + for i := 0; i < topBytes; i++ { + s[i] = byte(T[2*i] + (T[2*i+1] << 4)) + + } + return s +} + +func topDecode(s []byte) (T []int8) { + + T = make([]int8, 2*topBytes+1) + + for i := 0; i < topBytes; i++ { + T[2*i] = int8(s[i] & 15) + T[2*i+1] = int8(s[i] >> 4) + } + return T +} + +// Streamlined NTRU Prime Core plus encoding + +func inputsRandom(seed []byte) (r Inputs) { + + s := make([]byte, inputsBytes) + + if seed != nil { + copy(s, seed) + } else { + cryptoRand.Read(s) + } + + for i := 0; i < I; i++ { + r[i] = int8(1 & (s[i>>3] >> (i & 7))) + } + + return r +} + +// Generates public key and private key +// pk,sk = zKeyGen() +func zKeyGen(pk []byte, sk []byte, seed []byte) { + A := make([]Fq, p) + a := make([]small, p) + + xKeyGen(pk, A, a, seed) + + pk = pk[seedBytes:] + roundedEncode(pk, A) + + smallEncode(sk, a) +} + +// c = zEncrypt(r,pk) +func zEncrypt(c []byte, r Inputs, pk []byte) { + A := make([]Fq, p) + + roundedDecode(A, pk[seedBytes:]) + B, T := xEncrypt(r[:], pk[:seedBytes], A) + roundedEncode(c, B) + c = c[roundedBytes:] + + copy(c, topEncode(T)) +} + +// r = zDecrypt(C,sk) +func zDecrypt(r *Inputs, c []byte, sk []byte) { + a := make([]small, p) + B := make([]Fq, p) + + smallDecode(a, sk) + roundedDecode(B, c) + T := topDecode(c[roundedBytes:]) + copy(r[:], decrypt(B, T, a)) +} + +// Confirmation hash + +// h = hashConfirm(r,pk,cache); cache is Hash4(pk) +func hashConfirm(h []byte, r []byte, pk []byte, cache []byte) { + x := make([]byte, inputsBytes+hashBytes) + + copy(x, r) + copy(x[inputsBytes:], cache) + + hashPrefix(h, 2, x, len(x)) + +} + +// Session-key hash + +// k = hashSession(b,y,z) +func hashSession(k []byte, b int, y []byte, z []byte) { + x := make([]byte, inputsBytes+ciphertextsBytes+confirmBytes) + copy(x[:inputsBytes], y) + copy(x[inputsBytes:], z) + + hashPrefix(k, b, x, len(x)) +} + +// Streamlined NTRU Prime + +// pk,sk = kemKeyGen() +func kemKeyGen(pk []byte, sk []byte, seed []byte) { + + if seed == nil { + seed = make([]byte, KeySeedSize) + cryptoRand.Read(seed) + } + + if len(seed) != KeySeedSize { + panic("seed must be of length KeySeedSize") + } + + zKeyGen(pk, sk, seed[:seedBytes+p*4]) + seed = seed[seedBytes+p*4:] + + sk = sk[secretKeysBytes:] + + copy(sk, pk) + sk = sk[publicKeysBytes:] + + if seed != nil { + copy(sk[:inputsBytes], seed) + + } else { + cryptoRand.Read(sk[:inputsBytes]) + } + sk = sk[inputsBytes:] + hashPrefix(sk, 4, pk, publicKeysBytes) + +} + +// c,r_enc = hide(r,pk,cache); cache is Hash4(pk) +func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { + inputsEncode(r_enc, r) + + zEncrypt(c, r, pk) + c = c[ciphertextsBytes:] + hashConfirm(c, r_enc, pk, cache) + +} + +// Takes as input a public key +// Returns ciphertext and shared key +// c,k = encap(pk) +func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { + + if seed != nil && len(seed) != EncapsulationSeedSize { + panic("seed must be of length EncapsulationSeedSize") + } + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + r_enc := make([]byte, inputsBytes) + cache := make([]byte, hashBytes) + + hashPrefix(cache, 4, pk.pk[:], publicKeysBytes) + r := inputsRandom(seed) + hide(c, r_enc, r, pk.pk[:], cache) + hashSession(k, 1, r_enc, c) +} + +// Returns 0 if matching ciphertext+confirm, else -1 +func ciphertextsDiffMask(c []byte, c2 []byte) int { + var differentbits uint16 = 0 + var len int = ciphertextsBytes + confirmBytes + + for i := 0; i < len; i++ { + differentbits |= uint16((c[i]) ^ (c2[i])) + } + return int((1 & ((differentbits - 1) >> 8)) - 1) + +} + +// Returns shared key from ciphertext and private key +// k = decap(c,sk) +func (priv *PrivateKey) DecapsulateTo(ss []byte, ct []byte) { + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(ss) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + sk := priv.sk[:] + pk := sk[secretKeysBytes:] + rho := pk[publicKeysBytes:] + cache := rho[inputsBytes:] + var r Inputs + + r_enc := make([]byte, inputsBytes) + cnew := make([]byte, ciphertextsBytes+confirmBytes) + + zDecrypt(&r, ct, sk) + hide(cnew, r_enc, r, pk, cache) + var mask int = ciphertextsDiffMask(ct, cnew) + + for i := 0; i < inputsBytes; i++ { + r_enc[i] ^= byte(mask & int(r_enc[i]^rho[i])) + } + hashSession(ss, 1+mask, r_enc, ct) +} + +// The structure of the private key is given by the following segments: +// The secret key, the public key, entropy and the hash of the public key +type PrivateKey struct { + sk [PrivateKeySize]byte +} + +type PublicKey struct { + pk [PublicKeySize]byte +} + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "ntrulpr653" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + copy(ret[:], sk.sk[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + return bytes.Equal(sk.sk[:], oth.sk[:]) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + return bytes.Equal(pk.pk[:], oth.pk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + var pk [PublicKeySize]byte + skey, _ := sk.MarshalBinary() + ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] + copy(pk[:], ppk[:]) + return &PublicKey{pk: pk} +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + copy(ret[:], pk.pk[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + kemKeyGen(pk[:], sk[:], nil) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk}, nil + +} + +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + + kemKeyGen(pk[:], sk[:], seed) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk} +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, nil) + return ct, ss, nil + +} + +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) (ct, ss []byte, err error) { + + if len(seed) != EncapsulationSeedSize { + return nil, nil, kem.ErrSeedSize + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, seed) + return ct, ss, nil +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + ssk, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + ss := [SharedKeySize]byte{} + + ssk.DecapsulateTo(ss[:], ct) + + return ss[:], nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + pk := [PublicKeySize]byte{} + copy(pk[:], buf) + return &PublicKey{pk: pk}, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + sk := [PrivateKeySize]byte{} + copy(sk[:], buf) + return &PrivateKey{sk: sk}, nil +} diff --git a/kem/ntruprime/ntrulpr761/ntruprime.go b/kem/ntruprime/ntrulpr761/ntruprime.go new file mode 100644 index 000000000..d1c0968d5 --- /dev/null +++ b/kem/ntruprime/ntrulpr761/ntruprime.go @@ -0,0 +1,867 @@ +// Code generated from ntrulpr.templ.go. DO NOT EDIT. + +// Package ntrulpr761 implements the IND-CCA2 secure key encapsulation mechanism +// ntrulpr761 as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package ntrulpr761 + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + cryptoRand "crypto/rand" + "crypto/sha512" + + "github.com/cloudflare/circl/kem" + "github.com/cloudflare/circl/kem/ntruprime/internal" + ntrup "github.com/cloudflare/circl/pke/ntruprime/ntrulpr761" +) + +const ( + p = ntrup.P + q = ntrup.Q + q12 = ((q - 1) / 2) + roundedBytes = ntrup.RoundedBytes + + w = ntrup.W + tau0 = ntrup.Tau0 + tau1 = ntrup.Tau1 + tau2 = ntrup.Tau2 + tau3 = ntrup.Tau3 + + I = ntrup.I + + hashBytes = 32 + + smallBytes = ((p + 3) / 4) + + inputsBytes = I / 8 + seedBytes = 32 + ciphertextsBytes = roundedBytes + topBytes + secretKeysBytes = smallBytes + publicKeysBytes = seedBytes + roundedBytes + + confirmBytes = 32 + + tau = 16 + topBytes = I / 2 +) + +const ( + // Size of seed for NewKeyFromSeed + KeySeedSize = seedBytes + p*4 + inputsBytes + + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = inputsBytes + + // Size of the established shared key. + SharedKeySize = ntrup.SharedKeySize + + // Size of the encapsulated shared key. + CiphertextSize = ntrup.CiphertextSize + + // Size of a packed public key. + PublicKeySize = ntrup.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = ntrup.PrivateKeySize +) + +type ( + small int8 + Fq int16 +) + +// arithmetic operations over GF(3) + +// A polynomial of R has all of its coefficients in (-1,0,1) +// F3 is always represented as -1,0,1 +// so ZZ_fromF3 is a no-op + +// x must not be close to top int16 +func f3Freeze(x int16) small { + return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 +} + +/* ----- arithmetic mod q */ +// GF (q) +// type Fq int16 + +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +func fqFreeze(x int32) Fq { + return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) +} + +func top(C Fq) int8 { + return int8((tau1*(int32)(C+tau0) + 16384) >> 15) +} + +func right(T int8) Fq { + return fqFreeze(tau3*int32(T) - tau2) +} + +// Polynomials mod q + +// h = f*g in the ring Rq */ +func rqMultSmall(f []Fq, g []small) (h []Fq) { + h = make([]Fq, p) + fg := make([]Fq, p+p-1) + var result Fq + + for i := 0; i < p; i++ { + result = 0 + for j := 0; j <= i; j++ { + result = fqFreeze(int32(result) + int32(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 = fqFreeze(int32(result) + int32(f[j])*(int32)(g[i-j])) + } + fg[i] = result + } + + for i := p + p - 2; i >= p; i-- { + fg[i-p] = fqFreeze(int32(fg[i-p] + fg[i])) + fg[i-p+1] = fqFreeze(int32(fg[i-p+1] + fg[i])) + + } + + for i := 0; i < p; i++ { + h[i] = fg[i] + } + return h +} + +// Rounding all coefficients of a polynomial to the nearest multiple of 3 +// Rounded polynomials mod q +func round(a []Fq) []Fq { + out := make([]Fq, p) + for i := 0; i < p; i++ { + out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) + } + return out +} + +// Returns (min(x, y), max(x, y)), executes in constant time +func minmax(x, y *uint32) { + var xi uint32 = *x + var yi uint32 = *y + var xy uint32 = xi ^ yi + var c uint32 = yi - xi + c ^= xy & (c ^ yi ^ 0x80000000) + c >>= 31 + c = -c + c &= xy + *x = xi ^ c + *y = yi ^ c +} + +// Sorts the array of unsigned integers +func cryptoSortUint32(x []uint32, n int) { + if n < 2 { + return + } + top := 1 + + for top < n-top { + top += top + } + + for p := top; p > 0; p >>= 1 { + for i := 0; i < n-p; i++ { + if i&p == 0 { + minmax(&x[i], &x[i+p]) + } + } + for q := top; q > p; q >>= 1 { + for i := 0; i < n-q; i++ { + if i&p == 0 { + minmax(&x[i+p], &x[i+q]) + } + } + } + } +} + +// Sorting to generate short polynomial +func shortFromList(in []int32) []small { + out := make([]small, p) + L := make([]uint32, p) + + var neg2, neg3 int = -2, -3 + + for i := 0; i < w; i++ { + L[i] = uint32(in[i]) & uint32((neg2)) + } + + for i := w; i < p; i++ { + L[i] = (uint32(in[i]) & uint32((neg3))) | 1 + } + + cryptoSortUint32(L, p) + + for i := 0; i < p; i++ { + out[i] = small((L[i] & 3) - 1) + } + return out +} + +// Underlying hash function + +// The input byte array, in, is prepended by the byte b +// and its SHA-512 hash is calculated +// Only the first 32 bytes of the hash are returned +// e.g., b = 0 means out = Hash0(in) +func hashPrefix(out []byte, b int, in []byte, inlen int) { + x := make([]byte, inlen+1) + h := make([]byte, 64) + + x[0] = byte(b) + copy(x[1:], in) + + hash := sha512.New() + hash.Write([]byte(x)) + h = hash.Sum(nil) + + copy(out, h[:32]) + +} + +// Higher level randomness +// Returns a random unsigned integer +// generator can be passed for deterministic number generation +func urandom32(seed []byte) uint32 { + + c := make([]byte, 4) + var out [4]uint32 + + if seed != nil { + copy(c, seed) + } else { + cryptoRand.Read(c) + + } + + 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] +} + +// Generates a random short polynomial +func shortRandom(seed []byte) []small { + + L := make([]uint32, p) + + if seed != nil { + for i := 0; i < p; i++ { + L[i] = urandom32(seed[i*4 : i*4+4]) + } + } else { + for i := 0; i < p; i++ { + L[i] = urandom32(nil) + } + } + + // Converts uint32 array to int32 array + L_int32 := make([]int32, p) + for i := 0; i < len(L); i++ { + L_int32[i] = int32(L[i]) + } + out := shortFromList(L_int32) + + return out + +} + +// NTRU LPRime Core + +// (G,A),a = keyGen(G); leaves G unchanged +func keyGen(A []Fq, a []small, G []Fq, seed []byte) { + + copy(a, shortRandom(seed)) + aG := rqMultSmall(G, a) + copy(A, round(aG)) +} + +// B,T = encrypt(r,(G,A),b) +func encrypt(r []int8, G []Fq, A []Fq, b []small) ([]Fq, []int8) { + T := make([]int8, I) + bG := rqMultSmall(G, b) + B := round(bG) + bA := rqMultSmall(A, b) + + for i := 0; i < I; i++ { + T[i] = top(fqFreeze(int32(bA[i]) + int32(r[i])*q12)) + } + + return B, T +} + +// r = decrypt((B,T),a) +func decrypt(B []Fq, T []int8, a []small) []int8 { + + r := make([]int8, I) + + aB := rqMultSmall(B, a) + + for i := 0; i < I; i++ { + r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) + } + + return r +} + +// Encoding I-bit inputs +type Inputs [I]int8 + +func inputsEncode(s []byte, r Inputs) { + + for i := 0; i < I; i++ { + s[i>>3] |= byte(r[i] << (i & 7)) + } + +} + +// Expand + +func expand(L []uint32, k []byte) { + temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) + aes_nonce := make([]byte, aes.BlockSize) // IV + + block, err := aes.NewCipher(k[:32]) + if err != nil { + panic(err) + } + + ciphertext := make([]byte, len(temp)) // same length as plaintext + + stream := cipher.NewCTR(block, aes_nonce) + stream.XORKeyStream(ciphertext, temp) + + // convert byte to uint32 + for i := 0; i < len(temp); i++ { + L[i] = uint32(ciphertext[i]) + } + + for i := 0; i < p; i++ { + var L0 uint32 = L[4*i] + var L1 uint32 = L[4*i+1] + var L2 uint32 = L[4*i+2] + var L3 uint32 = L[4*i+3] + L[i] = L0 + (L1 << 8) + (L2 << 16) + (L3 << 24) + } +} + +// generator, hashShort +// G = generator(k) +func generator(k []byte) (G []Fq) { + G = make([]Fq, p) + L := make([]uint32, 4*p) + expand(L, k) + for i := 0; i < p; i++ { + G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) + } + return G +} + +// out = hashShort(r) +func hashShort(r Inputs) (out []small) { + s := make([]byte, inputsBytes) + inputsEncode(s, r) + h := make([]byte, hashBytes) + L := make([]uint32, 4*p) + L_int32 := make([]int32, p) + + hashPrefix(h, 5, s, len(s)) + + expand(L, h) + + // convert []uint32 to []int32 + for i := 0; i < p; i++ { + L_int32[i] = int32(L[i]) + } + out = shortFromList(L_int32) + return out +} + +// NTRU LPRime expand + +// (S,A),a = xKeyGen() +func xKeyGen(S []byte, A []Fq, a []small, seed []byte) { + + if seed == nil { + cryptoRand.Read(S) + } else { + copy(S, seed[:seedBytes]) + seed = seed[seedBytes:] + } + + G := generator(S) + + keyGen(A, a, G, seed) +} + +// B,T = xEncrypt(r,(S,A)) +func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { + G := generator(S) + + // convert []int8 to Inputs + var r_inputs Inputs + for i := 0; i < len(r); i++ { + r_inputs[i] = r[i] + } + + b := hashShort(r_inputs) + + return encrypt(r, G, A, b) +} + +// Encoding small polynomials (including short polynomials) + +// Transform polynomial in R to bytes +// these are the only functions that rely on p mod 4 = 1 */ +func smallEncode(s []byte, f []small) { + var x small + var index int = 0 + for i := 0; i < p/4; i++ { + x = f[index] + 1 + index++ + + x += (f[index] + 1) << 2 + index++ + x += (f[index] + 1) << 4 + index++ + x += (f[index] + 1) << 6 + index++ + + s[0] = byte(x) + s = s[1:] + } + x = f[index] + 1 + + s[0] = byte(x) +} + +// Transform bytes into polynomial in R +func smallDecode(f []small, s []byte) { + var index int = 0 + var x byte + + for i := 0; i < p/4; i++ { + x = s[0] + s = s[1:] + + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + index++ + } + x = s[0] + f[index] = ((small)(x & 3)) - 1 +} + +// Encoding rounded polynomials + +// Transform rounded polynomials to bytes +func roundedEncode(s []byte, r []Fq) { + + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16((int32((r[i])+q12) * 10923) >> 15) + M[i] = (q + 2) / 3 + } + internal.Encode(s, R, M, p) +} + +// Transform bytes to rounded polynomials +func roundedDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = (q + 2) / 3 + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = Fq(R[i]*3 - q12) + } + +} + +// Encoding top polynomials + +func topEncode(T []int8) (s []byte) { + s = make([]byte, topBytes) + for i := 0; i < topBytes; i++ { + s[i] = byte(T[2*i] + (T[2*i+1] << 4)) + + } + return s +} + +func topDecode(s []byte) (T []int8) { + + T = make([]int8, 2*topBytes+1) + + for i := 0; i < topBytes; i++ { + T[2*i] = int8(s[i] & 15) + T[2*i+1] = int8(s[i] >> 4) + } + return T +} + +// Streamlined NTRU Prime Core plus encoding + +func inputsRandom(seed []byte) (r Inputs) { + + s := make([]byte, inputsBytes) + + if seed != nil { + copy(s, seed) + } else { + cryptoRand.Read(s) + } + + for i := 0; i < I; i++ { + r[i] = int8(1 & (s[i>>3] >> (i & 7))) + } + + return r +} + +// Generates public key and private key +// pk,sk = zKeyGen() +func zKeyGen(pk []byte, sk []byte, seed []byte) { + A := make([]Fq, p) + a := make([]small, p) + + xKeyGen(pk, A, a, seed) + + pk = pk[seedBytes:] + roundedEncode(pk, A) + + smallEncode(sk, a) +} + +// c = zEncrypt(r,pk) +func zEncrypt(c []byte, r Inputs, pk []byte) { + A := make([]Fq, p) + + roundedDecode(A, pk[seedBytes:]) + B, T := xEncrypt(r[:], pk[:seedBytes], A) + roundedEncode(c, B) + c = c[roundedBytes:] + + copy(c, topEncode(T)) +} + +// r = zDecrypt(C,sk) +func zDecrypt(r *Inputs, c []byte, sk []byte) { + a := make([]small, p) + B := make([]Fq, p) + + smallDecode(a, sk) + roundedDecode(B, c) + T := topDecode(c[roundedBytes:]) + copy(r[:], decrypt(B, T, a)) +} + +// Confirmation hash + +// h = hashConfirm(r,pk,cache); cache is Hash4(pk) +func hashConfirm(h []byte, r []byte, pk []byte, cache []byte) { + x := make([]byte, inputsBytes+hashBytes) + + copy(x, r) + copy(x[inputsBytes:], cache) + + hashPrefix(h, 2, x, len(x)) + +} + +// Session-key hash + +// k = hashSession(b,y,z) +func hashSession(k []byte, b int, y []byte, z []byte) { + x := make([]byte, inputsBytes+ciphertextsBytes+confirmBytes) + copy(x[:inputsBytes], y) + copy(x[inputsBytes:], z) + + hashPrefix(k, b, x, len(x)) +} + +// Streamlined NTRU Prime + +// pk,sk = kemKeyGen() +func kemKeyGen(pk []byte, sk []byte, seed []byte) { + + if seed == nil { + seed = make([]byte, KeySeedSize) + cryptoRand.Read(seed) + } + + if len(seed) != KeySeedSize { + panic("seed must be of length KeySeedSize") + } + + zKeyGen(pk, sk, seed[:seedBytes+p*4]) + seed = seed[seedBytes+p*4:] + + sk = sk[secretKeysBytes:] + + copy(sk, pk) + sk = sk[publicKeysBytes:] + + if seed != nil { + copy(sk[:inputsBytes], seed) + + } else { + cryptoRand.Read(sk[:inputsBytes]) + } + sk = sk[inputsBytes:] + hashPrefix(sk, 4, pk, publicKeysBytes) + +} + +// c,r_enc = hide(r,pk,cache); cache is Hash4(pk) +func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { + inputsEncode(r_enc, r) + + zEncrypt(c, r, pk) + c = c[ciphertextsBytes:] + hashConfirm(c, r_enc, pk, cache) + +} + +// Takes as input a public key +// Returns ciphertext and shared key +// c,k = encap(pk) +func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { + + if seed != nil && len(seed) != EncapsulationSeedSize { + panic("seed must be of length EncapsulationSeedSize") + } + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + r_enc := make([]byte, inputsBytes) + cache := make([]byte, hashBytes) + + hashPrefix(cache, 4, pk.pk[:], publicKeysBytes) + r := inputsRandom(seed) + hide(c, r_enc, r, pk.pk[:], cache) + hashSession(k, 1, r_enc, c) +} + +// Returns 0 if matching ciphertext+confirm, else -1 +func ciphertextsDiffMask(c []byte, c2 []byte) int { + var differentbits uint16 = 0 + var len int = ciphertextsBytes + confirmBytes + + for i := 0; i < len; i++ { + differentbits |= uint16((c[i]) ^ (c2[i])) + } + return int((1 & ((differentbits - 1) >> 8)) - 1) + +} + +// Returns shared key from ciphertext and private key +// k = decap(c,sk) +func (priv *PrivateKey) DecapsulateTo(ss []byte, ct []byte) { + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(ss) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + sk := priv.sk[:] + pk := sk[secretKeysBytes:] + rho := pk[publicKeysBytes:] + cache := rho[inputsBytes:] + var r Inputs + + r_enc := make([]byte, inputsBytes) + cnew := make([]byte, ciphertextsBytes+confirmBytes) + + zDecrypt(&r, ct, sk) + hide(cnew, r_enc, r, pk, cache) + var mask int = ciphertextsDiffMask(ct, cnew) + + for i := 0; i < inputsBytes; i++ { + r_enc[i] ^= byte(mask & int(r_enc[i]^rho[i])) + } + hashSession(ss, 1+mask, r_enc, ct) +} + +// The structure of the private key is given by the following segments: +// The secret key, the public key, entropy and the hash of the public key +type PrivateKey struct { + sk [PrivateKeySize]byte +} + +type PublicKey struct { + pk [PublicKeySize]byte +} + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "ntrulpr761" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + copy(ret[:], sk.sk[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + return bytes.Equal(sk.sk[:], oth.sk[:]) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + return bytes.Equal(pk.pk[:], oth.pk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + var pk [PublicKeySize]byte + skey, _ := sk.MarshalBinary() + ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] + copy(pk[:], ppk[:]) + return &PublicKey{pk: pk} +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + copy(ret[:], pk.pk[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + kemKeyGen(pk[:], sk[:], nil) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk}, nil + +} + +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + + kemKeyGen(pk[:], sk[:], seed) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk} +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, nil) + return ct, ss, nil + +} + +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) (ct, ss []byte, err error) { + + if len(seed) != EncapsulationSeedSize { + return nil, nil, kem.ErrSeedSize + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, seed) + return ct, ss, nil +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + ssk, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + ss := [SharedKeySize]byte{} + + ssk.DecapsulateTo(ss[:], ct) + + return ss[:], nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + pk := [PublicKeySize]byte{} + copy(pk[:], buf) + return &PublicKey{pk: pk}, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + sk := [PrivateKeySize]byte{} + copy(sk[:], buf) + return &PrivateKey{sk: sk}, nil +} diff --git a/kem/ntruprime/ntrulpr857/ntruprime.go b/kem/ntruprime/ntrulpr857/ntruprime.go new file mode 100644 index 000000000..9bc18cfb9 --- /dev/null +++ b/kem/ntruprime/ntrulpr857/ntruprime.go @@ -0,0 +1,867 @@ +// Code generated from ntrulpr.templ.go. DO NOT EDIT. + +// Package ntrulpr857 implements the IND-CCA2 secure key encapsulation mechanism +// ntrulpr857 as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package ntrulpr857 + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + cryptoRand "crypto/rand" + "crypto/sha512" + + "github.com/cloudflare/circl/kem" + "github.com/cloudflare/circl/kem/ntruprime/internal" + ntrup "github.com/cloudflare/circl/pke/ntruprime/ntrulpr857" +) + +const ( + p = ntrup.P + q = ntrup.Q + q12 = ((q - 1) / 2) + roundedBytes = ntrup.RoundedBytes + + w = ntrup.W + tau0 = ntrup.Tau0 + tau1 = ntrup.Tau1 + tau2 = ntrup.Tau2 + tau3 = ntrup.Tau3 + + I = ntrup.I + + hashBytes = 32 + + smallBytes = ((p + 3) / 4) + + inputsBytes = I / 8 + seedBytes = 32 + ciphertextsBytes = roundedBytes + topBytes + secretKeysBytes = smallBytes + publicKeysBytes = seedBytes + roundedBytes + + confirmBytes = 32 + + tau = 16 + topBytes = I / 2 +) + +const ( + // Size of seed for NewKeyFromSeed + KeySeedSize = seedBytes + p*4 + inputsBytes + + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = inputsBytes + + // Size of the established shared key. + SharedKeySize = ntrup.SharedKeySize + + // Size of the encapsulated shared key. + CiphertextSize = ntrup.CiphertextSize + + // Size of a packed public key. + PublicKeySize = ntrup.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = ntrup.PrivateKeySize +) + +type ( + small int8 + Fq int16 +) + +// arithmetic operations over GF(3) + +// A polynomial of R has all of its coefficients in (-1,0,1) +// F3 is always represented as -1,0,1 +// so ZZ_fromF3 is a no-op + +// x must not be close to top int16 +func f3Freeze(x int16) small { + return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 +} + +/* ----- arithmetic mod q */ +// GF (q) +// type Fq int16 + +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +func fqFreeze(x int32) Fq { + return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) +} + +func top(C Fq) int8 { + return int8((tau1*(int32)(C+tau0) + 16384) >> 15) +} + +func right(T int8) Fq { + return fqFreeze(tau3*int32(T) - tau2) +} + +// Polynomials mod q + +// h = f*g in the ring Rq */ +func rqMultSmall(f []Fq, g []small) (h []Fq) { + h = make([]Fq, p) + fg := make([]Fq, p+p-1) + var result Fq + + for i := 0; i < p; i++ { + result = 0 + for j := 0; j <= i; j++ { + result = fqFreeze(int32(result) + int32(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 = fqFreeze(int32(result) + int32(f[j])*(int32)(g[i-j])) + } + fg[i] = result + } + + for i := p + p - 2; i >= p; i-- { + fg[i-p] = fqFreeze(int32(fg[i-p] + fg[i])) + fg[i-p+1] = fqFreeze(int32(fg[i-p+1] + fg[i])) + + } + + for i := 0; i < p; i++ { + h[i] = fg[i] + } + return h +} + +// Rounding all coefficients of a polynomial to the nearest multiple of 3 +// Rounded polynomials mod q +func round(a []Fq) []Fq { + out := make([]Fq, p) + for i := 0; i < p; i++ { + out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) + } + return out +} + +// Returns (min(x, y), max(x, y)), executes in constant time +func minmax(x, y *uint32) { + var xi uint32 = *x + var yi uint32 = *y + var xy uint32 = xi ^ yi + var c uint32 = yi - xi + c ^= xy & (c ^ yi ^ 0x80000000) + c >>= 31 + c = -c + c &= xy + *x = xi ^ c + *y = yi ^ c +} + +// Sorts the array of unsigned integers +func cryptoSortUint32(x []uint32, n int) { + if n < 2 { + return + } + top := 1 + + for top < n-top { + top += top + } + + for p := top; p > 0; p >>= 1 { + for i := 0; i < n-p; i++ { + if i&p == 0 { + minmax(&x[i], &x[i+p]) + } + } + for q := top; q > p; q >>= 1 { + for i := 0; i < n-q; i++ { + if i&p == 0 { + minmax(&x[i+p], &x[i+q]) + } + } + } + } +} + +// Sorting to generate short polynomial +func shortFromList(in []int32) []small { + out := make([]small, p) + L := make([]uint32, p) + + var neg2, neg3 int = -2, -3 + + for i := 0; i < w; i++ { + L[i] = uint32(in[i]) & uint32((neg2)) + } + + for i := w; i < p; i++ { + L[i] = (uint32(in[i]) & uint32((neg3))) | 1 + } + + cryptoSortUint32(L, p) + + for i := 0; i < p; i++ { + out[i] = small((L[i] & 3) - 1) + } + return out +} + +// Underlying hash function + +// The input byte array, in, is prepended by the byte b +// and its SHA-512 hash is calculated +// Only the first 32 bytes of the hash are returned +// e.g., b = 0 means out = Hash0(in) +func hashPrefix(out []byte, b int, in []byte, inlen int) { + x := make([]byte, inlen+1) + h := make([]byte, 64) + + x[0] = byte(b) + copy(x[1:], in) + + hash := sha512.New() + hash.Write([]byte(x)) + h = hash.Sum(nil) + + copy(out, h[:32]) + +} + +// Higher level randomness +// Returns a random unsigned integer +// generator can be passed for deterministic number generation +func urandom32(seed []byte) uint32 { + + c := make([]byte, 4) + var out [4]uint32 + + if seed != nil { + copy(c, seed) + } else { + cryptoRand.Read(c) + + } + + 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] +} + +// Generates a random short polynomial +func shortRandom(seed []byte) []small { + + L := make([]uint32, p) + + if seed != nil { + for i := 0; i < p; i++ { + L[i] = urandom32(seed[i*4 : i*4+4]) + } + } else { + for i := 0; i < p; i++ { + L[i] = urandom32(nil) + } + } + + // Converts uint32 array to int32 array + L_int32 := make([]int32, p) + for i := 0; i < len(L); i++ { + L_int32[i] = int32(L[i]) + } + out := shortFromList(L_int32) + + return out + +} + +// NTRU LPRime Core + +// (G,A),a = keyGen(G); leaves G unchanged +func keyGen(A []Fq, a []small, G []Fq, seed []byte) { + + copy(a, shortRandom(seed)) + aG := rqMultSmall(G, a) + copy(A, round(aG)) +} + +// B,T = encrypt(r,(G,A),b) +func encrypt(r []int8, G []Fq, A []Fq, b []small) ([]Fq, []int8) { + T := make([]int8, I) + bG := rqMultSmall(G, b) + B := round(bG) + bA := rqMultSmall(A, b) + + for i := 0; i < I; i++ { + T[i] = top(fqFreeze(int32(bA[i]) + int32(r[i])*q12)) + } + + return B, T +} + +// r = decrypt((B,T),a) +func decrypt(B []Fq, T []int8, a []small) []int8 { + + r := make([]int8, I) + + aB := rqMultSmall(B, a) + + for i := 0; i < I; i++ { + r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) + } + + return r +} + +// Encoding I-bit inputs +type Inputs [I]int8 + +func inputsEncode(s []byte, r Inputs) { + + for i := 0; i < I; i++ { + s[i>>3] |= byte(r[i] << (i & 7)) + } + +} + +// Expand + +func expand(L []uint32, k []byte) { + temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) + aes_nonce := make([]byte, aes.BlockSize) // IV + + block, err := aes.NewCipher(k[:32]) + if err != nil { + panic(err) + } + + ciphertext := make([]byte, len(temp)) // same length as plaintext + + stream := cipher.NewCTR(block, aes_nonce) + stream.XORKeyStream(ciphertext, temp) + + // convert byte to uint32 + for i := 0; i < len(temp); i++ { + L[i] = uint32(ciphertext[i]) + } + + for i := 0; i < p; i++ { + var L0 uint32 = L[4*i] + var L1 uint32 = L[4*i+1] + var L2 uint32 = L[4*i+2] + var L3 uint32 = L[4*i+3] + L[i] = L0 + (L1 << 8) + (L2 << 16) + (L3 << 24) + } +} + +// generator, hashShort +// G = generator(k) +func generator(k []byte) (G []Fq) { + G = make([]Fq, p) + L := make([]uint32, 4*p) + expand(L, k) + for i := 0; i < p; i++ { + G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) + } + return G +} + +// out = hashShort(r) +func hashShort(r Inputs) (out []small) { + s := make([]byte, inputsBytes) + inputsEncode(s, r) + h := make([]byte, hashBytes) + L := make([]uint32, 4*p) + L_int32 := make([]int32, p) + + hashPrefix(h, 5, s, len(s)) + + expand(L, h) + + // convert []uint32 to []int32 + for i := 0; i < p; i++ { + L_int32[i] = int32(L[i]) + } + out = shortFromList(L_int32) + return out +} + +// NTRU LPRime expand + +// (S,A),a = xKeyGen() +func xKeyGen(S []byte, A []Fq, a []small, seed []byte) { + + if seed == nil { + cryptoRand.Read(S) + } else { + copy(S, seed[:seedBytes]) + seed = seed[seedBytes:] + } + + G := generator(S) + + keyGen(A, a, G, seed) +} + +// B,T = xEncrypt(r,(S,A)) +func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { + G := generator(S) + + // convert []int8 to Inputs + var r_inputs Inputs + for i := 0; i < len(r); i++ { + r_inputs[i] = r[i] + } + + b := hashShort(r_inputs) + + return encrypt(r, G, A, b) +} + +// Encoding small polynomials (including short polynomials) + +// Transform polynomial in R to bytes +// these are the only functions that rely on p mod 4 = 1 */ +func smallEncode(s []byte, f []small) { + var x small + var index int = 0 + for i := 0; i < p/4; i++ { + x = f[index] + 1 + index++ + + x += (f[index] + 1) << 2 + index++ + x += (f[index] + 1) << 4 + index++ + x += (f[index] + 1) << 6 + index++ + + s[0] = byte(x) + s = s[1:] + } + x = f[index] + 1 + + s[0] = byte(x) +} + +// Transform bytes into polynomial in R +func smallDecode(f []small, s []byte) { + var index int = 0 + var x byte + + for i := 0; i < p/4; i++ { + x = s[0] + s = s[1:] + + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + index++ + } + x = s[0] + f[index] = ((small)(x & 3)) - 1 +} + +// Encoding rounded polynomials + +// Transform rounded polynomials to bytes +func roundedEncode(s []byte, r []Fq) { + + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16((int32((r[i])+q12) * 10923) >> 15) + M[i] = (q + 2) / 3 + } + internal.Encode(s, R, M, p) +} + +// Transform bytes to rounded polynomials +func roundedDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = (q + 2) / 3 + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = Fq(R[i]*3 - q12) + } + +} + +// Encoding top polynomials + +func topEncode(T []int8) (s []byte) { + s = make([]byte, topBytes) + for i := 0; i < topBytes; i++ { + s[i] = byte(T[2*i] + (T[2*i+1] << 4)) + + } + return s +} + +func topDecode(s []byte) (T []int8) { + + T = make([]int8, 2*topBytes+1) + + for i := 0; i < topBytes; i++ { + T[2*i] = int8(s[i] & 15) + T[2*i+1] = int8(s[i] >> 4) + } + return T +} + +// Streamlined NTRU Prime Core plus encoding + +func inputsRandom(seed []byte) (r Inputs) { + + s := make([]byte, inputsBytes) + + if seed != nil { + copy(s, seed) + } else { + cryptoRand.Read(s) + } + + for i := 0; i < I; i++ { + r[i] = int8(1 & (s[i>>3] >> (i & 7))) + } + + return r +} + +// Generates public key and private key +// pk,sk = zKeyGen() +func zKeyGen(pk []byte, sk []byte, seed []byte) { + A := make([]Fq, p) + a := make([]small, p) + + xKeyGen(pk, A, a, seed) + + pk = pk[seedBytes:] + roundedEncode(pk, A) + + smallEncode(sk, a) +} + +// c = zEncrypt(r,pk) +func zEncrypt(c []byte, r Inputs, pk []byte) { + A := make([]Fq, p) + + roundedDecode(A, pk[seedBytes:]) + B, T := xEncrypt(r[:], pk[:seedBytes], A) + roundedEncode(c, B) + c = c[roundedBytes:] + + copy(c, topEncode(T)) +} + +// r = zDecrypt(C,sk) +func zDecrypt(r *Inputs, c []byte, sk []byte) { + a := make([]small, p) + B := make([]Fq, p) + + smallDecode(a, sk) + roundedDecode(B, c) + T := topDecode(c[roundedBytes:]) + copy(r[:], decrypt(B, T, a)) +} + +// Confirmation hash + +// h = hashConfirm(r,pk,cache); cache is Hash4(pk) +func hashConfirm(h []byte, r []byte, pk []byte, cache []byte) { + x := make([]byte, inputsBytes+hashBytes) + + copy(x, r) + copy(x[inputsBytes:], cache) + + hashPrefix(h, 2, x, len(x)) + +} + +// Session-key hash + +// k = hashSession(b,y,z) +func hashSession(k []byte, b int, y []byte, z []byte) { + x := make([]byte, inputsBytes+ciphertextsBytes+confirmBytes) + copy(x[:inputsBytes], y) + copy(x[inputsBytes:], z) + + hashPrefix(k, b, x, len(x)) +} + +// Streamlined NTRU Prime + +// pk,sk = kemKeyGen() +func kemKeyGen(pk []byte, sk []byte, seed []byte) { + + if seed == nil { + seed = make([]byte, KeySeedSize) + cryptoRand.Read(seed) + } + + if len(seed) != KeySeedSize { + panic("seed must be of length KeySeedSize") + } + + zKeyGen(pk, sk, seed[:seedBytes+p*4]) + seed = seed[seedBytes+p*4:] + + sk = sk[secretKeysBytes:] + + copy(sk, pk) + sk = sk[publicKeysBytes:] + + if seed != nil { + copy(sk[:inputsBytes], seed) + + } else { + cryptoRand.Read(sk[:inputsBytes]) + } + sk = sk[inputsBytes:] + hashPrefix(sk, 4, pk, publicKeysBytes) + +} + +// c,r_enc = hide(r,pk,cache); cache is Hash4(pk) +func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { + inputsEncode(r_enc, r) + + zEncrypt(c, r, pk) + c = c[ciphertextsBytes:] + hashConfirm(c, r_enc, pk, cache) + +} + +// Takes as input a public key +// Returns ciphertext and shared key +// c,k = encap(pk) +func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { + + if seed != nil && len(seed) != EncapsulationSeedSize { + panic("seed must be of length EncapsulationSeedSize") + } + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + r_enc := make([]byte, inputsBytes) + cache := make([]byte, hashBytes) + + hashPrefix(cache, 4, pk.pk[:], publicKeysBytes) + r := inputsRandom(seed) + hide(c, r_enc, r, pk.pk[:], cache) + hashSession(k, 1, r_enc, c) +} + +// Returns 0 if matching ciphertext+confirm, else -1 +func ciphertextsDiffMask(c []byte, c2 []byte) int { + var differentbits uint16 = 0 + var len int = ciphertextsBytes + confirmBytes + + for i := 0; i < len; i++ { + differentbits |= uint16((c[i]) ^ (c2[i])) + } + return int((1 & ((differentbits - 1) >> 8)) - 1) + +} + +// Returns shared key from ciphertext and private key +// k = decap(c,sk) +func (priv *PrivateKey) DecapsulateTo(ss []byte, ct []byte) { + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(ss) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + sk := priv.sk[:] + pk := sk[secretKeysBytes:] + rho := pk[publicKeysBytes:] + cache := rho[inputsBytes:] + var r Inputs + + r_enc := make([]byte, inputsBytes) + cnew := make([]byte, ciphertextsBytes+confirmBytes) + + zDecrypt(&r, ct, sk) + hide(cnew, r_enc, r, pk, cache) + var mask int = ciphertextsDiffMask(ct, cnew) + + for i := 0; i < inputsBytes; i++ { + r_enc[i] ^= byte(mask & int(r_enc[i]^rho[i])) + } + hashSession(ss, 1+mask, r_enc, ct) +} + +// The structure of the private key is given by the following segments: +// The secret key, the public key, entropy and the hash of the public key +type PrivateKey struct { + sk [PrivateKeySize]byte +} + +type PublicKey struct { + pk [PublicKeySize]byte +} + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "ntrulpr857" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + copy(ret[:], sk.sk[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + return bytes.Equal(sk.sk[:], oth.sk[:]) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + return bytes.Equal(pk.pk[:], oth.pk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + var pk [PublicKeySize]byte + skey, _ := sk.MarshalBinary() + ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] + copy(pk[:], ppk[:]) + return &PublicKey{pk: pk} +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + copy(ret[:], pk.pk[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + kemKeyGen(pk[:], sk[:], nil) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk}, nil + +} + +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + + kemKeyGen(pk[:], sk[:], seed) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk} +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, nil) + return ct, ss, nil + +} + +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) (ct, ss []byte, err error) { + + if len(seed) != EncapsulationSeedSize { + return nil, nil, kem.ErrSeedSize + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, seed) + return ct, ss, nil +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + ssk, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + ss := [SharedKeySize]byte{} + + ssk.DecapsulateTo(ss[:], ct) + + return ss[:], nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + pk := [PublicKeySize]byte{} + copy(pk[:], buf) + return &PublicKey{pk: pk}, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + sk := [PrivateKeySize]byte{} + copy(sk[:], buf) + return &PrivateKey{sk: sk}, nil +} diff --git a/kem/ntruprime/ntrulpr953/ntruprime.go b/kem/ntruprime/ntrulpr953/ntruprime.go new file mode 100644 index 000000000..9a047f29c --- /dev/null +++ b/kem/ntruprime/ntrulpr953/ntruprime.go @@ -0,0 +1,867 @@ +// Code generated from ntrulpr.templ.go. DO NOT EDIT. + +// Package ntrulpr953 implements the IND-CCA2 secure key encapsulation mechanism +// ntrulpr953 as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package ntrulpr953 + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + cryptoRand "crypto/rand" + "crypto/sha512" + + "github.com/cloudflare/circl/kem" + "github.com/cloudflare/circl/kem/ntruprime/internal" + ntrup "github.com/cloudflare/circl/pke/ntruprime/ntrulpr953" +) + +const ( + p = ntrup.P + q = ntrup.Q + q12 = ((q - 1) / 2) + roundedBytes = ntrup.RoundedBytes + + w = ntrup.W + tau0 = ntrup.Tau0 + tau1 = ntrup.Tau1 + tau2 = ntrup.Tau2 + tau3 = ntrup.Tau3 + + I = ntrup.I + + hashBytes = 32 + + smallBytes = ((p + 3) / 4) + + inputsBytes = I / 8 + seedBytes = 32 + ciphertextsBytes = roundedBytes + topBytes + secretKeysBytes = smallBytes + publicKeysBytes = seedBytes + roundedBytes + + confirmBytes = 32 + + tau = 16 + topBytes = I / 2 +) + +const ( + // Size of seed for NewKeyFromSeed + KeySeedSize = seedBytes + p*4 + inputsBytes + + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = inputsBytes + + // Size of the established shared key. + SharedKeySize = ntrup.SharedKeySize + + // Size of the encapsulated shared key. + CiphertextSize = ntrup.CiphertextSize + + // Size of a packed public key. + PublicKeySize = ntrup.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = ntrup.PrivateKeySize +) + +type ( + small int8 + Fq int16 +) + +// arithmetic operations over GF(3) + +// A polynomial of R has all of its coefficients in (-1,0,1) +// F3 is always represented as -1,0,1 +// so ZZ_fromF3 is a no-op + +// x must not be close to top int16 +func f3Freeze(x int16) small { + return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 +} + +/* ----- arithmetic mod q */ +// GF (q) +// type Fq int16 + +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +func fqFreeze(x int32) Fq { + return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) +} + +func top(C Fq) int8 { + return int8((tau1*(int32)(C+tau0) + 16384) >> 15) +} + +func right(T int8) Fq { + return fqFreeze(tau3*int32(T) - tau2) +} + +// Polynomials mod q + +// h = f*g in the ring Rq */ +func rqMultSmall(f []Fq, g []small) (h []Fq) { + h = make([]Fq, p) + fg := make([]Fq, p+p-1) + var result Fq + + for i := 0; i < p; i++ { + result = 0 + for j := 0; j <= i; j++ { + result = fqFreeze(int32(result) + int32(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 = fqFreeze(int32(result) + int32(f[j])*(int32)(g[i-j])) + } + fg[i] = result + } + + for i := p + p - 2; i >= p; i-- { + fg[i-p] = fqFreeze(int32(fg[i-p] + fg[i])) + fg[i-p+1] = fqFreeze(int32(fg[i-p+1] + fg[i])) + + } + + for i := 0; i < p; i++ { + h[i] = fg[i] + } + return h +} + +// Rounding all coefficients of a polynomial to the nearest multiple of 3 +// Rounded polynomials mod q +func round(a []Fq) []Fq { + out := make([]Fq, p) + for i := 0; i < p; i++ { + out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) + } + return out +} + +// Returns (min(x, y), max(x, y)), executes in constant time +func minmax(x, y *uint32) { + var xi uint32 = *x + var yi uint32 = *y + var xy uint32 = xi ^ yi + var c uint32 = yi - xi + c ^= xy & (c ^ yi ^ 0x80000000) + c >>= 31 + c = -c + c &= xy + *x = xi ^ c + *y = yi ^ c +} + +// Sorts the array of unsigned integers +func cryptoSortUint32(x []uint32, n int) { + if n < 2 { + return + } + top := 1 + + for top < n-top { + top += top + } + + for p := top; p > 0; p >>= 1 { + for i := 0; i < n-p; i++ { + if i&p == 0 { + minmax(&x[i], &x[i+p]) + } + } + for q := top; q > p; q >>= 1 { + for i := 0; i < n-q; i++ { + if i&p == 0 { + minmax(&x[i+p], &x[i+q]) + } + } + } + } +} + +// Sorting to generate short polynomial +func shortFromList(in []int32) []small { + out := make([]small, p) + L := make([]uint32, p) + + var neg2, neg3 int = -2, -3 + + for i := 0; i < w; i++ { + L[i] = uint32(in[i]) & uint32((neg2)) + } + + for i := w; i < p; i++ { + L[i] = (uint32(in[i]) & uint32((neg3))) | 1 + } + + cryptoSortUint32(L, p) + + for i := 0; i < p; i++ { + out[i] = small((L[i] & 3) - 1) + } + return out +} + +// Underlying hash function + +// The input byte array, in, is prepended by the byte b +// and its SHA-512 hash is calculated +// Only the first 32 bytes of the hash are returned +// e.g., b = 0 means out = Hash0(in) +func hashPrefix(out []byte, b int, in []byte, inlen int) { + x := make([]byte, inlen+1) + h := make([]byte, 64) + + x[0] = byte(b) + copy(x[1:], in) + + hash := sha512.New() + hash.Write([]byte(x)) + h = hash.Sum(nil) + + copy(out, h[:32]) + +} + +// Higher level randomness +// Returns a random unsigned integer +// generator can be passed for deterministic number generation +func urandom32(seed []byte) uint32 { + + c := make([]byte, 4) + var out [4]uint32 + + if seed != nil { + copy(c, seed) + } else { + cryptoRand.Read(c) + + } + + 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] +} + +// Generates a random short polynomial +func shortRandom(seed []byte) []small { + + L := make([]uint32, p) + + if seed != nil { + for i := 0; i < p; i++ { + L[i] = urandom32(seed[i*4 : i*4+4]) + } + } else { + for i := 0; i < p; i++ { + L[i] = urandom32(nil) + } + } + + // Converts uint32 array to int32 array + L_int32 := make([]int32, p) + for i := 0; i < len(L); i++ { + L_int32[i] = int32(L[i]) + } + out := shortFromList(L_int32) + + return out + +} + +// NTRU LPRime Core + +// (G,A),a = keyGen(G); leaves G unchanged +func keyGen(A []Fq, a []small, G []Fq, seed []byte) { + + copy(a, shortRandom(seed)) + aG := rqMultSmall(G, a) + copy(A, round(aG)) +} + +// B,T = encrypt(r,(G,A),b) +func encrypt(r []int8, G []Fq, A []Fq, b []small) ([]Fq, []int8) { + T := make([]int8, I) + bG := rqMultSmall(G, b) + B := round(bG) + bA := rqMultSmall(A, b) + + for i := 0; i < I; i++ { + T[i] = top(fqFreeze(int32(bA[i]) + int32(r[i])*q12)) + } + + return B, T +} + +// r = decrypt((B,T),a) +func decrypt(B []Fq, T []int8, a []small) []int8 { + + r := make([]int8, I) + + aB := rqMultSmall(B, a) + + for i := 0; i < I; i++ { + r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) + } + + return r +} + +// Encoding I-bit inputs +type Inputs [I]int8 + +func inputsEncode(s []byte, r Inputs) { + + for i := 0; i < I; i++ { + s[i>>3] |= byte(r[i] << (i & 7)) + } + +} + +// Expand + +func expand(L []uint32, k []byte) { + temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) + aes_nonce := make([]byte, aes.BlockSize) // IV + + block, err := aes.NewCipher(k[:32]) + if err != nil { + panic(err) + } + + ciphertext := make([]byte, len(temp)) // same length as plaintext + + stream := cipher.NewCTR(block, aes_nonce) + stream.XORKeyStream(ciphertext, temp) + + // convert byte to uint32 + for i := 0; i < len(temp); i++ { + L[i] = uint32(ciphertext[i]) + } + + for i := 0; i < p; i++ { + var L0 uint32 = L[4*i] + var L1 uint32 = L[4*i+1] + var L2 uint32 = L[4*i+2] + var L3 uint32 = L[4*i+3] + L[i] = L0 + (L1 << 8) + (L2 << 16) + (L3 << 24) + } +} + +// generator, hashShort +// G = generator(k) +func generator(k []byte) (G []Fq) { + G = make([]Fq, p) + L := make([]uint32, 4*p) + expand(L, k) + for i := 0; i < p; i++ { + G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) + } + return G +} + +// out = hashShort(r) +func hashShort(r Inputs) (out []small) { + s := make([]byte, inputsBytes) + inputsEncode(s, r) + h := make([]byte, hashBytes) + L := make([]uint32, 4*p) + L_int32 := make([]int32, p) + + hashPrefix(h, 5, s, len(s)) + + expand(L, h) + + // convert []uint32 to []int32 + for i := 0; i < p; i++ { + L_int32[i] = int32(L[i]) + } + out = shortFromList(L_int32) + return out +} + +// NTRU LPRime expand + +// (S,A),a = xKeyGen() +func xKeyGen(S []byte, A []Fq, a []small, seed []byte) { + + if seed == nil { + cryptoRand.Read(S) + } else { + copy(S, seed[:seedBytes]) + seed = seed[seedBytes:] + } + + G := generator(S) + + keyGen(A, a, G, seed) +} + +// B,T = xEncrypt(r,(S,A)) +func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { + G := generator(S) + + // convert []int8 to Inputs + var r_inputs Inputs + for i := 0; i < len(r); i++ { + r_inputs[i] = r[i] + } + + b := hashShort(r_inputs) + + return encrypt(r, G, A, b) +} + +// Encoding small polynomials (including short polynomials) + +// Transform polynomial in R to bytes +// these are the only functions that rely on p mod 4 = 1 */ +func smallEncode(s []byte, f []small) { + var x small + var index int = 0 + for i := 0; i < p/4; i++ { + x = f[index] + 1 + index++ + + x += (f[index] + 1) << 2 + index++ + x += (f[index] + 1) << 4 + index++ + x += (f[index] + 1) << 6 + index++ + + s[0] = byte(x) + s = s[1:] + } + x = f[index] + 1 + + s[0] = byte(x) +} + +// Transform bytes into polynomial in R +func smallDecode(f []small, s []byte) { + var index int = 0 + var x byte + + for i := 0; i < p/4; i++ { + x = s[0] + s = s[1:] + + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + index++ + } + x = s[0] + f[index] = ((small)(x & 3)) - 1 +} + +// Encoding rounded polynomials + +// Transform rounded polynomials to bytes +func roundedEncode(s []byte, r []Fq) { + + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16((int32((r[i])+q12) * 10923) >> 15) + M[i] = (q + 2) / 3 + } + internal.Encode(s, R, M, p) +} + +// Transform bytes to rounded polynomials +func roundedDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = (q + 2) / 3 + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = Fq(R[i]*3 - q12) + } + +} + +// Encoding top polynomials + +func topEncode(T []int8) (s []byte) { + s = make([]byte, topBytes) + for i := 0; i < topBytes; i++ { + s[i] = byte(T[2*i] + (T[2*i+1] << 4)) + + } + return s +} + +func topDecode(s []byte) (T []int8) { + + T = make([]int8, 2*topBytes+1) + + for i := 0; i < topBytes; i++ { + T[2*i] = int8(s[i] & 15) + T[2*i+1] = int8(s[i] >> 4) + } + return T +} + +// Streamlined NTRU Prime Core plus encoding + +func inputsRandom(seed []byte) (r Inputs) { + + s := make([]byte, inputsBytes) + + if seed != nil { + copy(s, seed) + } else { + cryptoRand.Read(s) + } + + for i := 0; i < I; i++ { + r[i] = int8(1 & (s[i>>3] >> (i & 7))) + } + + return r +} + +// Generates public key and private key +// pk,sk = zKeyGen() +func zKeyGen(pk []byte, sk []byte, seed []byte) { + A := make([]Fq, p) + a := make([]small, p) + + xKeyGen(pk, A, a, seed) + + pk = pk[seedBytes:] + roundedEncode(pk, A) + + smallEncode(sk, a) +} + +// c = zEncrypt(r,pk) +func zEncrypt(c []byte, r Inputs, pk []byte) { + A := make([]Fq, p) + + roundedDecode(A, pk[seedBytes:]) + B, T := xEncrypt(r[:], pk[:seedBytes], A) + roundedEncode(c, B) + c = c[roundedBytes:] + + copy(c, topEncode(T)) +} + +// r = zDecrypt(C,sk) +func zDecrypt(r *Inputs, c []byte, sk []byte) { + a := make([]small, p) + B := make([]Fq, p) + + smallDecode(a, sk) + roundedDecode(B, c) + T := topDecode(c[roundedBytes:]) + copy(r[:], decrypt(B, T, a)) +} + +// Confirmation hash + +// h = hashConfirm(r,pk,cache); cache is Hash4(pk) +func hashConfirm(h []byte, r []byte, pk []byte, cache []byte) { + x := make([]byte, inputsBytes+hashBytes) + + copy(x, r) + copy(x[inputsBytes:], cache) + + hashPrefix(h, 2, x, len(x)) + +} + +// Session-key hash + +// k = hashSession(b,y,z) +func hashSession(k []byte, b int, y []byte, z []byte) { + x := make([]byte, inputsBytes+ciphertextsBytes+confirmBytes) + copy(x[:inputsBytes], y) + copy(x[inputsBytes:], z) + + hashPrefix(k, b, x, len(x)) +} + +// Streamlined NTRU Prime + +// pk,sk = kemKeyGen() +func kemKeyGen(pk []byte, sk []byte, seed []byte) { + + if seed == nil { + seed = make([]byte, KeySeedSize) + cryptoRand.Read(seed) + } + + if len(seed) != KeySeedSize { + panic("seed must be of length KeySeedSize") + } + + zKeyGen(pk, sk, seed[:seedBytes+p*4]) + seed = seed[seedBytes+p*4:] + + sk = sk[secretKeysBytes:] + + copy(sk, pk) + sk = sk[publicKeysBytes:] + + if seed != nil { + copy(sk[:inputsBytes], seed) + + } else { + cryptoRand.Read(sk[:inputsBytes]) + } + sk = sk[inputsBytes:] + hashPrefix(sk, 4, pk, publicKeysBytes) + +} + +// c,r_enc = hide(r,pk,cache); cache is Hash4(pk) +func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { + inputsEncode(r_enc, r) + + zEncrypt(c, r, pk) + c = c[ciphertextsBytes:] + hashConfirm(c, r_enc, pk, cache) + +} + +// Takes as input a public key +// Returns ciphertext and shared key +// c,k = encap(pk) +func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { + + if seed != nil && len(seed) != EncapsulationSeedSize { + panic("seed must be of length EncapsulationSeedSize") + } + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + r_enc := make([]byte, inputsBytes) + cache := make([]byte, hashBytes) + + hashPrefix(cache, 4, pk.pk[:], publicKeysBytes) + r := inputsRandom(seed) + hide(c, r_enc, r, pk.pk[:], cache) + hashSession(k, 1, r_enc, c) +} + +// Returns 0 if matching ciphertext+confirm, else -1 +func ciphertextsDiffMask(c []byte, c2 []byte) int { + var differentbits uint16 = 0 + var len int = ciphertextsBytes + confirmBytes + + for i := 0; i < len; i++ { + differentbits |= uint16((c[i]) ^ (c2[i])) + } + return int((1 & ((differentbits - 1) >> 8)) - 1) + +} + +// Returns shared key from ciphertext and private key +// k = decap(c,sk) +func (priv *PrivateKey) DecapsulateTo(ss []byte, ct []byte) { + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(ss) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + sk := priv.sk[:] + pk := sk[secretKeysBytes:] + rho := pk[publicKeysBytes:] + cache := rho[inputsBytes:] + var r Inputs + + r_enc := make([]byte, inputsBytes) + cnew := make([]byte, ciphertextsBytes+confirmBytes) + + zDecrypt(&r, ct, sk) + hide(cnew, r_enc, r, pk, cache) + var mask int = ciphertextsDiffMask(ct, cnew) + + for i := 0; i < inputsBytes; i++ { + r_enc[i] ^= byte(mask & int(r_enc[i]^rho[i])) + } + hashSession(ss, 1+mask, r_enc, ct) +} + +// The structure of the private key is given by the following segments: +// The secret key, the public key, entropy and the hash of the public key +type PrivateKey struct { + sk [PrivateKeySize]byte +} + +type PublicKey struct { + pk [PublicKeySize]byte +} + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "ntrulpr953" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + copy(ret[:], sk.sk[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + return bytes.Equal(sk.sk[:], oth.sk[:]) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + return bytes.Equal(pk.pk[:], oth.pk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + var pk [PublicKeySize]byte + skey, _ := sk.MarshalBinary() + ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] + copy(pk[:], ppk[:]) + return &PublicKey{pk: pk} +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + copy(ret[:], pk.pk[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + kemKeyGen(pk[:], sk[:], nil) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk}, nil + +} + +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + + kemKeyGen(pk[:], sk[:], seed) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk} +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, nil) + return ct, ss, nil + +} + +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) (ct, ss []byte, err error) { + + if len(seed) != EncapsulationSeedSize { + return nil, nil, kem.ErrSeedSize + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, seed) + return ct, ss, nil +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + ssk, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + ss := [SharedKeySize]byte{} + + ssk.DecapsulateTo(ss[:], ct) + + return ss[:], nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + pk := [PublicKeySize]byte{} + copy(pk[:], buf) + return &PublicKey{pk: pk}, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + sk := [PrivateKeySize]byte{} + copy(sk[:], buf) + return &PrivateKey{sk: sk}, nil +} diff --git a/kem/ntruprime/sntrup1013/ntruprime.go b/kem/ntruprime/sntrup1013/ntruprime.go new file mode 100644 index 000000000..3edc4d670 --- /dev/null +++ b/kem/ntruprime/sntrup1013/ntruprime.go @@ -0,0 +1,956 @@ +// Code generated from sntrup.templ.go. DO NOT EDIT. + +// Package sntrup1013 implements the IND-CCA2 secure key encapsulation mechanism +// sntrup1013 as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package sntrup1013 + +import ( + "bytes" + cryptoRand "crypto/rand" + "crypto/sha512" + + "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/kem/ntruprime/internal" + "github.com/cloudflare/circl/pke/ntruprime/kem" + ntrup "github.com/cloudflare/circl/pke/ntruprime/sntrup1013" +) + +type ( + small int8 + Fq int16 + Inputs [p]small +) + +const ( + p = ntrup.P + q = ntrup.Q + q12 = ((q - 1) / 2) + roundedBytes = ntrup.RoundedBytes + rqBytes = ntrup.RqBytes + w = ntrup.W + + hashBytes = 32 + + smallBytes = ((p + 3) / 4) + + inputsBytes = smallBytes + ciphertextsBytes = roundedBytes + secretKeysBytes = (2 * smallBytes) + publicKeysBytes = rqBytes + + confirmBytes = 32 +) + +const ( + // Size of the established shared key. + SharedKeySize = ntrup.SharedKeySize + + // Size of the encapsulated shared key. + CiphertextSize = ntrup.CiphertextSize + + // Size of a packed public key. + PublicKeySize = ntrup.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = ntrup.PrivateKeySize +) + +// Arithmetic operations over GF(3) + +// A polynomial of R has all of its coefficients in (-1,0,1) +// F3 is always represented as -1,0,1 +// so ZZ_fromF3 is a no-op + +// x must not be close to top int16 +func f3Freeze(x int16) small { + return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 +} + +// Arithmetic operations over GF(q) + +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +func fqFreeze(x int32) Fq { + return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) +} + +// Calculates reciprocal of Fq +func fqRecip(a1 Fq) Fq { + var i int = 1 + ai := a1 + + for i < (q - 2) { + ai = fqFreeze(int32(a1) * int32(ai)) + i += 1 + } + return ai +} + +// Returns 0 if the weight w is equal to r +// otherwise returns -1 +func weightwMask(r []small) int { + var weight int = 0 + + for i := 0; i < p; i++ { + weight += int(r[i]) & 1 + } + + // returns -1 if non zero + // otherwise returns 0 if weight==w + return internal.Int16_nonzero_mask(int16(weight - w)) + +} + +/* R3_fromR(R_fromRq(r)) */ +func r3FromRq(r []Fq) []small { + out := make([]small, p) + for i := 0; i < p; i++ { + out[i] = small(f3Freeze(int16(r[i]))) + } + return out +} + +// h = f*g in the ring R3 +func r3Mult(f []small, g []small) (h []small) { + h = make([]small, p) + fg := make([]small, p+p-1) + var result small + var i, j int + + for i = 0; i < p; i++ { + result = 0 + for j = 0; j <= i; j++ { + result = f3Freeze(int16(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 = f3Freeze(int16(result + f[j]*g[i-j])) + } + fg[i] = result + } + + for i = p + p - 2; i >= p; i-- { + fg[i-p] = f3Freeze(int16(fg[i-p] + fg[i])) + fg[i-p+1] = f3Freeze(int16(fg[i-p+1] + fg[i])) + } + + for i = 0; i < p; i++ { + h[i] = fg[i] + } + + return h + +} + +// Calculates the reciprocal of R3 polynomials +// Returns 0 if recip succeeded; else -1 +func r3Recip(in []small) ([]small, int) { + out := make([]small, p) + f := make([]small, p+1) + g := make([]small, p+1) + v := make([]small, p+1) + r := make([]small, p+1) + + var sign int + + r[0] = 1 + f[0] = 1 + + f[p-1] = -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 = int(-g[0] * f[0]) + var swap int = int(internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0]))) + delta ^= swap & int(delta^-delta) + delta += 1 + + for i := 0; i < p+1; i++ { + t := swap & int(f[i]^g[i]) + f[i] ^= small(t) + g[i] ^= small(t) + t = swap & int(v[i]^r[i]) + v[i] ^= small(t) + r[i] ^= small(t) + } + for i := 0; i < p+1; i++ { + g[i] = f3Freeze(int16(int(g[i]) + sign*int(f[i]))) + } + + for i := 0; i < p+1; i++ { + r[i] = f3Freeze(int16(int(r[i]) + sign*int(v[i]))) + } + + for i := 0; i < p; i++ { + g[i] = g[i+1] + } + + g[p] = 0 + + } + sign = int(f[0]) + + for i := 0; i < p; i++ { + + out[i] = small(sign * int(v[p-1-i])) + } + + return out, internal.Int16_nonzero_mask(int16(delta)) + +} + +// Polynomials mod q + +// h = f*g in the ring Rq */ +func rqMultSmall(f []Fq, g []small) (h []Fq) { + h = make([]Fq, p) + fg := make([]Fq, p+p-1) + var result Fq + + for i := 0; i < p; i++ { + result = 0 + for j := 0; j <= i; j++ { + result = fqFreeze(int32(result) + int32(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 = fqFreeze(int32(result) + int32(f[j])*(int32)(g[i-j])) + } + fg[i] = result + } + + for i := p + p - 2; i >= p; i-- { + fg[i-p] = fqFreeze(int32(fg[i-p] + fg[i])) + fg[i-p+1] = fqFreeze(int32(fg[i-p+1] + fg[i])) + + } + + for i := 0; i < p; i++ { + h[i] = fg[i] + } + return h +} + +// h = 3f in Rq +func rqMult3(f []Fq) (h []Fq) { + h = make([]Fq, p) + for i := 0; i < p; i++ { + h[i] = fqFreeze(int32(3 * f[i])) + } + return h +} + +// Returns 0 if recip succeeded; else -1 +// out = 1/(3*in) in Rq +func rqRecip3(in []small) ([]Fq, int) { + out := make([]Fq, p) + f := make([]Fq, p+1) + g := make([]Fq, p+1) + v := make([]Fq, p+1) + r := make([]Fq, p+1) + + var swap, t int + var f0, g0 int32 + + r[0] = fqRecip(3) + f[0] = 1 + f[p-1] = -1 + f[p] = -1 + + for i := 0; i < p; i++ { + g[p-1-i] = Fq(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 = internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0])) + delta ^= swap & (delta ^ -delta) + delta += 1 + + for i := 0; i < p+1; i++ { + t = swap & int(f[i]^g[i]) + f[i] ^= Fq(t) + g[i] ^= Fq(t) + t = swap & int(v[i]^r[i]) + v[i] ^= Fq(t) + r[i] ^= Fq(t) + } + + f0 = int32(f[0]) + g0 = int32(g[0]) + + for i := 0; i < p+1; i++ { + g[i] = fqFreeze(f0*int32(g[i]) - g0*int32(f[i])) + } + for i := 0; i < p+1; i++ { + r[i] = fqFreeze(f0*int32(r[i]) - g0*int32(v[i])) + } + + for i := 0; i < p; i++ { + g[i] = g[i+1] + } + g[p] = 0 + } + + scale := Fq(fqRecip(f[0])) + for i := 0; i < p; i++ { + out[i] = fqFreeze(int32(scale) * (int32)(v[p-1-i])) + } + + return out, internal.Int16_nonzero_mask(int16(delta)) + +} + +// Rounding all coefficients of a polynomial to the nearest multiple of 3 +// Rounded polynomials mod q +func round(a []Fq) []Fq { + out := make([]Fq, p) + for i := 0; i < p; i++ { + out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) + } + return out +} + +// Returns (min(x, y), max(x, y)), executes in constant time +func minmax(x, y *uint32) { + var xi uint32 = *x + var yi uint32 = *y + var xy uint32 = xi ^ yi + var c uint32 = yi - xi + c ^= xy & (c ^ yi ^ 0x80000000) + c >>= 31 + c = -c + c &= xy + *x = xi ^ c + *y = yi ^ c +} + +// Sorts the array of unsigned integers +func cryptoSortUint32(x []uint32, n int) { + if n < 2 { + return + } + top := 1 + + for top < n-top { + top += top + } + + for p := top; p > 0; p >>= 1 { + for i := 0; i < n-p; i++ { + if i&p == 0 { + minmax(&x[i], &x[i+p]) + } + } + for q := top; q > p; q >>= 1 { + for i := 0; i < n-q; i++ { + if i&p == 0 { + minmax(&x[i+p], &x[i+q]) + } + } + } + } +} + +// Sorting to generate short polynomial +func shortFromList(in []int32) []small { + out := make([]small, p) + L := make([]uint32, p) + + var neg2, neg3 int = -2, -3 + + for i := 0; i < w; i++ { + L[i] = uint32(in[i]) & uint32((neg2)) + } + + for i := w; i < p; i++ { + L[i] = (uint32(in[i]) & uint32((neg3))) | 1 + } + + cryptoSortUint32(L, p) + + for i := 0; i < p; i++ { + out[i] = small((L[i] & 3) - 1) + } + return out +} + +// Underlying hash function + +// The input byte array, in, is prepended by the byte b +// and its SHA-512 hash is calculated +// Only the first 32 bytes of the hash are returned +// e.g., b = 0 means out = Hash0(in) +func hashPrefix(out []byte, b int, in []byte, inlen int) { + x := make([]byte, inlen+1) + h := make([]byte, 64) + + x[0] = byte(b) + copy(x[1:], in) + + hash := sha512.New() + hash.Write([]byte(x)) + h = hash.Sum(nil) + + copy(out, h[:32]) + +} + +// Higher level randomness +// Returns a random unsigned integer +// A generator can be passed for deterministic number generation +func urandom32(gen *nist.DRBG) uint32 { + + c := make([]byte, 4) + var out [4]uint32 + + if gen != nil { + gen.Fill(c) + } else { + cryptoRand.Read(c) + + } + + 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] +} + +// Generates a random short polynomial +func shortRandom(gen *nist.DRBG) []small { + + L := make([]uint32, p) + + for i := 0; i < p; i++ { + L[i] = urandom32(gen) + } + + // Converts uint32 array to int32 array + L_int32 := make([]int32, p) + for i := 0; i < len(L); i++ { + L_int32[i] = int32(L[i]) + } + out := shortFromList(L_int32) + + return out + +} + +// Generates a random list of small +func smallRandom(gen *nist.DRBG) []small { + + out := make([]small, p) + for i := 0; i < p; i++ { + out[i] = small(((urandom32(gen)&0x3fffffff)*3)>>30) - 1 + } + return out +} + +// Streamlined NTRU Prime Core + +// h,(f,ginv) = keyGen() +func keyGen(gen *nist.DRBG) (h []Fq, f []small, ginv []small) { + g := make([]small, p) + var err int + for { + g = smallRandom(gen) + ginv, err = r3Recip(g) + if err == 0 { + break + } + + } + + f = shortRandom(gen) + + finv, _ := rqRecip3(f) /* always works */ + h = rqMultSmall(finv, g) + return h, f, ginv +} + +// c = encrypt(r,h) +func encrypt(r []small, h []Fq) []Fq { + + hr := rqMultSmall(h, r) + c := round(hr) + + return c + +} + +// r = decrypt(c,(f,ginv)) +func decrypt(c []Fq, f []small, ginv []small) []small { + r := make([]small, p) + cf := make([]Fq, p) + + cf = rqMultSmall(c, f) + cf3 := rqMult3(cf) + e := r3FromRq(cf3) + ev := r3Mult(e, ginv) + + mask := weightwMask(ev) /* 0 if weight w, else -1 */ + for i := 0; i < w; i++ { + r[i] = ((ev[i] ^ 1) & small(^mask)) ^ 1 + } + + for i := w; i < p; i++ { + r[i] = ev[i] & small(^mask) + } + return r + +} + +// Encoding small polynomials (including short polynomials) + +// Transform polynomial in R to bytes +// these are the only functions that rely on p mod 4 = 1 */ +func smallEncode(s []byte, f []small) { + var x small + var index int = 0 + for i := 0; i < p/4; i++ { + x = f[index] + 1 + index++ + + x += (f[index] + 1) << 2 + index++ + x += (f[index] + 1) << 4 + index++ + x += (f[index] + 1) << 6 + index++ + + s[0] = byte(x) + s = s[1:] + } + x = f[index] + 1 + + s[0] = byte(x) +} + +// Transform bytes into polynomial in R +func smallDecode(f []small, s []byte) { + var index int = 0 + var x byte + + for i := 0; i < p/4; i++ { + x = s[0] + s = s[1:] + + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + index++ + } + x = s[0] + f[index] = ((small)(x & 3)) - 1 +} + +// Encoding general polynomials + +// Transform polynomials in R/q to bytes +func rqEncode(s []byte, r []Fq) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16(r[i] + q12) + M[i] = q + } + internal.Encode(s, R, M, p) +} + +// Transform polynomials in R/q from bytes +func rqDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = q + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = ((Fq)(R[i])) - q12 + } + +} + +// Encoding rounded polynomials + +// Transform rounded polynomials to bytes +func roundedEncode(s []byte, r []Fq) { + + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16((int32((r[i])+q12) * 10923) >> 15) + M[i] = (q + 2) / 3 + } + internal.Encode(s, R, M, p) +} + +// Transform bytes to rounded polynomials +func roundedDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = (q + 2) / 3 + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = Fq(R[i]*3 - q12) + } + +} + +// Streamlined NTRU Prime Core plus encoding + +// Generates public key and private key +// pk,sk = zKeyGen() +func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { + + h, f, v := keyGen(gen) + + rqEncode(pk, h) + smallEncode(sk, f) + sk = sk[smallBytes:] + smallEncode(sk, v) + +} + +// C = zEncrypt(r,pk) +func zEncrypt(C []byte, r Inputs, pk []byte) { + h := make([]Fq, p) + rqDecode(h, pk) + c := encrypt(r[:], h) + roundedEncode(C, c) +} + +// r = zDecrypt(C,sk) +func zDecrypt(r *Inputs, C []byte, sk []byte) { + f := make([]small, p) + v := make([]small, p) + c := make([]Fq, p) + + smallDecode(f, sk) + sk = sk[smallBytes:] + smallDecode(v, sk) + roundedDecode(c, C) + + copy(r[:], decrypt(c, f, v)) +} + +// Confirmation hash + +// h = hashConfirm(r,pk,cache); cache is Hash4(pk) +func hashConfirm(h []byte, r []byte, pk []byte, cache []byte) { + x := make([]byte, hashBytes*2) + + hashPrefix(x, 3, r, inputsBytes) + + copy(x[hashBytes:], cache[:hashBytes]) + + hashPrefix(h, 2, x, len(x)) + +} + +// Session-key hash + +// k = hashSession(b,y,z) +func hashSession(k []byte, b int, y []byte, z []byte) { + x := make([]byte, hashBytes+ciphertextsBytes+confirmBytes) + + hashPrefix(x, 3, y, inputsBytes) + + copy(x[hashBytes:], z[:ciphertextsBytes+confirmBytes]) + + hashPrefix(k, b, x, len(x)) + +} + +// Streamlined NTRU Prime + +// pk,sk = kemKeyGen() +func kemKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { + zKeyGen(pk, sk, gen) + sk = sk[secretKeysBytes:] + + copy(sk, pk) + sk = sk[publicKeysBytes:] + + if gen != nil { + gen.Fill(sk[:inputsBytes]) + + } else { + cryptoRand.Read(sk[:inputsBytes]) + } + sk = sk[inputsBytes:] + hashPrefix(sk, 4, pk, publicKeysBytes) + +} + +// c,r_enc = hide(r,pk,cache); cache is Hash4(pk) +func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { + smallEncode(r_enc, r[:]) + zEncrypt(c, r, pk) + c = c[ciphertextsBytes:] + hashConfirm(c, r_enc, pk, cache) + +} + +// Takes as input a public key +// Returns ciphertext and shared key +// c,k = encap(pk) +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + pk := pub.pk[:] + + var r Inputs + r_enc := make([]byte, inputsBytes) + cache := make([]byte, hashBytes) + + hashPrefix(cache, 4, pk, publicKeysBytes) + copy(r[:], shortRandom(gen)) + hide(c, r_enc, r, pk, cache) + hashSession(k, 1, r_enc, c) + +} + +// Returns 0 if matching ciphertext+confirm, else -1 +func ciphertexts_diff_mask(c []byte, c2 []byte) int { + var differentbits uint16 = 0 + var len int = ciphertextsBytes + confirmBytes + + for i := 0; i < len; i++ { + differentbits |= uint16((c[i]) ^ (c2[i])) + } + return int((1 & ((differentbits - 1) >> 8)) - 1) + +} + +// Returns shared key from ciphertext and private key +// k = decap(c,sk) +func (priv *PrivateKey) DecapsulateTo(k []byte, c []byte) { + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + sk := priv.sk[:] + + pk := sk[secretKeysBytes:] + rho := pk[publicKeysBytes:] + cache := rho[inputsBytes:] + var r Inputs + + r_enc := make([]byte, inputsBytes) + cnew := make([]byte, ciphertextsBytes+confirmBytes) + + zDecrypt(&r, c, sk) + hide(cnew, r_enc, r, pk, cache) + var mask int = ciphertexts_diff_mask(c, cnew) + + for i := 0; i < inputsBytes; i++ { + r_enc[i] ^= byte(mask & int(r_enc[i]^rho[i])) + } + hashSession(k, 1+mask, r_enc, c) +} + +// The structure of the private key is given by the following segments: +// The secret key, the public key, entropy and the hash of the public key +type PrivateKey struct { + sk [PrivateKeySize]byte +} + +type PublicKey struct { + pk [PublicKeySize]byte +} + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "sntrup1013" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + copy(ret[:], sk.sk[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + return bytes.Equal(sk.sk[:], oth.sk[:]) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + return bytes.Equal(pk.pk[:], oth.pk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + var pk [PublicKeySize]byte + skey, _ := sk.MarshalBinary() + ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] + copy(pk[:], ppk[:]) + return &PublicKey{pk: pk} +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + copy(ret[:], pk.pk[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + kemKeyGen(pk[:], sk[:], nil) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk}, nil + +} + +func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + + kemKeyGen(pk[:], sk[:], gen) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk} +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, nil) + + return ct, ss, nil + +} + +func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DRBG) (ct, ss []byte, err error) { + + if gen == nil { + panic("A nist DRBG must be provided") + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, gen) + + return ct, ss, nil +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + ssk, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + ss := [SharedKeySize]byte{} + + ssk.DecapsulateTo(ss[:], ct) + + return ss[:], nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + pk := [PublicKeySize]byte{} + copy(pk[:], buf) + return &PublicKey{pk: pk}, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + sk := [PrivateKeySize]byte{} + copy(sk[:], buf) + return &PrivateKey{sk: sk}, nil +} diff --git a/kem/ntruprime/sntrup1277/ntruprime.go b/kem/ntruprime/sntrup1277/ntruprime.go new file mode 100644 index 000000000..561beee71 --- /dev/null +++ b/kem/ntruprime/sntrup1277/ntruprime.go @@ -0,0 +1,956 @@ +// Code generated from sntrup.templ.go. DO NOT EDIT. + +// Package sntrup1277 implements the IND-CCA2 secure key encapsulation mechanism +// sntrup1277 as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package sntrup1277 + +import ( + "bytes" + cryptoRand "crypto/rand" + "crypto/sha512" + + "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/kem/ntruprime/internal" + "github.com/cloudflare/circl/pke/ntruprime/kem" + ntrup "github.com/cloudflare/circl/pke/ntruprime/sntrup1277" +) + +type ( + small int8 + Fq int16 + Inputs [p]small +) + +const ( + p = ntrup.P + q = ntrup.Q + q12 = ((q - 1) / 2) + roundedBytes = ntrup.RoundedBytes + rqBytes = ntrup.RqBytes + w = ntrup.W + + hashBytes = 32 + + smallBytes = ((p + 3) / 4) + + inputsBytes = smallBytes + ciphertextsBytes = roundedBytes + secretKeysBytes = (2 * smallBytes) + publicKeysBytes = rqBytes + + confirmBytes = 32 +) + +const ( + // Size of the established shared key. + SharedKeySize = ntrup.SharedKeySize + + // Size of the encapsulated shared key. + CiphertextSize = ntrup.CiphertextSize + + // Size of a packed public key. + PublicKeySize = ntrup.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = ntrup.PrivateKeySize +) + +// Arithmetic operations over GF(3) + +// A polynomial of R has all of its coefficients in (-1,0,1) +// F3 is always represented as -1,0,1 +// so ZZ_fromF3 is a no-op + +// x must not be close to top int16 +func f3Freeze(x int16) small { + return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 +} + +// Arithmetic operations over GF(q) + +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +func fqFreeze(x int32) Fq { + return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) +} + +// Calculates reciprocal of Fq +func fqRecip(a1 Fq) Fq { + var i int = 1 + ai := a1 + + for i < (q - 2) { + ai = fqFreeze(int32(a1) * int32(ai)) + i += 1 + } + return ai +} + +// Returns 0 if the weight w is equal to r +// otherwise returns -1 +func weightwMask(r []small) int { + var weight int = 0 + + for i := 0; i < p; i++ { + weight += int(r[i]) & 1 + } + + // returns -1 if non zero + // otherwise returns 0 if weight==w + return internal.Int16_nonzero_mask(int16(weight - w)) + +} + +/* R3_fromR(R_fromRq(r)) */ +func r3FromRq(r []Fq) []small { + out := make([]small, p) + for i := 0; i < p; i++ { + out[i] = small(f3Freeze(int16(r[i]))) + } + return out +} + +// h = f*g in the ring R3 +func r3Mult(f []small, g []small) (h []small) { + h = make([]small, p) + fg := make([]small, p+p-1) + var result small + var i, j int + + for i = 0; i < p; i++ { + result = 0 + for j = 0; j <= i; j++ { + result = f3Freeze(int16(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 = f3Freeze(int16(result + f[j]*g[i-j])) + } + fg[i] = result + } + + for i = p + p - 2; i >= p; i-- { + fg[i-p] = f3Freeze(int16(fg[i-p] + fg[i])) + fg[i-p+1] = f3Freeze(int16(fg[i-p+1] + fg[i])) + } + + for i = 0; i < p; i++ { + h[i] = fg[i] + } + + return h + +} + +// Calculates the reciprocal of R3 polynomials +// Returns 0 if recip succeeded; else -1 +func r3Recip(in []small) ([]small, int) { + out := make([]small, p) + f := make([]small, p+1) + g := make([]small, p+1) + v := make([]small, p+1) + r := make([]small, p+1) + + var sign int + + r[0] = 1 + f[0] = 1 + + f[p-1] = -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 = int(-g[0] * f[0]) + var swap int = int(internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0]))) + delta ^= swap & int(delta^-delta) + delta += 1 + + for i := 0; i < p+1; i++ { + t := swap & int(f[i]^g[i]) + f[i] ^= small(t) + g[i] ^= small(t) + t = swap & int(v[i]^r[i]) + v[i] ^= small(t) + r[i] ^= small(t) + } + for i := 0; i < p+1; i++ { + g[i] = f3Freeze(int16(int(g[i]) + sign*int(f[i]))) + } + + for i := 0; i < p+1; i++ { + r[i] = f3Freeze(int16(int(r[i]) + sign*int(v[i]))) + } + + for i := 0; i < p; i++ { + g[i] = g[i+1] + } + + g[p] = 0 + + } + sign = int(f[0]) + + for i := 0; i < p; i++ { + + out[i] = small(sign * int(v[p-1-i])) + } + + return out, internal.Int16_nonzero_mask(int16(delta)) + +} + +// Polynomials mod q + +// h = f*g in the ring Rq */ +func rqMultSmall(f []Fq, g []small) (h []Fq) { + h = make([]Fq, p) + fg := make([]Fq, p+p-1) + var result Fq + + for i := 0; i < p; i++ { + result = 0 + for j := 0; j <= i; j++ { + result = fqFreeze(int32(result) + int32(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 = fqFreeze(int32(result) + int32(f[j])*(int32)(g[i-j])) + } + fg[i] = result + } + + for i := p + p - 2; i >= p; i-- { + fg[i-p] = fqFreeze(int32(fg[i-p] + fg[i])) + fg[i-p+1] = fqFreeze(int32(fg[i-p+1] + fg[i])) + + } + + for i := 0; i < p; i++ { + h[i] = fg[i] + } + return h +} + +// h = 3f in Rq +func rqMult3(f []Fq) (h []Fq) { + h = make([]Fq, p) + for i := 0; i < p; i++ { + h[i] = fqFreeze(int32(3 * f[i])) + } + return h +} + +// Returns 0 if recip succeeded; else -1 +// out = 1/(3*in) in Rq +func rqRecip3(in []small) ([]Fq, int) { + out := make([]Fq, p) + f := make([]Fq, p+1) + g := make([]Fq, p+1) + v := make([]Fq, p+1) + r := make([]Fq, p+1) + + var swap, t int + var f0, g0 int32 + + r[0] = fqRecip(3) + f[0] = 1 + f[p-1] = -1 + f[p] = -1 + + for i := 0; i < p; i++ { + g[p-1-i] = Fq(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 = internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0])) + delta ^= swap & (delta ^ -delta) + delta += 1 + + for i := 0; i < p+1; i++ { + t = swap & int(f[i]^g[i]) + f[i] ^= Fq(t) + g[i] ^= Fq(t) + t = swap & int(v[i]^r[i]) + v[i] ^= Fq(t) + r[i] ^= Fq(t) + } + + f0 = int32(f[0]) + g0 = int32(g[0]) + + for i := 0; i < p+1; i++ { + g[i] = fqFreeze(f0*int32(g[i]) - g0*int32(f[i])) + } + for i := 0; i < p+1; i++ { + r[i] = fqFreeze(f0*int32(r[i]) - g0*int32(v[i])) + } + + for i := 0; i < p; i++ { + g[i] = g[i+1] + } + g[p] = 0 + } + + scale := Fq(fqRecip(f[0])) + for i := 0; i < p; i++ { + out[i] = fqFreeze(int32(scale) * (int32)(v[p-1-i])) + } + + return out, internal.Int16_nonzero_mask(int16(delta)) + +} + +// Rounding all coefficients of a polynomial to the nearest multiple of 3 +// Rounded polynomials mod q +func round(a []Fq) []Fq { + out := make([]Fq, p) + for i := 0; i < p; i++ { + out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) + } + return out +} + +// Returns (min(x, y), max(x, y)), executes in constant time +func minmax(x, y *uint32) { + var xi uint32 = *x + var yi uint32 = *y + var xy uint32 = xi ^ yi + var c uint32 = yi - xi + c ^= xy & (c ^ yi ^ 0x80000000) + c >>= 31 + c = -c + c &= xy + *x = xi ^ c + *y = yi ^ c +} + +// Sorts the array of unsigned integers +func cryptoSortUint32(x []uint32, n int) { + if n < 2 { + return + } + top := 1 + + for top < n-top { + top += top + } + + for p := top; p > 0; p >>= 1 { + for i := 0; i < n-p; i++ { + if i&p == 0 { + minmax(&x[i], &x[i+p]) + } + } + for q := top; q > p; q >>= 1 { + for i := 0; i < n-q; i++ { + if i&p == 0 { + minmax(&x[i+p], &x[i+q]) + } + } + } + } +} + +// Sorting to generate short polynomial +func shortFromList(in []int32) []small { + out := make([]small, p) + L := make([]uint32, p) + + var neg2, neg3 int = -2, -3 + + for i := 0; i < w; i++ { + L[i] = uint32(in[i]) & uint32((neg2)) + } + + for i := w; i < p; i++ { + L[i] = (uint32(in[i]) & uint32((neg3))) | 1 + } + + cryptoSortUint32(L, p) + + for i := 0; i < p; i++ { + out[i] = small((L[i] & 3) - 1) + } + return out +} + +// Underlying hash function + +// The input byte array, in, is prepended by the byte b +// and its SHA-512 hash is calculated +// Only the first 32 bytes of the hash are returned +// e.g., b = 0 means out = Hash0(in) +func hashPrefix(out []byte, b int, in []byte, inlen int) { + x := make([]byte, inlen+1) + h := make([]byte, 64) + + x[0] = byte(b) + copy(x[1:], in) + + hash := sha512.New() + hash.Write([]byte(x)) + h = hash.Sum(nil) + + copy(out, h[:32]) + +} + +// Higher level randomness +// Returns a random unsigned integer +// A generator can be passed for deterministic number generation +func urandom32(gen *nist.DRBG) uint32 { + + c := make([]byte, 4) + var out [4]uint32 + + if gen != nil { + gen.Fill(c) + } else { + cryptoRand.Read(c) + + } + + 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] +} + +// Generates a random short polynomial +func shortRandom(gen *nist.DRBG) []small { + + L := make([]uint32, p) + + for i := 0; i < p; i++ { + L[i] = urandom32(gen) + } + + // Converts uint32 array to int32 array + L_int32 := make([]int32, p) + for i := 0; i < len(L); i++ { + L_int32[i] = int32(L[i]) + } + out := shortFromList(L_int32) + + return out + +} + +// Generates a random list of small +func smallRandom(gen *nist.DRBG) []small { + + out := make([]small, p) + for i := 0; i < p; i++ { + out[i] = small(((urandom32(gen)&0x3fffffff)*3)>>30) - 1 + } + return out +} + +// Streamlined NTRU Prime Core + +// h,(f,ginv) = keyGen() +func keyGen(gen *nist.DRBG) (h []Fq, f []small, ginv []small) { + g := make([]small, p) + var err int + for { + g = smallRandom(gen) + ginv, err = r3Recip(g) + if err == 0 { + break + } + + } + + f = shortRandom(gen) + + finv, _ := rqRecip3(f) /* always works */ + h = rqMultSmall(finv, g) + return h, f, ginv +} + +// c = encrypt(r,h) +func encrypt(r []small, h []Fq) []Fq { + + hr := rqMultSmall(h, r) + c := round(hr) + + return c + +} + +// r = decrypt(c,(f,ginv)) +func decrypt(c []Fq, f []small, ginv []small) []small { + r := make([]small, p) + cf := make([]Fq, p) + + cf = rqMultSmall(c, f) + cf3 := rqMult3(cf) + e := r3FromRq(cf3) + ev := r3Mult(e, ginv) + + mask := weightwMask(ev) /* 0 if weight w, else -1 */ + for i := 0; i < w; i++ { + r[i] = ((ev[i] ^ 1) & small(^mask)) ^ 1 + } + + for i := w; i < p; i++ { + r[i] = ev[i] & small(^mask) + } + return r + +} + +// Encoding small polynomials (including short polynomials) + +// Transform polynomial in R to bytes +// these are the only functions that rely on p mod 4 = 1 */ +func smallEncode(s []byte, f []small) { + var x small + var index int = 0 + for i := 0; i < p/4; i++ { + x = f[index] + 1 + index++ + + x += (f[index] + 1) << 2 + index++ + x += (f[index] + 1) << 4 + index++ + x += (f[index] + 1) << 6 + index++ + + s[0] = byte(x) + s = s[1:] + } + x = f[index] + 1 + + s[0] = byte(x) +} + +// Transform bytes into polynomial in R +func smallDecode(f []small, s []byte) { + var index int = 0 + var x byte + + for i := 0; i < p/4; i++ { + x = s[0] + s = s[1:] + + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + index++ + } + x = s[0] + f[index] = ((small)(x & 3)) - 1 +} + +// Encoding general polynomials + +// Transform polynomials in R/q to bytes +func rqEncode(s []byte, r []Fq) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16(r[i] + q12) + M[i] = q + } + internal.Encode(s, R, M, p) +} + +// Transform polynomials in R/q from bytes +func rqDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = q + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = ((Fq)(R[i])) - q12 + } + +} + +// Encoding rounded polynomials + +// Transform rounded polynomials to bytes +func roundedEncode(s []byte, r []Fq) { + + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16((int32((r[i])+q12) * 10923) >> 15) + M[i] = (q + 2) / 3 + } + internal.Encode(s, R, M, p) +} + +// Transform bytes to rounded polynomials +func roundedDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = (q + 2) / 3 + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = Fq(R[i]*3 - q12) + } + +} + +// Streamlined NTRU Prime Core plus encoding + +// Generates public key and private key +// pk,sk = zKeyGen() +func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { + + h, f, v := keyGen(gen) + + rqEncode(pk, h) + smallEncode(sk, f) + sk = sk[smallBytes:] + smallEncode(sk, v) + +} + +// C = zEncrypt(r,pk) +func zEncrypt(C []byte, r Inputs, pk []byte) { + h := make([]Fq, p) + rqDecode(h, pk) + c := encrypt(r[:], h) + roundedEncode(C, c) +} + +// r = zDecrypt(C,sk) +func zDecrypt(r *Inputs, C []byte, sk []byte) { + f := make([]small, p) + v := make([]small, p) + c := make([]Fq, p) + + smallDecode(f, sk) + sk = sk[smallBytes:] + smallDecode(v, sk) + roundedDecode(c, C) + + copy(r[:], decrypt(c, f, v)) +} + +// Confirmation hash + +// h = hashConfirm(r,pk,cache); cache is Hash4(pk) +func hashConfirm(h []byte, r []byte, pk []byte, cache []byte) { + x := make([]byte, hashBytes*2) + + hashPrefix(x, 3, r, inputsBytes) + + copy(x[hashBytes:], cache[:hashBytes]) + + hashPrefix(h, 2, x, len(x)) + +} + +// Session-key hash + +// k = hashSession(b,y,z) +func hashSession(k []byte, b int, y []byte, z []byte) { + x := make([]byte, hashBytes+ciphertextsBytes+confirmBytes) + + hashPrefix(x, 3, y, inputsBytes) + + copy(x[hashBytes:], z[:ciphertextsBytes+confirmBytes]) + + hashPrefix(k, b, x, len(x)) + +} + +// Streamlined NTRU Prime + +// pk,sk = kemKeyGen() +func kemKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { + zKeyGen(pk, sk, gen) + sk = sk[secretKeysBytes:] + + copy(sk, pk) + sk = sk[publicKeysBytes:] + + if gen != nil { + gen.Fill(sk[:inputsBytes]) + + } else { + cryptoRand.Read(sk[:inputsBytes]) + } + sk = sk[inputsBytes:] + hashPrefix(sk, 4, pk, publicKeysBytes) + +} + +// c,r_enc = hide(r,pk,cache); cache is Hash4(pk) +func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { + smallEncode(r_enc, r[:]) + zEncrypt(c, r, pk) + c = c[ciphertextsBytes:] + hashConfirm(c, r_enc, pk, cache) + +} + +// Takes as input a public key +// Returns ciphertext and shared key +// c,k = encap(pk) +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + pk := pub.pk[:] + + var r Inputs + r_enc := make([]byte, inputsBytes) + cache := make([]byte, hashBytes) + + hashPrefix(cache, 4, pk, publicKeysBytes) + copy(r[:], shortRandom(gen)) + hide(c, r_enc, r, pk, cache) + hashSession(k, 1, r_enc, c) + +} + +// Returns 0 if matching ciphertext+confirm, else -1 +func ciphertexts_diff_mask(c []byte, c2 []byte) int { + var differentbits uint16 = 0 + var len int = ciphertextsBytes + confirmBytes + + for i := 0; i < len; i++ { + differentbits |= uint16((c[i]) ^ (c2[i])) + } + return int((1 & ((differentbits - 1) >> 8)) - 1) + +} + +// Returns shared key from ciphertext and private key +// k = decap(c,sk) +func (priv *PrivateKey) DecapsulateTo(k []byte, c []byte) { + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + sk := priv.sk[:] + + pk := sk[secretKeysBytes:] + rho := pk[publicKeysBytes:] + cache := rho[inputsBytes:] + var r Inputs + + r_enc := make([]byte, inputsBytes) + cnew := make([]byte, ciphertextsBytes+confirmBytes) + + zDecrypt(&r, c, sk) + hide(cnew, r_enc, r, pk, cache) + var mask int = ciphertexts_diff_mask(c, cnew) + + for i := 0; i < inputsBytes; i++ { + r_enc[i] ^= byte(mask & int(r_enc[i]^rho[i])) + } + hashSession(k, 1+mask, r_enc, c) +} + +// The structure of the private key is given by the following segments: +// The secret key, the public key, entropy and the hash of the public key +type PrivateKey struct { + sk [PrivateKeySize]byte +} + +type PublicKey struct { + pk [PublicKeySize]byte +} + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "sntrup1277" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + copy(ret[:], sk.sk[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + return bytes.Equal(sk.sk[:], oth.sk[:]) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + return bytes.Equal(pk.pk[:], oth.pk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + var pk [PublicKeySize]byte + skey, _ := sk.MarshalBinary() + ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] + copy(pk[:], ppk[:]) + return &PublicKey{pk: pk} +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + copy(ret[:], pk.pk[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + kemKeyGen(pk[:], sk[:], nil) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk}, nil + +} + +func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + + kemKeyGen(pk[:], sk[:], gen) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk} +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, nil) + + return ct, ss, nil + +} + +func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DRBG) (ct, ss []byte, err error) { + + if gen == nil { + panic("A nist DRBG must be provided") + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, gen) + + return ct, ss, nil +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + ssk, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + ss := [SharedKeySize]byte{} + + ssk.DecapsulateTo(ss[:], ct) + + return ss[:], nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + pk := [PublicKeySize]byte{} + copy(pk[:], buf) + return &PublicKey{pk: pk}, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + sk := [PrivateKeySize]byte{} + copy(sk[:], buf) + return &PrivateKey{sk: sk}, nil +} diff --git a/kem/ntruprime/sntrup653/ntruprime.go b/kem/ntruprime/sntrup653/ntruprime.go new file mode 100644 index 000000000..203a6ea4f --- /dev/null +++ b/kem/ntruprime/sntrup653/ntruprime.go @@ -0,0 +1,956 @@ +// Code generated from sntrup.templ.go. DO NOT EDIT. + +// Package sntrup653 implements the IND-CCA2 secure key encapsulation mechanism +// sntrup653 as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package sntrup653 + +import ( + "bytes" + cryptoRand "crypto/rand" + "crypto/sha512" + + "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/kem/ntruprime/internal" + "github.com/cloudflare/circl/pke/ntruprime/kem" + ntrup "github.com/cloudflare/circl/pke/ntruprime/sntrup653" +) + +type ( + small int8 + Fq int16 + Inputs [p]small +) + +const ( + p = ntrup.P + q = ntrup.Q + q12 = ((q - 1) / 2) + roundedBytes = ntrup.RoundedBytes + rqBytes = ntrup.RqBytes + w = ntrup.W + + hashBytes = 32 + + smallBytes = ((p + 3) / 4) + + inputsBytes = smallBytes + ciphertextsBytes = roundedBytes + secretKeysBytes = (2 * smallBytes) + publicKeysBytes = rqBytes + + confirmBytes = 32 +) + +const ( + // Size of the established shared key. + SharedKeySize = ntrup.SharedKeySize + + // Size of the encapsulated shared key. + CiphertextSize = ntrup.CiphertextSize + + // Size of a packed public key. + PublicKeySize = ntrup.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = ntrup.PrivateKeySize +) + +// Arithmetic operations over GF(3) + +// A polynomial of R has all of its coefficients in (-1,0,1) +// F3 is always represented as -1,0,1 +// so ZZ_fromF3 is a no-op + +// x must not be close to top int16 +func f3Freeze(x int16) small { + return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 +} + +// Arithmetic operations over GF(q) + +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +func fqFreeze(x int32) Fq { + return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) +} + +// Calculates reciprocal of Fq +func fqRecip(a1 Fq) Fq { + var i int = 1 + ai := a1 + + for i < (q - 2) { + ai = fqFreeze(int32(a1) * int32(ai)) + i += 1 + } + return ai +} + +// Returns 0 if the weight w is equal to r +// otherwise returns -1 +func weightwMask(r []small) int { + var weight int = 0 + + for i := 0; i < p; i++ { + weight += int(r[i]) & 1 + } + + // returns -1 if non zero + // otherwise returns 0 if weight==w + return internal.Int16_nonzero_mask(int16(weight - w)) + +} + +/* R3_fromR(R_fromRq(r)) */ +func r3FromRq(r []Fq) []small { + out := make([]small, p) + for i := 0; i < p; i++ { + out[i] = small(f3Freeze(int16(r[i]))) + } + return out +} + +// h = f*g in the ring R3 +func r3Mult(f []small, g []small) (h []small) { + h = make([]small, p) + fg := make([]small, p+p-1) + var result small + var i, j int + + for i = 0; i < p; i++ { + result = 0 + for j = 0; j <= i; j++ { + result = f3Freeze(int16(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 = f3Freeze(int16(result + f[j]*g[i-j])) + } + fg[i] = result + } + + for i = p + p - 2; i >= p; i-- { + fg[i-p] = f3Freeze(int16(fg[i-p] + fg[i])) + fg[i-p+1] = f3Freeze(int16(fg[i-p+1] + fg[i])) + } + + for i = 0; i < p; i++ { + h[i] = fg[i] + } + + return h + +} + +// Calculates the reciprocal of R3 polynomials +// Returns 0 if recip succeeded; else -1 +func r3Recip(in []small) ([]small, int) { + out := make([]small, p) + f := make([]small, p+1) + g := make([]small, p+1) + v := make([]small, p+1) + r := make([]small, p+1) + + var sign int + + r[0] = 1 + f[0] = 1 + + f[p-1] = -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 = int(-g[0] * f[0]) + var swap int = int(internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0]))) + delta ^= swap & int(delta^-delta) + delta += 1 + + for i := 0; i < p+1; i++ { + t := swap & int(f[i]^g[i]) + f[i] ^= small(t) + g[i] ^= small(t) + t = swap & int(v[i]^r[i]) + v[i] ^= small(t) + r[i] ^= small(t) + } + for i := 0; i < p+1; i++ { + g[i] = f3Freeze(int16(int(g[i]) + sign*int(f[i]))) + } + + for i := 0; i < p+1; i++ { + r[i] = f3Freeze(int16(int(r[i]) + sign*int(v[i]))) + } + + for i := 0; i < p; i++ { + g[i] = g[i+1] + } + + g[p] = 0 + + } + sign = int(f[0]) + + for i := 0; i < p; i++ { + + out[i] = small(sign * int(v[p-1-i])) + } + + return out, internal.Int16_nonzero_mask(int16(delta)) + +} + +// Polynomials mod q + +// h = f*g in the ring Rq */ +func rqMultSmall(f []Fq, g []small) (h []Fq) { + h = make([]Fq, p) + fg := make([]Fq, p+p-1) + var result Fq + + for i := 0; i < p; i++ { + result = 0 + for j := 0; j <= i; j++ { + result = fqFreeze(int32(result) + int32(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 = fqFreeze(int32(result) + int32(f[j])*(int32)(g[i-j])) + } + fg[i] = result + } + + for i := p + p - 2; i >= p; i-- { + fg[i-p] = fqFreeze(int32(fg[i-p] + fg[i])) + fg[i-p+1] = fqFreeze(int32(fg[i-p+1] + fg[i])) + + } + + for i := 0; i < p; i++ { + h[i] = fg[i] + } + return h +} + +// h = 3f in Rq +func rqMult3(f []Fq) (h []Fq) { + h = make([]Fq, p) + for i := 0; i < p; i++ { + h[i] = fqFreeze(int32(3 * f[i])) + } + return h +} + +// Returns 0 if recip succeeded; else -1 +// out = 1/(3*in) in Rq +func rqRecip3(in []small) ([]Fq, int) { + out := make([]Fq, p) + f := make([]Fq, p+1) + g := make([]Fq, p+1) + v := make([]Fq, p+1) + r := make([]Fq, p+1) + + var swap, t int + var f0, g0 int32 + + r[0] = fqRecip(3) + f[0] = 1 + f[p-1] = -1 + f[p] = -1 + + for i := 0; i < p; i++ { + g[p-1-i] = Fq(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 = internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0])) + delta ^= swap & (delta ^ -delta) + delta += 1 + + for i := 0; i < p+1; i++ { + t = swap & int(f[i]^g[i]) + f[i] ^= Fq(t) + g[i] ^= Fq(t) + t = swap & int(v[i]^r[i]) + v[i] ^= Fq(t) + r[i] ^= Fq(t) + } + + f0 = int32(f[0]) + g0 = int32(g[0]) + + for i := 0; i < p+1; i++ { + g[i] = fqFreeze(f0*int32(g[i]) - g0*int32(f[i])) + } + for i := 0; i < p+1; i++ { + r[i] = fqFreeze(f0*int32(r[i]) - g0*int32(v[i])) + } + + for i := 0; i < p; i++ { + g[i] = g[i+1] + } + g[p] = 0 + } + + scale := Fq(fqRecip(f[0])) + for i := 0; i < p; i++ { + out[i] = fqFreeze(int32(scale) * (int32)(v[p-1-i])) + } + + return out, internal.Int16_nonzero_mask(int16(delta)) + +} + +// Rounding all coefficients of a polynomial to the nearest multiple of 3 +// Rounded polynomials mod q +func round(a []Fq) []Fq { + out := make([]Fq, p) + for i := 0; i < p; i++ { + out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) + } + return out +} + +// Returns (min(x, y), max(x, y)), executes in constant time +func minmax(x, y *uint32) { + var xi uint32 = *x + var yi uint32 = *y + var xy uint32 = xi ^ yi + var c uint32 = yi - xi + c ^= xy & (c ^ yi ^ 0x80000000) + c >>= 31 + c = -c + c &= xy + *x = xi ^ c + *y = yi ^ c +} + +// Sorts the array of unsigned integers +func cryptoSortUint32(x []uint32, n int) { + if n < 2 { + return + } + top := 1 + + for top < n-top { + top += top + } + + for p := top; p > 0; p >>= 1 { + for i := 0; i < n-p; i++ { + if i&p == 0 { + minmax(&x[i], &x[i+p]) + } + } + for q := top; q > p; q >>= 1 { + for i := 0; i < n-q; i++ { + if i&p == 0 { + minmax(&x[i+p], &x[i+q]) + } + } + } + } +} + +// Sorting to generate short polynomial +func shortFromList(in []int32) []small { + out := make([]small, p) + L := make([]uint32, p) + + var neg2, neg3 int = -2, -3 + + for i := 0; i < w; i++ { + L[i] = uint32(in[i]) & uint32((neg2)) + } + + for i := w; i < p; i++ { + L[i] = (uint32(in[i]) & uint32((neg3))) | 1 + } + + cryptoSortUint32(L, p) + + for i := 0; i < p; i++ { + out[i] = small((L[i] & 3) - 1) + } + return out +} + +// Underlying hash function + +// The input byte array, in, is prepended by the byte b +// and its SHA-512 hash is calculated +// Only the first 32 bytes of the hash are returned +// e.g., b = 0 means out = Hash0(in) +func hashPrefix(out []byte, b int, in []byte, inlen int) { + x := make([]byte, inlen+1) + h := make([]byte, 64) + + x[0] = byte(b) + copy(x[1:], in) + + hash := sha512.New() + hash.Write([]byte(x)) + h = hash.Sum(nil) + + copy(out, h[:32]) + +} + +// Higher level randomness +// Returns a random unsigned integer +// A generator can be passed for deterministic number generation +func urandom32(gen *nist.DRBG) uint32 { + + c := make([]byte, 4) + var out [4]uint32 + + if gen != nil { + gen.Fill(c) + } else { + cryptoRand.Read(c) + + } + + 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] +} + +// Generates a random short polynomial +func shortRandom(gen *nist.DRBG) []small { + + L := make([]uint32, p) + + for i := 0; i < p; i++ { + L[i] = urandom32(gen) + } + + // Converts uint32 array to int32 array + L_int32 := make([]int32, p) + for i := 0; i < len(L); i++ { + L_int32[i] = int32(L[i]) + } + out := shortFromList(L_int32) + + return out + +} + +// Generates a random list of small +func smallRandom(gen *nist.DRBG) []small { + + out := make([]small, p) + for i := 0; i < p; i++ { + out[i] = small(((urandom32(gen)&0x3fffffff)*3)>>30) - 1 + } + return out +} + +// Streamlined NTRU Prime Core + +// h,(f,ginv) = keyGen() +func keyGen(gen *nist.DRBG) (h []Fq, f []small, ginv []small) { + g := make([]small, p) + var err int + for { + g = smallRandom(gen) + ginv, err = r3Recip(g) + if err == 0 { + break + } + + } + + f = shortRandom(gen) + + finv, _ := rqRecip3(f) /* always works */ + h = rqMultSmall(finv, g) + return h, f, ginv +} + +// c = encrypt(r,h) +func encrypt(r []small, h []Fq) []Fq { + + hr := rqMultSmall(h, r) + c := round(hr) + + return c + +} + +// r = decrypt(c,(f,ginv)) +func decrypt(c []Fq, f []small, ginv []small) []small { + r := make([]small, p) + cf := make([]Fq, p) + + cf = rqMultSmall(c, f) + cf3 := rqMult3(cf) + e := r3FromRq(cf3) + ev := r3Mult(e, ginv) + + mask := weightwMask(ev) /* 0 if weight w, else -1 */ + for i := 0; i < w; i++ { + r[i] = ((ev[i] ^ 1) & small(^mask)) ^ 1 + } + + for i := w; i < p; i++ { + r[i] = ev[i] & small(^mask) + } + return r + +} + +// Encoding small polynomials (including short polynomials) + +// Transform polynomial in R to bytes +// these are the only functions that rely on p mod 4 = 1 */ +func smallEncode(s []byte, f []small) { + var x small + var index int = 0 + for i := 0; i < p/4; i++ { + x = f[index] + 1 + index++ + + x += (f[index] + 1) << 2 + index++ + x += (f[index] + 1) << 4 + index++ + x += (f[index] + 1) << 6 + index++ + + s[0] = byte(x) + s = s[1:] + } + x = f[index] + 1 + + s[0] = byte(x) +} + +// Transform bytes into polynomial in R +func smallDecode(f []small, s []byte) { + var index int = 0 + var x byte + + for i := 0; i < p/4; i++ { + x = s[0] + s = s[1:] + + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + index++ + } + x = s[0] + f[index] = ((small)(x & 3)) - 1 +} + +// Encoding general polynomials + +// Transform polynomials in R/q to bytes +func rqEncode(s []byte, r []Fq) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16(r[i] + q12) + M[i] = q + } + internal.Encode(s, R, M, p) +} + +// Transform polynomials in R/q from bytes +func rqDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = q + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = ((Fq)(R[i])) - q12 + } + +} + +// Encoding rounded polynomials + +// Transform rounded polynomials to bytes +func roundedEncode(s []byte, r []Fq) { + + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16((int32((r[i])+q12) * 10923) >> 15) + M[i] = (q + 2) / 3 + } + internal.Encode(s, R, M, p) +} + +// Transform bytes to rounded polynomials +func roundedDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = (q + 2) / 3 + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = Fq(R[i]*3 - q12) + } + +} + +// Streamlined NTRU Prime Core plus encoding + +// Generates public key and private key +// pk,sk = zKeyGen() +func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { + + h, f, v := keyGen(gen) + + rqEncode(pk, h) + smallEncode(sk, f) + sk = sk[smallBytes:] + smallEncode(sk, v) + +} + +// C = zEncrypt(r,pk) +func zEncrypt(C []byte, r Inputs, pk []byte) { + h := make([]Fq, p) + rqDecode(h, pk) + c := encrypt(r[:], h) + roundedEncode(C, c) +} + +// r = zDecrypt(C,sk) +func zDecrypt(r *Inputs, C []byte, sk []byte) { + f := make([]small, p) + v := make([]small, p) + c := make([]Fq, p) + + smallDecode(f, sk) + sk = sk[smallBytes:] + smallDecode(v, sk) + roundedDecode(c, C) + + copy(r[:], decrypt(c, f, v)) +} + +// Confirmation hash + +// h = hashConfirm(r,pk,cache); cache is Hash4(pk) +func hashConfirm(h []byte, r []byte, pk []byte, cache []byte) { + x := make([]byte, hashBytes*2) + + hashPrefix(x, 3, r, inputsBytes) + + copy(x[hashBytes:], cache[:hashBytes]) + + hashPrefix(h, 2, x, len(x)) + +} + +// Session-key hash + +// k = hashSession(b,y,z) +func hashSession(k []byte, b int, y []byte, z []byte) { + x := make([]byte, hashBytes+ciphertextsBytes+confirmBytes) + + hashPrefix(x, 3, y, inputsBytes) + + copy(x[hashBytes:], z[:ciphertextsBytes+confirmBytes]) + + hashPrefix(k, b, x, len(x)) + +} + +// Streamlined NTRU Prime + +// pk,sk = kemKeyGen() +func kemKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { + zKeyGen(pk, sk, gen) + sk = sk[secretKeysBytes:] + + copy(sk, pk) + sk = sk[publicKeysBytes:] + + if gen != nil { + gen.Fill(sk[:inputsBytes]) + + } else { + cryptoRand.Read(sk[:inputsBytes]) + } + sk = sk[inputsBytes:] + hashPrefix(sk, 4, pk, publicKeysBytes) + +} + +// c,r_enc = hide(r,pk,cache); cache is Hash4(pk) +func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { + smallEncode(r_enc, r[:]) + zEncrypt(c, r, pk) + c = c[ciphertextsBytes:] + hashConfirm(c, r_enc, pk, cache) + +} + +// Takes as input a public key +// Returns ciphertext and shared key +// c,k = encap(pk) +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + pk := pub.pk[:] + + var r Inputs + r_enc := make([]byte, inputsBytes) + cache := make([]byte, hashBytes) + + hashPrefix(cache, 4, pk, publicKeysBytes) + copy(r[:], shortRandom(gen)) + hide(c, r_enc, r, pk, cache) + hashSession(k, 1, r_enc, c) + +} + +// Returns 0 if matching ciphertext+confirm, else -1 +func ciphertexts_diff_mask(c []byte, c2 []byte) int { + var differentbits uint16 = 0 + var len int = ciphertextsBytes + confirmBytes + + for i := 0; i < len; i++ { + differentbits |= uint16((c[i]) ^ (c2[i])) + } + return int((1 & ((differentbits - 1) >> 8)) - 1) + +} + +// Returns shared key from ciphertext and private key +// k = decap(c,sk) +func (priv *PrivateKey) DecapsulateTo(k []byte, c []byte) { + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + sk := priv.sk[:] + + pk := sk[secretKeysBytes:] + rho := pk[publicKeysBytes:] + cache := rho[inputsBytes:] + var r Inputs + + r_enc := make([]byte, inputsBytes) + cnew := make([]byte, ciphertextsBytes+confirmBytes) + + zDecrypt(&r, c, sk) + hide(cnew, r_enc, r, pk, cache) + var mask int = ciphertexts_diff_mask(c, cnew) + + for i := 0; i < inputsBytes; i++ { + r_enc[i] ^= byte(mask & int(r_enc[i]^rho[i])) + } + hashSession(k, 1+mask, r_enc, c) +} + +// The structure of the private key is given by the following segments: +// The secret key, the public key, entropy and the hash of the public key +type PrivateKey struct { + sk [PrivateKeySize]byte +} + +type PublicKey struct { + pk [PublicKeySize]byte +} + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "sntrup653" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + copy(ret[:], sk.sk[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + return bytes.Equal(sk.sk[:], oth.sk[:]) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + return bytes.Equal(pk.pk[:], oth.pk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + var pk [PublicKeySize]byte + skey, _ := sk.MarshalBinary() + ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] + copy(pk[:], ppk[:]) + return &PublicKey{pk: pk} +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + copy(ret[:], pk.pk[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + kemKeyGen(pk[:], sk[:], nil) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk}, nil + +} + +func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + + kemKeyGen(pk[:], sk[:], gen) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk} +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, nil) + + return ct, ss, nil + +} + +func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DRBG) (ct, ss []byte, err error) { + + if gen == nil { + panic("A nist DRBG must be provided") + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, gen) + + return ct, ss, nil +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + ssk, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + ss := [SharedKeySize]byte{} + + ssk.DecapsulateTo(ss[:], ct) + + return ss[:], nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + pk := [PublicKeySize]byte{} + copy(pk[:], buf) + return &PublicKey{pk: pk}, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + sk := [PrivateKeySize]byte{} + copy(sk[:], buf) + return &PrivateKey{sk: sk}, nil +} diff --git a/kem/ntruprime/sntrup761/ntruprime.go b/kem/ntruprime/sntrup761/ntruprime.go new file mode 100644 index 000000000..05521828c --- /dev/null +++ b/kem/ntruprime/sntrup761/ntruprime.go @@ -0,0 +1,956 @@ +// Code generated from sntrup.templ.go. DO NOT EDIT. + +// Package sntrup761 implements the IND-CCA2 secure key encapsulation mechanism +// sntrup761 as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package sntrup761 + +import ( + "bytes" + cryptoRand "crypto/rand" + "crypto/sha512" + + "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/kem/ntruprime/internal" + "github.com/cloudflare/circl/pke/ntruprime/kem" + ntrup "github.com/cloudflare/circl/pke/ntruprime/sntrup761" +) + +type ( + small int8 + Fq int16 + Inputs [p]small +) + +const ( + p = ntrup.P + q = ntrup.Q + q12 = ((q - 1) / 2) + roundedBytes = ntrup.RoundedBytes + rqBytes = ntrup.RqBytes + w = ntrup.W + + hashBytes = 32 + + smallBytes = ((p + 3) / 4) + + inputsBytes = smallBytes + ciphertextsBytes = roundedBytes + secretKeysBytes = (2 * smallBytes) + publicKeysBytes = rqBytes + + confirmBytes = 32 +) + +const ( + // Size of the established shared key. + SharedKeySize = ntrup.SharedKeySize + + // Size of the encapsulated shared key. + CiphertextSize = ntrup.CiphertextSize + + // Size of a packed public key. + PublicKeySize = ntrup.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = ntrup.PrivateKeySize +) + +// Arithmetic operations over GF(3) + +// A polynomial of R has all of its coefficients in (-1,0,1) +// F3 is always represented as -1,0,1 +// so ZZ_fromF3 is a no-op + +// x must not be close to top int16 +func f3Freeze(x int16) small { + return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 +} + +// Arithmetic operations over GF(q) + +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +func fqFreeze(x int32) Fq { + return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) +} + +// Calculates reciprocal of Fq +func fqRecip(a1 Fq) Fq { + var i int = 1 + ai := a1 + + for i < (q - 2) { + ai = fqFreeze(int32(a1) * int32(ai)) + i += 1 + } + return ai +} + +// Returns 0 if the weight w is equal to r +// otherwise returns -1 +func weightwMask(r []small) int { + var weight int = 0 + + for i := 0; i < p; i++ { + weight += int(r[i]) & 1 + } + + // returns -1 if non zero + // otherwise returns 0 if weight==w + return internal.Int16_nonzero_mask(int16(weight - w)) + +} + +/* R3_fromR(R_fromRq(r)) */ +func r3FromRq(r []Fq) []small { + out := make([]small, p) + for i := 0; i < p; i++ { + out[i] = small(f3Freeze(int16(r[i]))) + } + return out +} + +// h = f*g in the ring R3 +func r3Mult(f []small, g []small) (h []small) { + h = make([]small, p) + fg := make([]small, p+p-1) + var result small + var i, j int + + for i = 0; i < p; i++ { + result = 0 + for j = 0; j <= i; j++ { + result = f3Freeze(int16(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 = f3Freeze(int16(result + f[j]*g[i-j])) + } + fg[i] = result + } + + for i = p + p - 2; i >= p; i-- { + fg[i-p] = f3Freeze(int16(fg[i-p] + fg[i])) + fg[i-p+1] = f3Freeze(int16(fg[i-p+1] + fg[i])) + } + + for i = 0; i < p; i++ { + h[i] = fg[i] + } + + return h + +} + +// Calculates the reciprocal of R3 polynomials +// Returns 0 if recip succeeded; else -1 +func r3Recip(in []small) ([]small, int) { + out := make([]small, p) + f := make([]small, p+1) + g := make([]small, p+1) + v := make([]small, p+1) + r := make([]small, p+1) + + var sign int + + r[0] = 1 + f[0] = 1 + + f[p-1] = -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 = int(-g[0] * f[0]) + var swap int = int(internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0]))) + delta ^= swap & int(delta^-delta) + delta += 1 + + for i := 0; i < p+1; i++ { + t := swap & int(f[i]^g[i]) + f[i] ^= small(t) + g[i] ^= small(t) + t = swap & int(v[i]^r[i]) + v[i] ^= small(t) + r[i] ^= small(t) + } + for i := 0; i < p+1; i++ { + g[i] = f3Freeze(int16(int(g[i]) + sign*int(f[i]))) + } + + for i := 0; i < p+1; i++ { + r[i] = f3Freeze(int16(int(r[i]) + sign*int(v[i]))) + } + + for i := 0; i < p; i++ { + g[i] = g[i+1] + } + + g[p] = 0 + + } + sign = int(f[0]) + + for i := 0; i < p; i++ { + + out[i] = small(sign * int(v[p-1-i])) + } + + return out, internal.Int16_nonzero_mask(int16(delta)) + +} + +// Polynomials mod q + +// h = f*g in the ring Rq */ +func rqMultSmall(f []Fq, g []small) (h []Fq) { + h = make([]Fq, p) + fg := make([]Fq, p+p-1) + var result Fq + + for i := 0; i < p; i++ { + result = 0 + for j := 0; j <= i; j++ { + result = fqFreeze(int32(result) + int32(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 = fqFreeze(int32(result) + int32(f[j])*(int32)(g[i-j])) + } + fg[i] = result + } + + for i := p + p - 2; i >= p; i-- { + fg[i-p] = fqFreeze(int32(fg[i-p] + fg[i])) + fg[i-p+1] = fqFreeze(int32(fg[i-p+1] + fg[i])) + + } + + for i := 0; i < p; i++ { + h[i] = fg[i] + } + return h +} + +// h = 3f in Rq +func rqMult3(f []Fq) (h []Fq) { + h = make([]Fq, p) + for i := 0; i < p; i++ { + h[i] = fqFreeze(int32(3 * f[i])) + } + return h +} + +// Returns 0 if recip succeeded; else -1 +// out = 1/(3*in) in Rq +func rqRecip3(in []small) ([]Fq, int) { + out := make([]Fq, p) + f := make([]Fq, p+1) + g := make([]Fq, p+1) + v := make([]Fq, p+1) + r := make([]Fq, p+1) + + var swap, t int + var f0, g0 int32 + + r[0] = fqRecip(3) + f[0] = 1 + f[p-1] = -1 + f[p] = -1 + + for i := 0; i < p; i++ { + g[p-1-i] = Fq(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 = internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0])) + delta ^= swap & (delta ^ -delta) + delta += 1 + + for i := 0; i < p+1; i++ { + t = swap & int(f[i]^g[i]) + f[i] ^= Fq(t) + g[i] ^= Fq(t) + t = swap & int(v[i]^r[i]) + v[i] ^= Fq(t) + r[i] ^= Fq(t) + } + + f0 = int32(f[0]) + g0 = int32(g[0]) + + for i := 0; i < p+1; i++ { + g[i] = fqFreeze(f0*int32(g[i]) - g0*int32(f[i])) + } + for i := 0; i < p+1; i++ { + r[i] = fqFreeze(f0*int32(r[i]) - g0*int32(v[i])) + } + + for i := 0; i < p; i++ { + g[i] = g[i+1] + } + g[p] = 0 + } + + scale := Fq(fqRecip(f[0])) + for i := 0; i < p; i++ { + out[i] = fqFreeze(int32(scale) * (int32)(v[p-1-i])) + } + + return out, internal.Int16_nonzero_mask(int16(delta)) + +} + +// Rounding all coefficients of a polynomial to the nearest multiple of 3 +// Rounded polynomials mod q +func round(a []Fq) []Fq { + out := make([]Fq, p) + for i := 0; i < p; i++ { + out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) + } + return out +} + +// Returns (min(x, y), max(x, y)), executes in constant time +func minmax(x, y *uint32) { + var xi uint32 = *x + var yi uint32 = *y + var xy uint32 = xi ^ yi + var c uint32 = yi - xi + c ^= xy & (c ^ yi ^ 0x80000000) + c >>= 31 + c = -c + c &= xy + *x = xi ^ c + *y = yi ^ c +} + +// Sorts the array of unsigned integers +func cryptoSortUint32(x []uint32, n int) { + if n < 2 { + return + } + top := 1 + + for top < n-top { + top += top + } + + for p := top; p > 0; p >>= 1 { + for i := 0; i < n-p; i++ { + if i&p == 0 { + minmax(&x[i], &x[i+p]) + } + } + for q := top; q > p; q >>= 1 { + for i := 0; i < n-q; i++ { + if i&p == 0 { + minmax(&x[i+p], &x[i+q]) + } + } + } + } +} + +// Sorting to generate short polynomial +func shortFromList(in []int32) []small { + out := make([]small, p) + L := make([]uint32, p) + + var neg2, neg3 int = -2, -3 + + for i := 0; i < w; i++ { + L[i] = uint32(in[i]) & uint32((neg2)) + } + + for i := w; i < p; i++ { + L[i] = (uint32(in[i]) & uint32((neg3))) | 1 + } + + cryptoSortUint32(L, p) + + for i := 0; i < p; i++ { + out[i] = small((L[i] & 3) - 1) + } + return out +} + +// Underlying hash function + +// The input byte array, in, is prepended by the byte b +// and its SHA-512 hash is calculated +// Only the first 32 bytes of the hash are returned +// e.g., b = 0 means out = Hash0(in) +func hashPrefix(out []byte, b int, in []byte, inlen int) { + x := make([]byte, inlen+1) + h := make([]byte, 64) + + x[0] = byte(b) + copy(x[1:], in) + + hash := sha512.New() + hash.Write([]byte(x)) + h = hash.Sum(nil) + + copy(out, h[:32]) + +} + +// Higher level randomness +// Returns a random unsigned integer +// A generator can be passed for deterministic number generation +func urandom32(gen *nist.DRBG) uint32 { + + c := make([]byte, 4) + var out [4]uint32 + + if gen != nil { + gen.Fill(c) + } else { + cryptoRand.Read(c) + + } + + 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] +} + +// Generates a random short polynomial +func shortRandom(gen *nist.DRBG) []small { + + L := make([]uint32, p) + + for i := 0; i < p; i++ { + L[i] = urandom32(gen) + } + + // Converts uint32 array to int32 array + L_int32 := make([]int32, p) + for i := 0; i < len(L); i++ { + L_int32[i] = int32(L[i]) + } + out := shortFromList(L_int32) + + return out + +} + +// Generates a random list of small +func smallRandom(gen *nist.DRBG) []small { + + out := make([]small, p) + for i := 0; i < p; i++ { + out[i] = small(((urandom32(gen)&0x3fffffff)*3)>>30) - 1 + } + return out +} + +// Streamlined NTRU Prime Core + +// h,(f,ginv) = keyGen() +func keyGen(gen *nist.DRBG) (h []Fq, f []small, ginv []small) { + g := make([]small, p) + var err int + for { + g = smallRandom(gen) + ginv, err = r3Recip(g) + if err == 0 { + break + } + + } + + f = shortRandom(gen) + + finv, _ := rqRecip3(f) /* always works */ + h = rqMultSmall(finv, g) + return h, f, ginv +} + +// c = encrypt(r,h) +func encrypt(r []small, h []Fq) []Fq { + + hr := rqMultSmall(h, r) + c := round(hr) + + return c + +} + +// r = decrypt(c,(f,ginv)) +func decrypt(c []Fq, f []small, ginv []small) []small { + r := make([]small, p) + cf := make([]Fq, p) + + cf = rqMultSmall(c, f) + cf3 := rqMult3(cf) + e := r3FromRq(cf3) + ev := r3Mult(e, ginv) + + mask := weightwMask(ev) /* 0 if weight w, else -1 */ + for i := 0; i < w; i++ { + r[i] = ((ev[i] ^ 1) & small(^mask)) ^ 1 + } + + for i := w; i < p; i++ { + r[i] = ev[i] & small(^mask) + } + return r + +} + +// Encoding small polynomials (including short polynomials) + +// Transform polynomial in R to bytes +// these are the only functions that rely on p mod 4 = 1 */ +func smallEncode(s []byte, f []small) { + var x small + var index int = 0 + for i := 0; i < p/4; i++ { + x = f[index] + 1 + index++ + + x += (f[index] + 1) << 2 + index++ + x += (f[index] + 1) << 4 + index++ + x += (f[index] + 1) << 6 + index++ + + s[0] = byte(x) + s = s[1:] + } + x = f[index] + 1 + + s[0] = byte(x) +} + +// Transform bytes into polynomial in R +func smallDecode(f []small, s []byte) { + var index int = 0 + var x byte + + for i := 0; i < p/4; i++ { + x = s[0] + s = s[1:] + + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + index++ + } + x = s[0] + f[index] = ((small)(x & 3)) - 1 +} + +// Encoding general polynomials + +// Transform polynomials in R/q to bytes +func rqEncode(s []byte, r []Fq) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16(r[i] + q12) + M[i] = q + } + internal.Encode(s, R, M, p) +} + +// Transform polynomials in R/q from bytes +func rqDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = q + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = ((Fq)(R[i])) - q12 + } + +} + +// Encoding rounded polynomials + +// Transform rounded polynomials to bytes +func roundedEncode(s []byte, r []Fq) { + + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16((int32((r[i])+q12) * 10923) >> 15) + M[i] = (q + 2) / 3 + } + internal.Encode(s, R, M, p) +} + +// Transform bytes to rounded polynomials +func roundedDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = (q + 2) / 3 + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = Fq(R[i]*3 - q12) + } + +} + +// Streamlined NTRU Prime Core plus encoding + +// Generates public key and private key +// pk,sk = zKeyGen() +func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { + + h, f, v := keyGen(gen) + + rqEncode(pk, h) + smallEncode(sk, f) + sk = sk[smallBytes:] + smallEncode(sk, v) + +} + +// C = zEncrypt(r,pk) +func zEncrypt(C []byte, r Inputs, pk []byte) { + h := make([]Fq, p) + rqDecode(h, pk) + c := encrypt(r[:], h) + roundedEncode(C, c) +} + +// r = zDecrypt(C,sk) +func zDecrypt(r *Inputs, C []byte, sk []byte) { + f := make([]small, p) + v := make([]small, p) + c := make([]Fq, p) + + smallDecode(f, sk) + sk = sk[smallBytes:] + smallDecode(v, sk) + roundedDecode(c, C) + + copy(r[:], decrypt(c, f, v)) +} + +// Confirmation hash + +// h = hashConfirm(r,pk,cache); cache is Hash4(pk) +func hashConfirm(h []byte, r []byte, pk []byte, cache []byte) { + x := make([]byte, hashBytes*2) + + hashPrefix(x, 3, r, inputsBytes) + + copy(x[hashBytes:], cache[:hashBytes]) + + hashPrefix(h, 2, x, len(x)) + +} + +// Session-key hash + +// k = hashSession(b,y,z) +func hashSession(k []byte, b int, y []byte, z []byte) { + x := make([]byte, hashBytes+ciphertextsBytes+confirmBytes) + + hashPrefix(x, 3, y, inputsBytes) + + copy(x[hashBytes:], z[:ciphertextsBytes+confirmBytes]) + + hashPrefix(k, b, x, len(x)) + +} + +// Streamlined NTRU Prime + +// pk,sk = kemKeyGen() +func kemKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { + zKeyGen(pk, sk, gen) + sk = sk[secretKeysBytes:] + + copy(sk, pk) + sk = sk[publicKeysBytes:] + + if gen != nil { + gen.Fill(sk[:inputsBytes]) + + } else { + cryptoRand.Read(sk[:inputsBytes]) + } + sk = sk[inputsBytes:] + hashPrefix(sk, 4, pk, publicKeysBytes) + +} + +// c,r_enc = hide(r,pk,cache); cache is Hash4(pk) +func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { + smallEncode(r_enc, r[:]) + zEncrypt(c, r, pk) + c = c[ciphertextsBytes:] + hashConfirm(c, r_enc, pk, cache) + +} + +// Takes as input a public key +// Returns ciphertext and shared key +// c,k = encap(pk) +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + pk := pub.pk[:] + + var r Inputs + r_enc := make([]byte, inputsBytes) + cache := make([]byte, hashBytes) + + hashPrefix(cache, 4, pk, publicKeysBytes) + copy(r[:], shortRandom(gen)) + hide(c, r_enc, r, pk, cache) + hashSession(k, 1, r_enc, c) + +} + +// Returns 0 if matching ciphertext+confirm, else -1 +func ciphertexts_diff_mask(c []byte, c2 []byte) int { + var differentbits uint16 = 0 + var len int = ciphertextsBytes + confirmBytes + + for i := 0; i < len; i++ { + differentbits |= uint16((c[i]) ^ (c2[i])) + } + return int((1 & ((differentbits - 1) >> 8)) - 1) + +} + +// Returns shared key from ciphertext and private key +// k = decap(c,sk) +func (priv *PrivateKey) DecapsulateTo(k []byte, c []byte) { + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + sk := priv.sk[:] + + pk := sk[secretKeysBytes:] + rho := pk[publicKeysBytes:] + cache := rho[inputsBytes:] + var r Inputs + + r_enc := make([]byte, inputsBytes) + cnew := make([]byte, ciphertextsBytes+confirmBytes) + + zDecrypt(&r, c, sk) + hide(cnew, r_enc, r, pk, cache) + var mask int = ciphertexts_diff_mask(c, cnew) + + for i := 0; i < inputsBytes; i++ { + r_enc[i] ^= byte(mask & int(r_enc[i]^rho[i])) + } + hashSession(k, 1+mask, r_enc, c) +} + +// The structure of the private key is given by the following segments: +// The secret key, the public key, entropy and the hash of the public key +type PrivateKey struct { + sk [PrivateKeySize]byte +} + +type PublicKey struct { + pk [PublicKeySize]byte +} + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "sntrup761" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + copy(ret[:], sk.sk[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + return bytes.Equal(sk.sk[:], oth.sk[:]) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + return bytes.Equal(pk.pk[:], oth.pk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + var pk [PublicKeySize]byte + skey, _ := sk.MarshalBinary() + ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] + copy(pk[:], ppk[:]) + return &PublicKey{pk: pk} +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + copy(ret[:], pk.pk[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + kemKeyGen(pk[:], sk[:], nil) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk}, nil + +} + +func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + + kemKeyGen(pk[:], sk[:], gen) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk} +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, nil) + + return ct, ss, nil + +} + +func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DRBG) (ct, ss []byte, err error) { + + if gen == nil { + panic("A nist DRBG must be provided") + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, gen) + + return ct, ss, nil +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + ssk, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + ss := [SharedKeySize]byte{} + + ssk.DecapsulateTo(ss[:], ct) + + return ss[:], nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + pk := [PublicKeySize]byte{} + copy(pk[:], buf) + return &PublicKey{pk: pk}, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + sk := [PrivateKeySize]byte{} + copy(sk[:], buf) + return &PrivateKey{sk: sk}, nil +} diff --git a/kem/ntruprime/sntrup857/ntruprime.go b/kem/ntruprime/sntrup857/ntruprime.go new file mode 100644 index 000000000..b475cdd39 --- /dev/null +++ b/kem/ntruprime/sntrup857/ntruprime.go @@ -0,0 +1,956 @@ +// Code generated from sntrup.templ.go. DO NOT EDIT. + +// Package sntrup857 implements the IND-CCA2 secure key encapsulation mechanism +// sntrup857 as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package sntrup857 + +import ( + "bytes" + cryptoRand "crypto/rand" + "crypto/sha512" + + "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/kem/ntruprime/internal" + "github.com/cloudflare/circl/pke/ntruprime/kem" + ntrup "github.com/cloudflare/circl/pke/ntruprime/sntrup857" +) + +type ( + small int8 + Fq int16 + Inputs [p]small +) + +const ( + p = ntrup.P + q = ntrup.Q + q12 = ((q - 1) / 2) + roundedBytes = ntrup.RoundedBytes + rqBytes = ntrup.RqBytes + w = ntrup.W + + hashBytes = 32 + + smallBytes = ((p + 3) / 4) + + inputsBytes = smallBytes + ciphertextsBytes = roundedBytes + secretKeysBytes = (2 * smallBytes) + publicKeysBytes = rqBytes + + confirmBytes = 32 +) + +const ( + // Size of the established shared key. + SharedKeySize = ntrup.SharedKeySize + + // Size of the encapsulated shared key. + CiphertextSize = ntrup.CiphertextSize + + // Size of a packed public key. + PublicKeySize = ntrup.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = ntrup.PrivateKeySize +) + +// Arithmetic operations over GF(3) + +// A polynomial of R has all of its coefficients in (-1,0,1) +// F3 is always represented as -1,0,1 +// so ZZ_fromF3 is a no-op + +// x must not be close to top int16 +func f3Freeze(x int16) small { + return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 +} + +// Arithmetic operations over GF(q) + +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +func fqFreeze(x int32) Fq { + return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) +} + +// Calculates reciprocal of Fq +func fqRecip(a1 Fq) Fq { + var i int = 1 + ai := a1 + + for i < (q - 2) { + ai = fqFreeze(int32(a1) * int32(ai)) + i += 1 + } + return ai +} + +// Returns 0 if the weight w is equal to r +// otherwise returns -1 +func weightwMask(r []small) int { + var weight int = 0 + + for i := 0; i < p; i++ { + weight += int(r[i]) & 1 + } + + // returns -1 if non zero + // otherwise returns 0 if weight==w + return internal.Int16_nonzero_mask(int16(weight - w)) + +} + +/* R3_fromR(R_fromRq(r)) */ +func r3FromRq(r []Fq) []small { + out := make([]small, p) + for i := 0; i < p; i++ { + out[i] = small(f3Freeze(int16(r[i]))) + } + return out +} + +// h = f*g in the ring R3 +func r3Mult(f []small, g []small) (h []small) { + h = make([]small, p) + fg := make([]small, p+p-1) + var result small + var i, j int + + for i = 0; i < p; i++ { + result = 0 + for j = 0; j <= i; j++ { + result = f3Freeze(int16(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 = f3Freeze(int16(result + f[j]*g[i-j])) + } + fg[i] = result + } + + for i = p + p - 2; i >= p; i-- { + fg[i-p] = f3Freeze(int16(fg[i-p] + fg[i])) + fg[i-p+1] = f3Freeze(int16(fg[i-p+1] + fg[i])) + } + + for i = 0; i < p; i++ { + h[i] = fg[i] + } + + return h + +} + +// Calculates the reciprocal of R3 polynomials +// Returns 0 if recip succeeded; else -1 +func r3Recip(in []small) ([]small, int) { + out := make([]small, p) + f := make([]small, p+1) + g := make([]small, p+1) + v := make([]small, p+1) + r := make([]small, p+1) + + var sign int + + r[0] = 1 + f[0] = 1 + + f[p-1] = -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 = int(-g[0] * f[0]) + var swap int = int(internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0]))) + delta ^= swap & int(delta^-delta) + delta += 1 + + for i := 0; i < p+1; i++ { + t := swap & int(f[i]^g[i]) + f[i] ^= small(t) + g[i] ^= small(t) + t = swap & int(v[i]^r[i]) + v[i] ^= small(t) + r[i] ^= small(t) + } + for i := 0; i < p+1; i++ { + g[i] = f3Freeze(int16(int(g[i]) + sign*int(f[i]))) + } + + for i := 0; i < p+1; i++ { + r[i] = f3Freeze(int16(int(r[i]) + sign*int(v[i]))) + } + + for i := 0; i < p; i++ { + g[i] = g[i+1] + } + + g[p] = 0 + + } + sign = int(f[0]) + + for i := 0; i < p; i++ { + + out[i] = small(sign * int(v[p-1-i])) + } + + return out, internal.Int16_nonzero_mask(int16(delta)) + +} + +// Polynomials mod q + +// h = f*g in the ring Rq */ +func rqMultSmall(f []Fq, g []small) (h []Fq) { + h = make([]Fq, p) + fg := make([]Fq, p+p-1) + var result Fq + + for i := 0; i < p; i++ { + result = 0 + for j := 0; j <= i; j++ { + result = fqFreeze(int32(result) + int32(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 = fqFreeze(int32(result) + int32(f[j])*(int32)(g[i-j])) + } + fg[i] = result + } + + for i := p + p - 2; i >= p; i-- { + fg[i-p] = fqFreeze(int32(fg[i-p] + fg[i])) + fg[i-p+1] = fqFreeze(int32(fg[i-p+1] + fg[i])) + + } + + for i := 0; i < p; i++ { + h[i] = fg[i] + } + return h +} + +// h = 3f in Rq +func rqMult3(f []Fq) (h []Fq) { + h = make([]Fq, p) + for i := 0; i < p; i++ { + h[i] = fqFreeze(int32(3 * f[i])) + } + return h +} + +// Returns 0 if recip succeeded; else -1 +// out = 1/(3*in) in Rq +func rqRecip3(in []small) ([]Fq, int) { + out := make([]Fq, p) + f := make([]Fq, p+1) + g := make([]Fq, p+1) + v := make([]Fq, p+1) + r := make([]Fq, p+1) + + var swap, t int + var f0, g0 int32 + + r[0] = fqRecip(3) + f[0] = 1 + f[p-1] = -1 + f[p] = -1 + + for i := 0; i < p; i++ { + g[p-1-i] = Fq(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 = internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0])) + delta ^= swap & (delta ^ -delta) + delta += 1 + + for i := 0; i < p+1; i++ { + t = swap & int(f[i]^g[i]) + f[i] ^= Fq(t) + g[i] ^= Fq(t) + t = swap & int(v[i]^r[i]) + v[i] ^= Fq(t) + r[i] ^= Fq(t) + } + + f0 = int32(f[0]) + g0 = int32(g[0]) + + for i := 0; i < p+1; i++ { + g[i] = fqFreeze(f0*int32(g[i]) - g0*int32(f[i])) + } + for i := 0; i < p+1; i++ { + r[i] = fqFreeze(f0*int32(r[i]) - g0*int32(v[i])) + } + + for i := 0; i < p; i++ { + g[i] = g[i+1] + } + g[p] = 0 + } + + scale := Fq(fqRecip(f[0])) + for i := 0; i < p; i++ { + out[i] = fqFreeze(int32(scale) * (int32)(v[p-1-i])) + } + + return out, internal.Int16_nonzero_mask(int16(delta)) + +} + +// Rounding all coefficients of a polynomial to the nearest multiple of 3 +// Rounded polynomials mod q +func round(a []Fq) []Fq { + out := make([]Fq, p) + for i := 0; i < p; i++ { + out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) + } + return out +} + +// Returns (min(x, y), max(x, y)), executes in constant time +func minmax(x, y *uint32) { + var xi uint32 = *x + var yi uint32 = *y + var xy uint32 = xi ^ yi + var c uint32 = yi - xi + c ^= xy & (c ^ yi ^ 0x80000000) + c >>= 31 + c = -c + c &= xy + *x = xi ^ c + *y = yi ^ c +} + +// Sorts the array of unsigned integers +func cryptoSortUint32(x []uint32, n int) { + if n < 2 { + return + } + top := 1 + + for top < n-top { + top += top + } + + for p := top; p > 0; p >>= 1 { + for i := 0; i < n-p; i++ { + if i&p == 0 { + minmax(&x[i], &x[i+p]) + } + } + for q := top; q > p; q >>= 1 { + for i := 0; i < n-q; i++ { + if i&p == 0 { + minmax(&x[i+p], &x[i+q]) + } + } + } + } +} + +// Sorting to generate short polynomial +func shortFromList(in []int32) []small { + out := make([]small, p) + L := make([]uint32, p) + + var neg2, neg3 int = -2, -3 + + for i := 0; i < w; i++ { + L[i] = uint32(in[i]) & uint32((neg2)) + } + + for i := w; i < p; i++ { + L[i] = (uint32(in[i]) & uint32((neg3))) | 1 + } + + cryptoSortUint32(L, p) + + for i := 0; i < p; i++ { + out[i] = small((L[i] & 3) - 1) + } + return out +} + +// Underlying hash function + +// The input byte array, in, is prepended by the byte b +// and its SHA-512 hash is calculated +// Only the first 32 bytes of the hash are returned +// e.g., b = 0 means out = Hash0(in) +func hashPrefix(out []byte, b int, in []byte, inlen int) { + x := make([]byte, inlen+1) + h := make([]byte, 64) + + x[0] = byte(b) + copy(x[1:], in) + + hash := sha512.New() + hash.Write([]byte(x)) + h = hash.Sum(nil) + + copy(out, h[:32]) + +} + +// Higher level randomness +// Returns a random unsigned integer +// A generator can be passed for deterministic number generation +func urandom32(gen *nist.DRBG) uint32 { + + c := make([]byte, 4) + var out [4]uint32 + + if gen != nil { + gen.Fill(c) + } else { + cryptoRand.Read(c) + + } + + 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] +} + +// Generates a random short polynomial +func shortRandom(gen *nist.DRBG) []small { + + L := make([]uint32, p) + + for i := 0; i < p; i++ { + L[i] = urandom32(gen) + } + + // Converts uint32 array to int32 array + L_int32 := make([]int32, p) + for i := 0; i < len(L); i++ { + L_int32[i] = int32(L[i]) + } + out := shortFromList(L_int32) + + return out + +} + +// Generates a random list of small +func smallRandom(gen *nist.DRBG) []small { + + out := make([]small, p) + for i := 0; i < p; i++ { + out[i] = small(((urandom32(gen)&0x3fffffff)*3)>>30) - 1 + } + return out +} + +// Streamlined NTRU Prime Core + +// h,(f,ginv) = keyGen() +func keyGen(gen *nist.DRBG) (h []Fq, f []small, ginv []small) { + g := make([]small, p) + var err int + for { + g = smallRandom(gen) + ginv, err = r3Recip(g) + if err == 0 { + break + } + + } + + f = shortRandom(gen) + + finv, _ := rqRecip3(f) /* always works */ + h = rqMultSmall(finv, g) + return h, f, ginv +} + +// c = encrypt(r,h) +func encrypt(r []small, h []Fq) []Fq { + + hr := rqMultSmall(h, r) + c := round(hr) + + return c + +} + +// r = decrypt(c,(f,ginv)) +func decrypt(c []Fq, f []small, ginv []small) []small { + r := make([]small, p) + cf := make([]Fq, p) + + cf = rqMultSmall(c, f) + cf3 := rqMult3(cf) + e := r3FromRq(cf3) + ev := r3Mult(e, ginv) + + mask := weightwMask(ev) /* 0 if weight w, else -1 */ + for i := 0; i < w; i++ { + r[i] = ((ev[i] ^ 1) & small(^mask)) ^ 1 + } + + for i := w; i < p; i++ { + r[i] = ev[i] & small(^mask) + } + return r + +} + +// Encoding small polynomials (including short polynomials) + +// Transform polynomial in R to bytes +// these are the only functions that rely on p mod 4 = 1 */ +func smallEncode(s []byte, f []small) { + var x small + var index int = 0 + for i := 0; i < p/4; i++ { + x = f[index] + 1 + index++ + + x += (f[index] + 1) << 2 + index++ + x += (f[index] + 1) << 4 + index++ + x += (f[index] + 1) << 6 + index++ + + s[0] = byte(x) + s = s[1:] + } + x = f[index] + 1 + + s[0] = byte(x) +} + +// Transform bytes into polynomial in R +func smallDecode(f []small, s []byte) { + var index int = 0 + var x byte + + for i := 0; i < p/4; i++ { + x = s[0] + s = s[1:] + + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + index++ + } + x = s[0] + f[index] = ((small)(x & 3)) - 1 +} + +// Encoding general polynomials + +// Transform polynomials in R/q to bytes +func rqEncode(s []byte, r []Fq) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16(r[i] + q12) + M[i] = q + } + internal.Encode(s, R, M, p) +} + +// Transform polynomials in R/q from bytes +func rqDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = q + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = ((Fq)(R[i])) - q12 + } + +} + +// Encoding rounded polynomials + +// Transform rounded polynomials to bytes +func roundedEncode(s []byte, r []Fq) { + + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16((int32((r[i])+q12) * 10923) >> 15) + M[i] = (q + 2) / 3 + } + internal.Encode(s, R, M, p) +} + +// Transform bytes to rounded polynomials +func roundedDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = (q + 2) / 3 + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = Fq(R[i]*3 - q12) + } + +} + +// Streamlined NTRU Prime Core plus encoding + +// Generates public key and private key +// pk,sk = zKeyGen() +func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { + + h, f, v := keyGen(gen) + + rqEncode(pk, h) + smallEncode(sk, f) + sk = sk[smallBytes:] + smallEncode(sk, v) + +} + +// C = zEncrypt(r,pk) +func zEncrypt(C []byte, r Inputs, pk []byte) { + h := make([]Fq, p) + rqDecode(h, pk) + c := encrypt(r[:], h) + roundedEncode(C, c) +} + +// r = zDecrypt(C,sk) +func zDecrypt(r *Inputs, C []byte, sk []byte) { + f := make([]small, p) + v := make([]small, p) + c := make([]Fq, p) + + smallDecode(f, sk) + sk = sk[smallBytes:] + smallDecode(v, sk) + roundedDecode(c, C) + + copy(r[:], decrypt(c, f, v)) +} + +// Confirmation hash + +// h = hashConfirm(r,pk,cache); cache is Hash4(pk) +func hashConfirm(h []byte, r []byte, pk []byte, cache []byte) { + x := make([]byte, hashBytes*2) + + hashPrefix(x, 3, r, inputsBytes) + + copy(x[hashBytes:], cache[:hashBytes]) + + hashPrefix(h, 2, x, len(x)) + +} + +// Session-key hash + +// k = hashSession(b,y,z) +func hashSession(k []byte, b int, y []byte, z []byte) { + x := make([]byte, hashBytes+ciphertextsBytes+confirmBytes) + + hashPrefix(x, 3, y, inputsBytes) + + copy(x[hashBytes:], z[:ciphertextsBytes+confirmBytes]) + + hashPrefix(k, b, x, len(x)) + +} + +// Streamlined NTRU Prime + +// pk,sk = kemKeyGen() +func kemKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { + zKeyGen(pk, sk, gen) + sk = sk[secretKeysBytes:] + + copy(sk, pk) + sk = sk[publicKeysBytes:] + + if gen != nil { + gen.Fill(sk[:inputsBytes]) + + } else { + cryptoRand.Read(sk[:inputsBytes]) + } + sk = sk[inputsBytes:] + hashPrefix(sk, 4, pk, publicKeysBytes) + +} + +// c,r_enc = hide(r,pk,cache); cache is Hash4(pk) +func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { + smallEncode(r_enc, r[:]) + zEncrypt(c, r, pk) + c = c[ciphertextsBytes:] + hashConfirm(c, r_enc, pk, cache) + +} + +// Takes as input a public key +// Returns ciphertext and shared key +// c,k = encap(pk) +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + pk := pub.pk[:] + + var r Inputs + r_enc := make([]byte, inputsBytes) + cache := make([]byte, hashBytes) + + hashPrefix(cache, 4, pk, publicKeysBytes) + copy(r[:], shortRandom(gen)) + hide(c, r_enc, r, pk, cache) + hashSession(k, 1, r_enc, c) + +} + +// Returns 0 if matching ciphertext+confirm, else -1 +func ciphertexts_diff_mask(c []byte, c2 []byte) int { + var differentbits uint16 = 0 + var len int = ciphertextsBytes + confirmBytes + + for i := 0; i < len; i++ { + differentbits |= uint16((c[i]) ^ (c2[i])) + } + return int((1 & ((differentbits - 1) >> 8)) - 1) + +} + +// Returns shared key from ciphertext and private key +// k = decap(c,sk) +func (priv *PrivateKey) DecapsulateTo(k []byte, c []byte) { + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + sk := priv.sk[:] + + pk := sk[secretKeysBytes:] + rho := pk[publicKeysBytes:] + cache := rho[inputsBytes:] + var r Inputs + + r_enc := make([]byte, inputsBytes) + cnew := make([]byte, ciphertextsBytes+confirmBytes) + + zDecrypt(&r, c, sk) + hide(cnew, r_enc, r, pk, cache) + var mask int = ciphertexts_diff_mask(c, cnew) + + for i := 0; i < inputsBytes; i++ { + r_enc[i] ^= byte(mask & int(r_enc[i]^rho[i])) + } + hashSession(k, 1+mask, r_enc, c) +} + +// The structure of the private key is given by the following segments: +// The secret key, the public key, entropy and the hash of the public key +type PrivateKey struct { + sk [PrivateKeySize]byte +} + +type PublicKey struct { + pk [PublicKeySize]byte +} + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "sntrup857" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + copy(ret[:], sk.sk[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + return bytes.Equal(sk.sk[:], oth.sk[:]) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + return bytes.Equal(pk.pk[:], oth.pk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + var pk [PublicKeySize]byte + skey, _ := sk.MarshalBinary() + ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] + copy(pk[:], ppk[:]) + return &PublicKey{pk: pk} +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + copy(ret[:], pk.pk[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + kemKeyGen(pk[:], sk[:], nil) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk}, nil + +} + +func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + + kemKeyGen(pk[:], sk[:], gen) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk} +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, nil) + + return ct, ss, nil + +} + +func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DRBG) (ct, ss []byte, err error) { + + if gen == nil { + panic("A nist DRBG must be provided") + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, gen) + + return ct, ss, nil +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + ssk, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + ss := [SharedKeySize]byte{} + + ssk.DecapsulateTo(ss[:], ct) + + return ss[:], nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + pk := [PublicKeySize]byte{} + copy(pk[:], buf) + return &PublicKey{pk: pk}, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + sk := [PrivateKeySize]byte{} + copy(sk[:], buf) + return &PrivateKey{sk: sk}, nil +} diff --git a/kem/ntruprime/sntrup953/ntruprime.go b/kem/ntruprime/sntrup953/ntruprime.go new file mode 100644 index 000000000..1c2d06ceb --- /dev/null +++ b/kem/ntruprime/sntrup953/ntruprime.go @@ -0,0 +1,956 @@ +// Code generated from sntrup.templ.go. DO NOT EDIT. + +// Package sntrup953 implements the IND-CCA2 secure key encapsulation mechanism +// sntrup953 as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package sntrup953 + +import ( + "bytes" + cryptoRand "crypto/rand" + "crypto/sha512" + + "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/kem/ntruprime/internal" + "github.com/cloudflare/circl/pke/ntruprime/kem" + ntrup "github.com/cloudflare/circl/pke/ntruprime/sntrup953" +) + +type ( + small int8 + Fq int16 + Inputs [p]small +) + +const ( + p = ntrup.P + q = ntrup.Q + q12 = ((q - 1) / 2) + roundedBytes = ntrup.RoundedBytes + rqBytes = ntrup.RqBytes + w = ntrup.W + + hashBytes = 32 + + smallBytes = ((p + 3) / 4) + + inputsBytes = smallBytes + ciphertextsBytes = roundedBytes + secretKeysBytes = (2 * smallBytes) + publicKeysBytes = rqBytes + + confirmBytes = 32 +) + +const ( + // Size of the established shared key. + SharedKeySize = ntrup.SharedKeySize + + // Size of the encapsulated shared key. + CiphertextSize = ntrup.CiphertextSize + + // Size of a packed public key. + PublicKeySize = ntrup.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = ntrup.PrivateKeySize +) + +// Arithmetic operations over GF(3) + +// A polynomial of R has all of its coefficients in (-1,0,1) +// F3 is always represented as -1,0,1 +// so ZZ_fromF3 is a no-op + +// x must not be close to top int16 +func f3Freeze(x int16) small { + return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 +} + +// Arithmetic operations over GF(q) + +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +func fqFreeze(x int32) Fq { + return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) +} + +// Calculates reciprocal of Fq +func fqRecip(a1 Fq) Fq { + var i int = 1 + ai := a1 + + for i < (q - 2) { + ai = fqFreeze(int32(a1) * int32(ai)) + i += 1 + } + return ai +} + +// Returns 0 if the weight w is equal to r +// otherwise returns -1 +func weightwMask(r []small) int { + var weight int = 0 + + for i := 0; i < p; i++ { + weight += int(r[i]) & 1 + } + + // returns -1 if non zero + // otherwise returns 0 if weight==w + return internal.Int16_nonzero_mask(int16(weight - w)) + +} + +/* R3_fromR(R_fromRq(r)) */ +func r3FromRq(r []Fq) []small { + out := make([]small, p) + for i := 0; i < p; i++ { + out[i] = small(f3Freeze(int16(r[i]))) + } + return out +} + +// h = f*g in the ring R3 +func r3Mult(f []small, g []small) (h []small) { + h = make([]small, p) + fg := make([]small, p+p-1) + var result small + var i, j int + + for i = 0; i < p; i++ { + result = 0 + for j = 0; j <= i; j++ { + result = f3Freeze(int16(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 = f3Freeze(int16(result + f[j]*g[i-j])) + } + fg[i] = result + } + + for i = p + p - 2; i >= p; i-- { + fg[i-p] = f3Freeze(int16(fg[i-p] + fg[i])) + fg[i-p+1] = f3Freeze(int16(fg[i-p+1] + fg[i])) + } + + for i = 0; i < p; i++ { + h[i] = fg[i] + } + + return h + +} + +// Calculates the reciprocal of R3 polynomials +// Returns 0 if recip succeeded; else -1 +func r3Recip(in []small) ([]small, int) { + out := make([]small, p) + f := make([]small, p+1) + g := make([]small, p+1) + v := make([]small, p+1) + r := make([]small, p+1) + + var sign int + + r[0] = 1 + f[0] = 1 + + f[p-1] = -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 = int(-g[0] * f[0]) + var swap int = int(internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0]))) + delta ^= swap & int(delta^-delta) + delta += 1 + + for i := 0; i < p+1; i++ { + t := swap & int(f[i]^g[i]) + f[i] ^= small(t) + g[i] ^= small(t) + t = swap & int(v[i]^r[i]) + v[i] ^= small(t) + r[i] ^= small(t) + } + for i := 0; i < p+1; i++ { + g[i] = f3Freeze(int16(int(g[i]) + sign*int(f[i]))) + } + + for i := 0; i < p+1; i++ { + r[i] = f3Freeze(int16(int(r[i]) + sign*int(v[i]))) + } + + for i := 0; i < p; i++ { + g[i] = g[i+1] + } + + g[p] = 0 + + } + sign = int(f[0]) + + for i := 0; i < p; i++ { + + out[i] = small(sign * int(v[p-1-i])) + } + + return out, internal.Int16_nonzero_mask(int16(delta)) + +} + +// Polynomials mod q + +// h = f*g in the ring Rq */ +func rqMultSmall(f []Fq, g []small) (h []Fq) { + h = make([]Fq, p) + fg := make([]Fq, p+p-1) + var result Fq + + for i := 0; i < p; i++ { + result = 0 + for j := 0; j <= i; j++ { + result = fqFreeze(int32(result) + int32(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 = fqFreeze(int32(result) + int32(f[j])*(int32)(g[i-j])) + } + fg[i] = result + } + + for i := p + p - 2; i >= p; i-- { + fg[i-p] = fqFreeze(int32(fg[i-p] + fg[i])) + fg[i-p+1] = fqFreeze(int32(fg[i-p+1] + fg[i])) + + } + + for i := 0; i < p; i++ { + h[i] = fg[i] + } + return h +} + +// h = 3f in Rq +func rqMult3(f []Fq) (h []Fq) { + h = make([]Fq, p) + for i := 0; i < p; i++ { + h[i] = fqFreeze(int32(3 * f[i])) + } + return h +} + +// Returns 0 if recip succeeded; else -1 +// out = 1/(3*in) in Rq +func rqRecip3(in []small) ([]Fq, int) { + out := make([]Fq, p) + f := make([]Fq, p+1) + g := make([]Fq, p+1) + v := make([]Fq, p+1) + r := make([]Fq, p+1) + + var swap, t int + var f0, g0 int32 + + r[0] = fqRecip(3) + f[0] = 1 + f[p-1] = -1 + f[p] = -1 + + for i := 0; i < p; i++ { + g[p-1-i] = Fq(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 = internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0])) + delta ^= swap & (delta ^ -delta) + delta += 1 + + for i := 0; i < p+1; i++ { + t = swap & int(f[i]^g[i]) + f[i] ^= Fq(t) + g[i] ^= Fq(t) + t = swap & int(v[i]^r[i]) + v[i] ^= Fq(t) + r[i] ^= Fq(t) + } + + f0 = int32(f[0]) + g0 = int32(g[0]) + + for i := 0; i < p+1; i++ { + g[i] = fqFreeze(f0*int32(g[i]) - g0*int32(f[i])) + } + for i := 0; i < p+1; i++ { + r[i] = fqFreeze(f0*int32(r[i]) - g0*int32(v[i])) + } + + for i := 0; i < p; i++ { + g[i] = g[i+1] + } + g[p] = 0 + } + + scale := Fq(fqRecip(f[0])) + for i := 0; i < p; i++ { + out[i] = fqFreeze(int32(scale) * (int32)(v[p-1-i])) + } + + return out, internal.Int16_nonzero_mask(int16(delta)) + +} + +// Rounding all coefficients of a polynomial to the nearest multiple of 3 +// Rounded polynomials mod q +func round(a []Fq) []Fq { + out := make([]Fq, p) + for i := 0; i < p; i++ { + out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) + } + return out +} + +// Returns (min(x, y), max(x, y)), executes in constant time +func minmax(x, y *uint32) { + var xi uint32 = *x + var yi uint32 = *y + var xy uint32 = xi ^ yi + var c uint32 = yi - xi + c ^= xy & (c ^ yi ^ 0x80000000) + c >>= 31 + c = -c + c &= xy + *x = xi ^ c + *y = yi ^ c +} + +// Sorts the array of unsigned integers +func cryptoSortUint32(x []uint32, n int) { + if n < 2 { + return + } + top := 1 + + for top < n-top { + top += top + } + + for p := top; p > 0; p >>= 1 { + for i := 0; i < n-p; i++ { + if i&p == 0 { + minmax(&x[i], &x[i+p]) + } + } + for q := top; q > p; q >>= 1 { + for i := 0; i < n-q; i++ { + if i&p == 0 { + minmax(&x[i+p], &x[i+q]) + } + } + } + } +} + +// Sorting to generate short polynomial +func shortFromList(in []int32) []small { + out := make([]small, p) + L := make([]uint32, p) + + var neg2, neg3 int = -2, -3 + + for i := 0; i < w; i++ { + L[i] = uint32(in[i]) & uint32((neg2)) + } + + for i := w; i < p; i++ { + L[i] = (uint32(in[i]) & uint32((neg3))) | 1 + } + + cryptoSortUint32(L, p) + + for i := 0; i < p; i++ { + out[i] = small((L[i] & 3) - 1) + } + return out +} + +// Underlying hash function + +// The input byte array, in, is prepended by the byte b +// and its SHA-512 hash is calculated +// Only the first 32 bytes of the hash are returned +// e.g., b = 0 means out = Hash0(in) +func hashPrefix(out []byte, b int, in []byte, inlen int) { + x := make([]byte, inlen+1) + h := make([]byte, 64) + + x[0] = byte(b) + copy(x[1:], in) + + hash := sha512.New() + hash.Write([]byte(x)) + h = hash.Sum(nil) + + copy(out, h[:32]) + +} + +// Higher level randomness +// Returns a random unsigned integer +// A generator can be passed for deterministic number generation +func urandom32(gen *nist.DRBG) uint32 { + + c := make([]byte, 4) + var out [4]uint32 + + if gen != nil { + gen.Fill(c) + } else { + cryptoRand.Read(c) + + } + + 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] +} + +// Generates a random short polynomial +func shortRandom(gen *nist.DRBG) []small { + + L := make([]uint32, p) + + for i := 0; i < p; i++ { + L[i] = urandom32(gen) + } + + // Converts uint32 array to int32 array + L_int32 := make([]int32, p) + for i := 0; i < len(L); i++ { + L_int32[i] = int32(L[i]) + } + out := shortFromList(L_int32) + + return out + +} + +// Generates a random list of small +func smallRandom(gen *nist.DRBG) []small { + + out := make([]small, p) + for i := 0; i < p; i++ { + out[i] = small(((urandom32(gen)&0x3fffffff)*3)>>30) - 1 + } + return out +} + +// Streamlined NTRU Prime Core + +// h,(f,ginv) = keyGen() +func keyGen(gen *nist.DRBG) (h []Fq, f []small, ginv []small) { + g := make([]small, p) + var err int + for { + g = smallRandom(gen) + ginv, err = r3Recip(g) + if err == 0 { + break + } + + } + + f = shortRandom(gen) + + finv, _ := rqRecip3(f) /* always works */ + h = rqMultSmall(finv, g) + return h, f, ginv +} + +// c = encrypt(r,h) +func encrypt(r []small, h []Fq) []Fq { + + hr := rqMultSmall(h, r) + c := round(hr) + + return c + +} + +// r = decrypt(c,(f,ginv)) +func decrypt(c []Fq, f []small, ginv []small) []small { + r := make([]small, p) + cf := make([]Fq, p) + + cf = rqMultSmall(c, f) + cf3 := rqMult3(cf) + e := r3FromRq(cf3) + ev := r3Mult(e, ginv) + + mask := weightwMask(ev) /* 0 if weight w, else -1 */ + for i := 0; i < w; i++ { + r[i] = ((ev[i] ^ 1) & small(^mask)) ^ 1 + } + + for i := w; i < p; i++ { + r[i] = ev[i] & small(^mask) + } + return r + +} + +// Encoding small polynomials (including short polynomials) + +// Transform polynomial in R to bytes +// these are the only functions that rely on p mod 4 = 1 */ +func smallEncode(s []byte, f []small) { + var x small + var index int = 0 + for i := 0; i < p/4; i++ { + x = f[index] + 1 + index++ + + x += (f[index] + 1) << 2 + index++ + x += (f[index] + 1) << 4 + index++ + x += (f[index] + 1) << 6 + index++ + + s[0] = byte(x) + s = s[1:] + } + x = f[index] + 1 + + s[0] = byte(x) +} + +// Transform bytes into polynomial in R +func smallDecode(f []small, s []byte) { + var index int = 0 + var x byte + + for i := 0; i < p/4; i++ { + x = s[0] + s = s[1:] + + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + index++ + } + x = s[0] + f[index] = ((small)(x & 3)) - 1 +} + +// Encoding general polynomials + +// Transform polynomials in R/q to bytes +func rqEncode(s []byte, r []Fq) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16(r[i] + q12) + M[i] = q + } + internal.Encode(s, R, M, p) +} + +// Transform polynomials in R/q from bytes +func rqDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = q + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = ((Fq)(R[i])) - q12 + } + +} + +// Encoding rounded polynomials + +// Transform rounded polynomials to bytes +func roundedEncode(s []byte, r []Fq) { + + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16((int32((r[i])+q12) * 10923) >> 15) + M[i] = (q + 2) / 3 + } + internal.Encode(s, R, M, p) +} + +// Transform bytes to rounded polynomials +func roundedDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = (q + 2) / 3 + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = Fq(R[i]*3 - q12) + } + +} + +// Streamlined NTRU Prime Core plus encoding + +// Generates public key and private key +// pk,sk = zKeyGen() +func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { + + h, f, v := keyGen(gen) + + rqEncode(pk, h) + smallEncode(sk, f) + sk = sk[smallBytes:] + smallEncode(sk, v) + +} + +// C = zEncrypt(r,pk) +func zEncrypt(C []byte, r Inputs, pk []byte) { + h := make([]Fq, p) + rqDecode(h, pk) + c := encrypt(r[:], h) + roundedEncode(C, c) +} + +// r = zDecrypt(C,sk) +func zDecrypt(r *Inputs, C []byte, sk []byte) { + f := make([]small, p) + v := make([]small, p) + c := make([]Fq, p) + + smallDecode(f, sk) + sk = sk[smallBytes:] + smallDecode(v, sk) + roundedDecode(c, C) + + copy(r[:], decrypt(c, f, v)) +} + +// Confirmation hash + +// h = hashConfirm(r,pk,cache); cache is Hash4(pk) +func hashConfirm(h []byte, r []byte, pk []byte, cache []byte) { + x := make([]byte, hashBytes*2) + + hashPrefix(x, 3, r, inputsBytes) + + copy(x[hashBytes:], cache[:hashBytes]) + + hashPrefix(h, 2, x, len(x)) + +} + +// Session-key hash + +// k = hashSession(b,y,z) +func hashSession(k []byte, b int, y []byte, z []byte) { + x := make([]byte, hashBytes+ciphertextsBytes+confirmBytes) + + hashPrefix(x, 3, y, inputsBytes) + + copy(x[hashBytes:], z[:ciphertextsBytes+confirmBytes]) + + hashPrefix(k, b, x, len(x)) + +} + +// Streamlined NTRU Prime + +// pk,sk = kemKeyGen() +func kemKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { + zKeyGen(pk, sk, gen) + sk = sk[secretKeysBytes:] + + copy(sk, pk) + sk = sk[publicKeysBytes:] + + if gen != nil { + gen.Fill(sk[:inputsBytes]) + + } else { + cryptoRand.Read(sk[:inputsBytes]) + } + sk = sk[inputsBytes:] + hashPrefix(sk, 4, pk, publicKeysBytes) + +} + +// c,r_enc = hide(r,pk,cache); cache is Hash4(pk) +func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { + smallEncode(r_enc, r[:]) + zEncrypt(c, r, pk) + c = c[ciphertextsBytes:] + hashConfirm(c, r_enc, pk, cache) + +} + +// Takes as input a public key +// Returns ciphertext and shared key +// c,k = encap(pk) +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + pk := pub.pk[:] + + var r Inputs + r_enc := make([]byte, inputsBytes) + cache := make([]byte, hashBytes) + + hashPrefix(cache, 4, pk, publicKeysBytes) + copy(r[:], shortRandom(gen)) + hide(c, r_enc, r, pk, cache) + hashSession(k, 1, r_enc, c) + +} + +// Returns 0 if matching ciphertext+confirm, else -1 +func ciphertexts_diff_mask(c []byte, c2 []byte) int { + var differentbits uint16 = 0 + var len int = ciphertextsBytes + confirmBytes + + for i := 0; i < len; i++ { + differentbits |= uint16((c[i]) ^ (c2[i])) + } + return int((1 & ((differentbits - 1) >> 8)) - 1) + +} + +// Returns shared key from ciphertext and private key +// k = decap(c,sk) +func (priv *PrivateKey) DecapsulateTo(k []byte, c []byte) { + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + sk := priv.sk[:] + + pk := sk[secretKeysBytes:] + rho := pk[publicKeysBytes:] + cache := rho[inputsBytes:] + var r Inputs + + r_enc := make([]byte, inputsBytes) + cnew := make([]byte, ciphertextsBytes+confirmBytes) + + zDecrypt(&r, c, sk) + hide(cnew, r_enc, r, pk, cache) + var mask int = ciphertexts_diff_mask(c, cnew) + + for i := 0; i < inputsBytes; i++ { + r_enc[i] ^= byte(mask & int(r_enc[i]^rho[i])) + } + hashSession(k, 1+mask, r_enc, c) +} + +// The structure of the private key is given by the following segments: +// The secret key, the public key, entropy and the hash of the public key +type PrivateKey struct { + sk [PrivateKeySize]byte +} + +type PublicKey struct { + pk [PublicKeySize]byte +} + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "sntrup953" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + copy(ret[:], sk.sk[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + return bytes.Equal(sk.sk[:], oth.sk[:]) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + return bytes.Equal(pk.pk[:], oth.pk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + var pk [PublicKeySize]byte + skey, _ := sk.MarshalBinary() + ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] + copy(pk[:], ppk[:]) + return &PublicKey{pk: pk} +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + copy(ret[:], pk.pk[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + kemKeyGen(pk[:], sk[:], nil) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk}, nil + +} + +func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + + kemKeyGen(pk[:], sk[:], gen) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk} +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, nil) + + return ct, ss, nil + +} + +func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DRBG) (ct, ss []byte, err error) { + + if gen == nil { + panic("A nist DRBG must be provided") + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, gen) + + return ct, ss, nil +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + ssk, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + ss := [SharedKeySize]byte{} + + ssk.DecapsulateTo(ss[:], ct) + + return ss[:], nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + pk := [PublicKeySize]byte{} + copy(pk[:], buf) + return &PublicKey{pk: pk}, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + sk := [PrivateKeySize]byte{} + copy(sk[:], buf) + return &PrivateKey{sk: sk}, nil +} diff --git a/kem/ntruprime/templates/ntrulpr.templ.go b/kem/ntruprime/templates/ntrulpr.templ.go new file mode 100644 index 000000000..e482b1821 --- /dev/null +++ b/kem/ntruprime/templates/ntrulpr.templ.go @@ -0,0 +1,872 @@ +// +build ignore +// The previous line (and this one up to the warning below) is removed by the +// template generator. + +// Code generated from ntrulpr.templ.go. DO NOT EDIT. + +// Package {{.Pkg}} implements the IND-CCA2 secure key encapsulation mechanism +// {{.Pkg}} as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package {{.Pkg}} + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + cryptoRand "crypto/rand" + "crypto/sha512" + + "github.com/cloudflare/circl/kem" + "github.com/cloudflare/circl/kem/ntruprime/internal" + ntrup "github.com/cloudflare/circl/pke/ntruprime/{{.Pkg}}" +) + +const ( + p = ntrup.P + q = ntrup.Q + q12 = ((q - 1) / 2) + roundedBytes = ntrup.RoundedBytes + + w = ntrup.W + tau0 = ntrup.Tau0 + tau1 = ntrup.Tau1 + tau2 = ntrup.Tau2 + tau3 = ntrup.Tau3 + + I = ntrup.I + + hashBytes = 32 + + smallBytes = ((p + 3) / 4) + + inputsBytes = I / 8 + seedBytes = 32 + ciphertextsBytes = roundedBytes + topBytes + secretKeysBytes = smallBytes + publicKeysBytes = seedBytes + roundedBytes + + confirmBytes = 32 + + tau = 16 + topBytes = I / 2 +) + +const ( + // Size of seed for NewKeyFromSeed + KeySeedSize = seedBytes + p*4 + inputsBytes + + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = inputsBytes + + // Size of the established shared key. + SharedKeySize = ntrup.SharedKeySize + + // Size of the encapsulated shared key. + CiphertextSize = ntrup.CiphertextSize + + // Size of a packed public key. + PublicKeySize = ntrup.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = ntrup.PrivateKeySize +) + +type ( + small int8 + Fq int16 +) + +// arithmetic operations over GF(3) + +// A polynomial of R has all of its coefficients in (-1,0,1) +// F3 is always represented as -1,0,1 +// so ZZ_fromF3 is a no-op + +// x must not be close to top int16 +func f3Freeze(x int16) small { + return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 +} + +/* ----- arithmetic mod q */ +// GF (q) +// type Fq int16 + +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +func fqFreeze(x int32) Fq { + return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) +} + +func top(C Fq) int8 { + return int8((tau1*(int32)(C+tau0) + 16384) >> 15) +} + +func right(T int8) Fq { + return fqFreeze(tau3*int32(T) - tau2) +} + +// Polynomials mod q + +// h = f*g in the ring Rq */ +func rqMultSmall(f []Fq, g []small) (h []Fq) { + h = make([]Fq, p) + fg := make([]Fq, p+p-1) + var result Fq + + for i := 0; i < p; i++ { + result = 0 + for j := 0; j <= i; j++ { + result = fqFreeze(int32(result) + int32(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 = fqFreeze(int32(result) + int32(f[j])*(int32)(g[i-j])) + } + fg[i] = result + } + + for i := p + p - 2; i >= p; i-- { + fg[i-p] = fqFreeze(int32(fg[i-p] + fg[i])) + fg[i-p+1] = fqFreeze(int32(fg[i-p+1] + fg[i])) + + } + + for i := 0; i < p; i++ { + h[i] = fg[i] + } + return h +} + +// Rounding all coefficients of a polynomial to the nearest multiple of 3 +// Rounded polynomials mod q +func round(a []Fq) []Fq { + out := make([]Fq, p) + for i := 0; i < p; i++ { + out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) + } + return out +} + +// Returns (min(x, y), max(x, y)), executes in constant time +func minmax(x, y *uint32) { + var xi uint32 = *x + var yi uint32 = *y + var xy uint32 = xi ^ yi + var c uint32 = yi - xi + c ^= xy & (c ^ yi ^ 0x80000000) + c >>= 31 + c = -c + c &= xy + *x = xi ^ c + *y = yi ^ c +} + +// Sorts the array of unsigned integers +func cryptoSortUint32(x []uint32, n int) { + if n < 2 { + return + } + top := 1 + + for top < n-top { + top += top + } + + for p := top; p > 0; p >>= 1 { + for i := 0; i < n-p; i++ { + if i&p == 0 { + minmax(&x[i], &x[i+p]) + } + } + for q := top; q > p; q >>= 1 { + for i := 0; i < n-q; i++ { + if i&p == 0 { + minmax(&x[i+p], &x[i+q]) + } + } + } + } +} + +// Sorting to generate short polynomial +func shortFromList(in []int32) []small { + out := make([]small, p) + L := make([]uint32, p) + + var neg2, neg3 int = -2, -3 + + for i := 0; i < w; i++ { + L[i] = uint32(in[i]) & uint32((neg2)) + } + + for i := w; i < p; i++ { + L[i] = (uint32(in[i]) & uint32((neg3))) | 1 + } + + cryptoSortUint32(L, p) + + for i := 0; i < p; i++ { + out[i] = small((L[i] & 3) - 1) + } + return out +} + +// Underlying hash function + +// The input byte array, in, is prepended by the byte b +// and its SHA-512 hash is calculated +// Only the first 32 bytes of the hash are returned +// e.g., b = 0 means out = Hash0(in) +func hashPrefix(out []byte, b int, in []byte, inlen int) { + x := make([]byte, inlen+1) + h := make([]byte, 64) + + x[0] = byte(b) + copy(x[1:], in) + + hash := sha512.New() + hash.Write([]byte(x)) + h = hash.Sum(nil) + + copy(out, h[:32]) + +} + +// Higher level randomness +// Returns a random unsigned integer +// generator can be passed for deterministic number generation +func urandom32(seed []byte) uint32 { + + c := make([]byte, 4) + var out [4]uint32 + + if seed != nil { + copy(c, seed) + } else { + cryptoRand.Read(c) + + } + + 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] +} + +// Generates a random short polynomial +func shortRandom(seed []byte) []small { + + L := make([]uint32, p) + + if seed != nil { + for i := 0; i < p; i++ { + L[i] = urandom32(seed[i*4 : i*4+4]) + } + } else { + for i := 0; i < p; i++ { + L[i] = urandom32(nil) + } + } + + // Converts uint32 array to int32 array + L_int32 := make([]int32, p) + for i := 0; i < len(L); i++ { + L_int32[i] = int32(L[i]) + } + out := shortFromList(L_int32) + + return out + +} + +// NTRU LPRime Core + +// (G,A),a = keyGen(G); leaves G unchanged +func keyGen(A []Fq, a []small, G []Fq, seed []byte) { + + copy(a, shortRandom(seed)) + aG := rqMultSmall(G, a) + copy(A, round(aG)) +} + +// B,T = encrypt(r,(G,A),b) +func encrypt(r []int8, G []Fq, A []Fq, b []small) ([]Fq, []int8) { + T := make([]int8, I) + bG := rqMultSmall(G, b) + B := round(bG) + bA := rqMultSmall(A, b) + + for i := 0; i < I; i++ { + T[i] = top(fqFreeze(int32(bA[i]) + int32(r[i])*q12)) + } + + return B, T +} + +// r = decrypt((B,T),a) +func decrypt(B []Fq, T []int8, a []small) []int8 { + + r := make([]int8, I) + + aB := rqMultSmall(B, a) + + for i := 0; i < I; i++ { + r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) + } + + return r +} + +// Encoding I-bit inputs +type Inputs [I]int8 + +func inputsEncode(s []byte, r Inputs) { + + for i := 0; i < I; i++ { + s[i>>3] |= byte(r[i] << (i & 7)) + } + +} + +// Expand + +func expand(L []uint32, k []byte) { + temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) + aes_nonce := make([]byte, aes.BlockSize) // IV + + block, err := aes.NewCipher(k[:32]) + if err != nil { + panic(err) + } + + ciphertext := make([]byte, len(temp)) // same length as plaintext + + stream := cipher.NewCTR(block, aes_nonce) + stream.XORKeyStream(ciphertext, temp) + + // convert byte to uint32 + for i := 0; i < len(temp); i++ { + L[i] = uint32(ciphertext[i]) + } + + for i := 0; i < p; i++ { + var L0 uint32 = L[4*i] + var L1 uint32 = L[4*i+1] + var L2 uint32 = L[4*i+2] + var L3 uint32 = L[4*i+3] + L[i] = L0 + (L1 << 8) + (L2 << 16) + (L3 << 24) + } +} + +// generator, hashShort +// G = generator(k) +func generator(k []byte) (G []Fq) { + G = make([]Fq, p) + L := make([]uint32, 4*p) + expand(L, k) + for i := 0; i < p; i++ { + G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) + } + return G +} + +// out = hashShort(r) +func hashShort(r Inputs) (out []small) { + s := make([]byte, inputsBytes) + inputsEncode(s, r) + h := make([]byte, hashBytes) + L := make([]uint32, 4*p) + L_int32 := make([]int32, p) + + hashPrefix(h, 5, s, len(s)) + + expand(L, h) + + // convert []uint32 to []int32 + for i := 0; i < p; i++ { + L_int32[i] = int32(L[i]) + } + out = shortFromList(L_int32) + return out +} + +// NTRU LPRime expand + +// (S,A),a = xKeyGen() +func xKeyGen(S []byte, A []Fq, a []small, seed []byte) { + + if seed == nil { + cryptoRand.Read(S) + } else { + copy(S, seed[:seedBytes]) + seed = seed[seedBytes:] + } + + G := generator(S) + + keyGen(A, a, G, seed) +} + +// B,T = xEncrypt(r,(S,A)) +func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { + G := generator(S) + + // convert []int8 to Inputs + var r_inputs Inputs + for i := 0; i < len(r); i++ { + r_inputs[i] = r[i] + } + + b := hashShort(r_inputs) + + return encrypt(r, G, A, b) +} + +// Encoding small polynomials (including short polynomials) + +// Transform polynomial in R to bytes +// these are the only functions that rely on p mod 4 = 1 */ +func smallEncode(s []byte, f []small) { + var x small + var index int = 0 + for i := 0; i < p/4; i++ { + x = f[index] + 1 + index++ + + x += (f[index] + 1) << 2 + index++ + x += (f[index] + 1) << 4 + index++ + x += (f[index] + 1) << 6 + index++ + + s[0] = byte(x) + s = s[1:] + } + x = f[index] + 1 + + s[0] = byte(x) +} + +// Transform bytes into polynomial in R +func smallDecode(f []small, s []byte) { + var index int = 0 + var x byte + + for i := 0; i < p/4; i++ { + x = s[0] + s = s[1:] + + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + index++ + } + x = s[0] + f[index] = ((small)(x & 3)) - 1 +} + +// Encoding rounded polynomials + +// Transform rounded polynomials to bytes +func roundedEncode(s []byte, r []Fq) { + + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16((int32((r[i])+q12) * 10923) >> 15) + M[i] = (q + 2) / 3 + } + internal.Encode(s, R, M, p) +} + +// Transform bytes to rounded polynomials +func roundedDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = (q + 2) / 3 + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = Fq(R[i]*3 - q12) + } + +} + +// Encoding top polynomials + +func topEncode(T []int8) (s []byte) { + s = make([]byte, topBytes) + for i := 0; i < topBytes; i++ { + s[i] = byte(T[2*i] + (T[2*i+1] << 4)) + + } + return s +} + +func topDecode(s []byte) (T []int8) { + + T = make([]int8, 2*topBytes+1) + + for i := 0; i < topBytes; i++ { + T[2*i] = int8(s[i] & 15) + T[2*i+1] = int8(s[i] >> 4) + } + return T +} + +// Streamlined NTRU Prime Core plus encoding + +func inputsRandom(seed []byte) (r Inputs) { + + s := make([]byte, inputsBytes) + + if seed != nil { + copy(s, seed) + } else { + cryptoRand.Read(s) + } + + for i := 0; i < I; i++ { + r[i] = int8(1 & (s[i>>3] >> (i & 7))) + } + + return r +} + +// Generates public key and private key +// pk,sk = zKeyGen() +func zKeyGen(pk []byte, sk []byte, seed []byte) { + A := make([]Fq, p) + a := make([]small, p) + + xKeyGen(pk, A, a, seed) + + pk = pk[seedBytes:] + roundedEncode(pk, A) + + smallEncode(sk, a) +} + +// c = zEncrypt(r,pk) +func zEncrypt(c []byte, r Inputs, pk []byte) { + A := make([]Fq, p) + + roundedDecode(A, pk[seedBytes:]) + B, T := xEncrypt(r[:], pk[:seedBytes], A) + roundedEncode(c, B) + c = c[roundedBytes:] + + copy(c, topEncode(T)) +} + +// r = zDecrypt(C,sk) +func zDecrypt(r *Inputs, c []byte, sk []byte) { + a := make([]small, p) + B := make([]Fq, p) + + smallDecode(a, sk) + roundedDecode(B, c) + T := topDecode(c[roundedBytes:]) + copy(r[:], decrypt(B, T, a)) +} + +// Confirmation hash + +// h = hashConfirm(r,pk,cache); cache is Hash4(pk) +func hashConfirm(h []byte, r []byte, pk []byte, cache []byte) { + x := make([]byte, inputsBytes+hashBytes) + + copy(x, r) + copy(x[inputsBytes:], cache) + + hashPrefix(h, 2, x, len(x)) + +} + +// Session-key hash + +// k = hashSession(b,y,z) +func hashSession(k []byte, b int, y []byte, z []byte) { + x := make([]byte, inputsBytes+ciphertextsBytes+confirmBytes) + copy(x[:inputsBytes], y) + copy(x[inputsBytes:], z) + + hashPrefix(k, b, x, len(x)) +} + +// Streamlined NTRU Prime + +// pk,sk = kemKeyGen() +func kemKeyGen(pk []byte, sk []byte, seed []byte) { + + if seed == nil { + seed = make([]byte, KeySeedSize) + cryptoRand.Read(seed) + } + + if len(seed) != KeySeedSize { + panic("seed must be of length KeySeedSize") + } + + zKeyGen(pk, sk, seed[:seedBytes+p*4]) + seed = seed[seedBytes+p*4:] + + sk = sk[secretKeysBytes:] + + copy(sk, pk) + sk = sk[publicKeysBytes:] + + if seed != nil { + copy(sk[:inputsBytes], seed) + + } else { + cryptoRand.Read(sk[:inputsBytes]) + } + sk = sk[inputsBytes:] + hashPrefix(sk, 4, pk, publicKeysBytes) + +} + +// c,r_enc = hide(r,pk,cache); cache is Hash4(pk) +func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { + inputsEncode(r_enc, r) + + zEncrypt(c, r, pk) + c = c[ciphertextsBytes:] + hashConfirm(c, r_enc, pk, cache) + +} + +// Takes as input a public key +// Returns ciphertext and shared key +// c,k = encap(pk) +func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { + + if seed != nil && len(seed) != EncapsulationSeedSize { + panic("seed must be of length EncapsulationSeedSize") + } + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + r_enc := make([]byte, inputsBytes) + cache := make([]byte, hashBytes) + + hashPrefix(cache, 4, pk.pk[:], publicKeysBytes) + r := inputsRandom(seed) + hide(c, r_enc, r, pk.pk[:], cache) + hashSession(k, 1, r_enc, c) +} + +// Returns 0 if matching ciphertext+confirm, else -1 +func ciphertextsDiffMask(c []byte, c2 []byte) int { + var differentbits uint16 = 0 + var len int = ciphertextsBytes + confirmBytes + + for i := 0; i < len; i++ { + differentbits |= uint16((c[i]) ^ (c2[i])) + } + return int((1 & ((differentbits - 1) >> 8)) - 1) + +} + +// Returns shared key from ciphertext and private key +// k = decap(c,sk) +func (priv *PrivateKey) DecapsulateTo(ss []byte, ct []byte) { + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(ss) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + sk := priv.sk[:] + pk := sk[secretKeysBytes:] + rho := pk[publicKeysBytes:] + cache := rho[inputsBytes:] + var r Inputs + + r_enc := make([]byte, inputsBytes) + cnew := make([]byte, ciphertextsBytes+confirmBytes) + + zDecrypt(&r, ct, sk) + hide(cnew, r_enc, r, pk, cache) + var mask int = ciphertextsDiffMask(ct, cnew) + + for i := 0; i < inputsBytes; i++ { + r_enc[i] ^= byte(mask & int(r_enc[i]^rho[i])) + } + hashSession(ss, 1+mask, r_enc, ct) +} + +// The structure of the private key is given by the following segments: +// The secret key, the public key, entropy and the hash of the public key +type PrivateKey struct { + sk [PrivateKeySize]byte +} + +type PublicKey struct { + pk [PublicKeySize]byte +} + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "{{.Pkg}}" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + copy(ret[:], sk.sk[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + return bytes.Equal(sk.sk[:], oth.sk[:]) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + return bytes.Equal(pk.pk[:], oth.pk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + var pk [PublicKeySize]byte + skey, _ := sk.MarshalBinary() + ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] + copy(pk[:], ppk[:]) + return &PublicKey{pk: pk} +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + copy(ret[:], pk.pk[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + kemKeyGen(pk[:], sk[:], nil) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk}, nil + +} + +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + + kemKeyGen(pk[:], sk[:], seed) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk} +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, nil) + return ct, ss, nil + +} + +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) (ct, ss []byte, err error) { + + if len(seed) != EncapsulationSeedSize { + return nil, nil, kem.ErrSeedSize + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, seed) + return ct, ss, nil +} + + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + ssk, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + ss := [SharedKeySize]byte{} + + ssk.DecapsulateTo(ss[:], ct) + + return ss[:], nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + pk := [PublicKeySize]byte{} + copy(pk[:], buf) + return &PublicKey{pk: pk}, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + sk := [PrivateKeySize]byte{} + copy(sk[:], buf) + return &PrivateKey{sk: sk}, nil +} diff --git a/kem/ntruprime/templates/sntrup.templ.go b/kem/ntruprime/templates/sntrup.templ.go new file mode 100644 index 000000000..de9cdd309 --- /dev/null +++ b/kem/ntruprime/templates/sntrup.templ.go @@ -0,0 +1,965 @@ +// +build ignore +// The previous line (and this one up to the warning below) is removed by the +// template generator. + +// Code generated from sntrup.templ.go. DO NOT EDIT. + +// Package {{.Pkg}} implements the IND-CCA2 secure key encapsulation mechanism +// {{.Pkg}} as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +package {{.Pkg}} + +import ( + "bytes" + cryptoRand "crypto/rand" + "crypto/sha512" + + "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/pke/ntruprime/kem" + "github.com/cloudflare/circl/kem/ntruprime/internal" + ntrup "github.com/cloudflare/circl/pke/ntruprime/{{.Pkg}}" +) + +type( + small int8 + Fq int16 + Inputs [p]small +) + +const ( + p = ntrup.P + q = ntrup.Q + q12 = ((q - 1) / 2) + roundedBytes = ntrup.RoundedBytes + rqBytes = ntrup.RqBytes + w = ntrup.W + + + hashBytes = 32 + + smallBytes = ((p + 3) / 4) + + inputsBytes = smallBytes + ciphertextsBytes = roundedBytes + secretKeysBytes = (2 * smallBytes) + publicKeysBytes = rqBytes + + confirmBytes = 32 +) + +const ( + // Size of the established shared key. + SharedKeySize = ntrup.SharedKeySize + + // Size of the encapsulated shared key. + CiphertextSize = ntrup.CiphertextSize + + // Size of a packed public key. + PublicKeySize = ntrup.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = ntrup.PrivateKeySize +) + +// Arithmetic operations over GF(3) + +// A polynomial of R has all of its coefficients in (-1,0,1) +// F3 is always represented as -1,0,1 +// so ZZ_fromF3 is a no-op + +// x must not be close to top int16 +func f3Freeze(x int16) small { + return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 +} + +// Arithmetic operations over GF(q) + +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +func fqFreeze(x int32) Fq { + return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) +} + +// Calculates reciprocal of Fq +func fqRecip(a1 Fq) Fq { + var i int = 1 + ai := a1 + + for i < (q - 2) { + ai = fqFreeze(int32(a1) * int32(ai)) + i += 1 + } + return ai +} + + +// Returns 0 if the weight w is equal to r +// otherwise returns -1 +func weightwMask(r []small) int { + var weight int = 0 + + for i := 0; i < p; i++ { + weight += int(r[i]) & 1 + } + + // returns -1 if non zero + // otherwise returns 0 if weight==w + return internal.Int16_nonzero_mask(int16(weight - w)) + +} + +/* R3_fromR(R_fromRq(r)) */ +func r3FromRq(r []Fq) []small { + out := make([]small, p) + for i := 0; i < p; i++ { + out[i] = small(f3Freeze(int16(r[i]))) + } + return out +} + +// h = f*g in the ring R3 +func r3Mult(f []small, g []small) (h []small) { + h = make([]small, p) + fg := make([]small, p+p-1) + var result small + var i, j int + + for i = 0; i < p; i++ { + result = 0 + for j = 0; j <= i; j++ { + result = f3Freeze(int16(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 = f3Freeze(int16(result + f[j]*g[i-j])) + } + fg[i] = result + } + + for i = p + p - 2; i >= p; i-- { + fg[i-p] = f3Freeze(int16(fg[i-p] + fg[i])) + fg[i-p+1] = f3Freeze(int16(fg[i-p+1] + fg[i])) + } + + for i = 0; i < p; i++ { + h[i] = fg[i] + } + + return h + +} + +// Calculates the reciprocal of R3 polynomials +// Returns 0 if recip succeeded; else -1 +func r3Recip(in []small) ([]small, int) { + out := make([]small, p) + f := make([]small, p+1) + g := make([]small, p+1) + v := make([]small, p+1) + r := make([]small, p+1) + + var sign int + + r[0] = 1 + f[0] = 1 + + f[p-1] = -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 = int(-g[0] * f[0]) + var swap int = int(internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0]))) + delta ^= swap & int(delta^-delta) + delta += 1 + + for i := 0; i < p+1; i++ { + t := swap & int(f[i]^g[i]) + f[i] ^= small(t) + g[i] ^= small(t) + t = swap & int(v[i]^r[i]) + v[i] ^= small(t) + r[i] ^= small(t) + } + for i := 0; i < p+1; i++ { + g[i] = f3Freeze(int16(int(g[i]) + sign*int(f[i]))) + } + + for i := 0; i < p+1; i++ { + r[i] = f3Freeze(int16(int(r[i]) + sign*int(v[i]))) + } + + for i := 0; i < p; i++ { + g[i] = g[i+1] + } + + g[p] = 0 + + } + sign = int(f[0]) + + for i := 0; i < p; i++ { + + out[i] = small(sign * int(v[p-1-i])) + } + + return out, internal.Int16_nonzero_mask(int16(delta)) + +} + +// Polynomials mod q + +// h = f*g in the ring Rq */ +func rqMultSmall(f []Fq, g []small) (h []Fq) { + h = make([]Fq, p) + fg := make([]Fq, p+p-1) + var result Fq + + for i := 0; i < p; i++ { + result = 0 + for j := 0; j <= i; j++ { + result = fqFreeze(int32(result) + int32(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 = fqFreeze(int32(result) + int32(f[j])*(int32)(g[i-j])) + } + fg[i] = result + } + + for i := p + p - 2; i >= p; i-- { + fg[i-p] = fqFreeze(int32(fg[i-p] + fg[i])) + fg[i-p+1] = fqFreeze(int32(fg[i-p+1] + fg[i])) + + } + + for i := 0; i < p; i++ { + h[i] = fg[i] + } + return h +} + +// h = 3f in Rq +func rqMult3(f []Fq) (h []Fq) { + h = make([]Fq, p) + for i := 0; i < p; i++ { + h[i] = fqFreeze(int32(3 * f[i])) + } + return h +} + +// Returns 0 if recip succeeded; else -1 +// out = 1/(3*in) in Rq +func rqRecip3(in []small) ([]Fq, int) { + out := make([]Fq, p) + f := make([]Fq, p+1) + g := make([]Fq, p+1) + v := make([]Fq, p+1) + r := make([]Fq, p+1) + + var swap, t int + var f0, g0 int32 + + r[0] = fqRecip(3) + f[0] = 1 + f[p-1] = -1 + f[p] = -1 + + for i := 0; i < p; i++ { + g[p-1-i] = Fq(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 = internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0])) + delta ^= swap & (delta ^ -delta) + delta += 1 + + for i := 0; i < p+1; i++ { + t = swap & int(f[i]^g[i]) + f[i] ^= Fq(t) + g[i] ^= Fq(t) + t = swap & int(v[i]^r[i]) + v[i] ^= Fq(t) + r[i] ^= Fq(t) + } + + f0 = int32(f[0]) + g0 = int32(g[0]) + + for i := 0; i < p+1; i++ { + g[i] = fqFreeze(f0*int32(g[i]) - g0*int32(f[i])) + } + for i := 0; i < p+1; i++ { + r[i] = fqFreeze(f0*int32(r[i]) - g0*int32(v[i])) + } + + for i := 0; i < p; i++ { + g[i] = g[i+1] + } + g[p] = 0 + } + + scale := Fq(fqRecip(f[0])) + for i := 0; i < p; i++ { + out[i] = fqFreeze(int32(scale) * (int32)(v[p-1-i])) + } + + return out, internal.Int16_nonzero_mask(int16(delta)) + +} + +// Rounding all coefficients of a polynomial to the nearest multiple of 3 +// Rounded polynomials mod q +func round(a []Fq) []Fq { + out := make([]Fq, p) + for i := 0; i < p; i++ { + out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) + } + return out +} + + +// Returns (min(x, y), max(x, y)), executes in constant time +func minmax(x , y *uint32) { + var xi uint32 = *x + var yi uint32 = *y + var xy uint32 = xi ^ yi + var c uint32 = yi - xi + c ^= xy & (c ^ yi ^ 0x80000000) + c >>= 31 + c = -c + c &= xy + *x = xi ^ c + *y = yi ^ c +} + +// Sorts the array of unsigned integers +func cryptoSortUint32(x []uint32, n int) { + if n < 2 { + return + } + top := 1 + + for top < n-top { + top += top + } + + for p := top; p > 0; p >>= 1 { + for i := 0; i < n-p; i++ { + if i&p == 0 { + minmax(&x[i], &x[i+p]) + } + } + for q := top; q > p; q >>= 1 { + for i := 0; i < n-q; i++ { + if i&p == 0 { + minmax(&x[i+p], &x[i+q]) + } + } + } + } +} + +// Sorting to generate short polynomial +func shortFromList(in []int32) []small { + out := make([]small, p) + L := make([]uint32, p) + + var neg2, neg3 int = -2, -3 + + for i := 0; i < w; i++ { + L[i] = uint32(in[i]) & uint32((neg2)) + } + + for i := w; i < p; i++ { + L[i] = (uint32(in[i]) & uint32((neg3))) | 1 + } + + cryptoSortUint32(L, p) + + for i := 0; i < p; i++ { + out[i] = small((L[i] & 3) - 1) + } + return out +} + +// Underlying hash function + +// The input byte array, in, is prepended by the byte b +// and its SHA-512 hash is calculated +// Only the first 32 bytes of the hash are returned +// e.g., b = 0 means out = Hash0(in) +func hashPrefix(out []byte, b int, in []byte, inlen int) { + x := make([]byte, inlen+1) + h := make([]byte, 64) + + x[0] = byte(b) + copy(x[1:], in) + + hash := sha512.New() + hash.Write([]byte(x)) + h = hash.Sum(nil) + + copy(out, h[:32]) + +} + +// Higher level randomness +// Returns a random unsigned integer +// A generator can be passed for deterministic number generation +func urandom32(gen *nist.DRBG) uint32 { + + c := make([]byte, 4) + var out [4]uint32 + + if gen != nil { + gen.Fill(c) + } else { + cryptoRand.Read(c) + + } + + 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] +} + +// Generates a random short polynomial +func shortRandom(gen *nist.DRBG) []small { + + L := make([]uint32, p) + + for i := 0; i < p; i++ { + L[i] = urandom32(gen) + } + + // Converts uint32 array to int32 array + L_int32 := make([]int32, p) + for i := 0; i < len(L); i++ { + L_int32[i] = int32(L[i]) + } + out := shortFromList(L_int32) + + return out + +} + +// Generates a random list of small +func smallRandom(gen *nist.DRBG) []small { + + out := make([]small, p) + for i := 0; i < p; i++ { + out[i] = small(((urandom32(gen)&0x3fffffff)*3)>>30) - 1 + } + return out +} + +// Streamlined NTRU Prime Core + +// h,(f,ginv) = keyGen() +func keyGen(gen *nist.DRBG) (h []Fq, f []small, ginv []small) { + g := make([]small, p) + var err int + for { + g = smallRandom(gen) + ginv, err = r3Recip(g) + if err == 0 { + break + } + + } + + f = shortRandom(gen) + + finv, _ := rqRecip3(f) /* always works */ + h = rqMultSmall(finv, g) + return h, f, ginv +} + +// c = encrypt(r,h) +func encrypt(r []small, h []Fq) []Fq { + + hr := rqMultSmall(h, r) + c := round(hr) + + return c + +} + +// r = decrypt(c,(f,ginv)) +func decrypt(c []Fq, f []small, ginv []small) []small { + r := make([]small, p) + cf := make([]Fq, p) + + cf = rqMultSmall(c, f) + cf3 := rqMult3(cf) + e := r3FromRq(cf3) + ev := r3Mult(e, ginv) + + mask := weightwMask(ev) /* 0 if weight w, else -1 */ + for i := 0; i < w; i++ { + r[i] = ((ev[i] ^ 1) & small(^mask)) ^ 1 + } + + for i := w; i < p; i++ { + r[i] = ev[i] & small(^mask) + } + return r + +} + +// Encoding small polynomials (including short polynomials) + +// Transform polynomial in R to bytes +// these are the only functions that rely on p mod 4 = 1 */ +func smallEncode(s []byte, f []small) { + var x small + var index int = 0 + for i := 0; i < p/4; i++ { + x = f[index] + 1 + index++ + + x += (f[index] + 1) << 2 + index++ + x += (f[index] + 1) << 4 + index++ + x += (f[index] + 1) << 6 + index++ + + s[0] = byte(x) + s = s[1:] + } + x = f[index] + 1 + + s[0] = byte(x) +} + +// Transform bytes into polynomial in R +func smallDecode(f []small, s []byte) { + var index int = 0 + var x byte + + for i := 0; i < p/4; i++ { + x = s[0] + s = s[1:] + + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + x >>= 2 + index++ + f[index] = ((small)(x & 3)) - 1 + index++ + } + x = s[0] + f[index] = ((small)(x & 3)) - 1 +} + +// Encoding general polynomials + +// Transform polynomials in R/q to bytes +func rqEncode(s []byte, r []Fq) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16(r[i] + q12) + M[i] = q + } + internal.Encode(s, R, M, p) +} + +// Transform polynomials in R/q from bytes +func rqDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = q + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = ((Fq)(R[i])) - q12 + } + +} + +// Encoding rounded polynomials + +// Transform rounded polynomials to bytes +func roundedEncode(s []byte, r []Fq) { + + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + R[i] = uint16((int32((r[i])+q12) * 10923) >> 15) + M[i] = (q + 2) / 3 + } + internal.Encode(s, R, M, p) +} + +// Transform bytes to rounded polynomials +func roundedDecode(r []Fq, s []byte) { + R := make([]uint16, p) + M := make([]uint16, p) + + for i := 0; i < p; i++ { + M[i] = (q + 2) / 3 + } + internal.Decode(R, s, M, p) + for i := 0; i < p; i++ { + r[i] = Fq(R[i]*3 - q12) + } + +} + +// Streamlined NTRU Prime Core plus encoding + + +// Generates public key and private key +// pk,sk = zKeyGen() +func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { + + h, f, v := keyGen(gen) + + rqEncode(pk, h) + smallEncode(sk, f) + sk = sk[smallBytes:] + smallEncode(sk, v) + +} + +// C = zEncrypt(r,pk) +func zEncrypt(C []byte, r Inputs, pk []byte) { + h := make([]Fq, p) + rqDecode(h, pk) + c := encrypt(r[:], h) + roundedEncode(C, c) +} + +// r = zDecrypt(C,sk) +func zDecrypt(r *Inputs, C []byte, sk []byte) { + f := make([]small, p) + v := make([]small, p) + c := make([]Fq, p) + + smallDecode(f, sk) + sk = sk[smallBytes:] + smallDecode(v, sk) + roundedDecode(c, C) + + copy(r[:], decrypt(c, f, v)) +} + +// Confirmation hash + +// h = hashConfirm(r,pk,cache); cache is Hash4(pk) +func hashConfirm(h []byte, r []byte, pk []byte, cache []byte) { + x := make([]byte, hashBytes*2) + + hashPrefix(x, 3, r, inputsBytes) + + copy(x[hashBytes:],cache[:hashBytes]) + + hashPrefix(h, 2, x, len(x)) + +} + +// Session-key hash + +// k = hashSession(b,y,z) +func hashSession(k []byte, b int, y []byte, z []byte) { + x := make([]byte, hashBytes+ciphertextsBytes+confirmBytes) + + hashPrefix(x, 3, y, inputsBytes) + + copy(x[hashBytes:],z[:ciphertextsBytes+confirmBytes]) + + hashPrefix(k, b, x, len(x)) + +} + +// Streamlined NTRU Prime + +// pk,sk = kemKeyGen() +func kemKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { + zKeyGen(pk, sk, gen) + sk = sk[secretKeysBytes:] + + copy(sk, pk) + sk = sk[publicKeysBytes:] + + if gen != nil { + gen.Fill(sk[:inputsBytes]) + + } else { + cryptoRand.Read(sk[:inputsBytes]) + } + sk = sk[inputsBytes:] + hashPrefix(sk, 4, pk, publicKeysBytes) + +} + +// c,r_enc = hide(r,pk,cache); cache is Hash4(pk) +func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { + smallEncode(r_enc, r[:]) + zEncrypt(c, r, pk) + c = c[ciphertextsBytes:] + hashConfirm(c, r_enc, pk, cache) + +} + +// Takes as input a public key +// Returns ciphertext and shared key +// c,k = encap(pk) +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + pk:=pub.pk[:] + + var r Inputs + r_enc := make([]byte, inputsBytes) + cache := make([]byte, hashBytes) + + hashPrefix(cache, 4, pk, publicKeysBytes) + copy(r[:], shortRandom(gen)) + hide(c, r_enc, r, pk, cache) + hashSession(k, 1, r_enc, c) + +} + +// Returns 0 if matching ciphertext+confirm, else -1 +func ciphertexts_diff_mask(c []byte, c2 []byte) int { + var differentbits uint16 = 0 + var len int = ciphertextsBytes + confirmBytes + + for i := 0; i < len; i++ { + differentbits |= uint16((c[i]) ^ (c2[i])) + } + return int((1 & ((differentbits - 1) >> 8)) - 1) + +} + +// Returns shared key from ciphertext and private key +// k = decap(c,sk) +func (priv *PrivateKey) DecapsulateTo (k []byte, c []byte) { + if len(c) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(k) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + sk:=priv.sk[:] + + pk := sk[secretKeysBytes:] + rho := pk[publicKeysBytes:] + cache := rho[inputsBytes:] + var r Inputs + + r_enc := make([]byte, inputsBytes) + cnew := make([]byte, ciphertextsBytes+confirmBytes) + + zDecrypt(&r, c, sk) + hide(cnew, r_enc, r, pk, cache) + var mask int = ciphertexts_diff_mask(c, cnew) + + for i := 0; i < inputsBytes; i++ { + r_enc[i] ^= byte(mask & int(r_enc[i]^rho[i])) + } + hashSession(k, 1+mask, r_enc, c) +} + +// The structure of the private key is given by the following segments: +// The secret key, the public key, entropy and the hash of the public key +type PrivateKey struct { + sk [PrivateKeySize]byte +} + +type PublicKey struct { + pk [PublicKeySize]byte +} + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "{{.Pkg}}" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + copy(ret[:], sk.sk[:]) + return ret[:], nil +} + + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + return bytes.Equal(sk.sk[:], oth.sk[:]) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + return bytes.Equal(pk.pk[:], oth.pk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + var pk [PublicKeySize]byte + skey, _ := sk.MarshalBinary() + ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] + copy(pk[:], ppk[:]) + return &PublicKey{pk: pk} +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + copy(ret[:], pk.pk[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + kemKeyGen(pk[:], sk[:], nil) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk}, nil + +} + +func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { + var pk [PublicKeySize]byte + var sk [PrivateKeySize]byte + + kemKeyGen(pk[:], sk[:], gen) + + return &PublicKey{pk: pk}, &PrivateKey{sk: sk} +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, nil) + + return ct, ss, nil + +} + +func (*scheme)EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DRBG) (ct, ss []byte, err error) { + + if gen == nil { + panic("A nist DRBG must be provided") + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + pub.EncapsulateTo(ct, ss, gen) + + return ct, ss, nil +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + ssk, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + ss := [SharedKeySize]byte{} + + ssk.DecapsulateTo(ss[:], ct) + + return ss[:], nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + pk := [PublicKeySize]byte{} + copy(pk[:], buf) + return &PublicKey{pk: pk}, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + sk := [PrivateKeySize]byte{} + copy(sk[:], buf) + return &PrivateKey{sk: sk}, nil +} diff --git a/kem/schemes/schemes.go b/kem/schemes/schemes.go index a33e7b96e..5f74cbd6c 100644 --- a/kem/schemes/schemes.go +++ b/kem/schemes/schemes.go @@ -14,6 +14,9 @@ // // FrodoKEM-640-SHAKE // Kyber512, Kyber768, Kyber1024 +// Kyber512, Kyber768, Kyber1024 +// SNTRUP653, SNTRUP761, SNTRUP857, SNTRUP953, SNTRUP1013, SNTRUP1277 + package schemes import ( @@ -26,6 +29,12 @@ import ( "github.com/cloudflare/circl/kem/kyber/kyber1024" "github.com/cloudflare/circl/kem/kyber/kyber512" "github.com/cloudflare/circl/kem/kyber/kyber768" + "github.com/cloudflare/circl/kem/ntruprime/ntrulpr1013" + "github.com/cloudflare/circl/kem/ntruprime/ntrulpr1277" + "github.com/cloudflare/circl/kem/ntruprime/ntrulpr653" + "github.com/cloudflare/circl/kem/ntruprime/ntrulpr761" + "github.com/cloudflare/circl/kem/ntruprime/ntrulpr857" + "github.com/cloudflare/circl/kem/ntruprime/ntrulpr953" ) var allSchemes = [...]kem.Scheme{ @@ -43,6 +52,12 @@ var allSchemes = [...]kem.Scheme{ hybrid.Kyber768X448(), hybrid.Kyber1024X448(), hybrid.P256Kyber768Draft00(), + ntrulpr1013.Scheme(), + ntrulpr1277.Scheme(), + ntrulpr653.Scheme(), + ntrulpr761.Scheme(), + ntrulpr857.Scheme(), + ntrulpr953.Scheme(), } var allSchemeNames map[string]kem.Scheme diff --git a/pke/ntruprime/gen.go b/pke/ntruprime/gen.go new file mode 100644 index 000000000..cc9c788e2 --- /dev/null +++ b/pke/ntruprime/gen.go @@ -0,0 +1,115 @@ +package main + +import ( + "bytes" + "go/format" + "io/ioutil" + "strings" + "text/template" +) + +type StreamlinedInstance struct { + Name string + P, Q, Rounded_bytes, Rq_bytes, W, SharedKeySize, CiphertextSize, PublicKeySize, PrivateKeySize int +} + +func (m StreamlinedInstance) Pkg() string { + return strings.ToLower(m.Name) +} + +type LPRInstance struct { + Name string + P, Q, Rounded_bytes, Rq_bytes, W, SharedKeySize, CiphertextSize, PublicKeySize, PrivateKeySize int + Tau0, Tau1, Tau2, Tau3 int +} + +func (m LPRInstance) Pkg() string { + return strings.ToLower(m.Name) +} + +var ( + StreamlinedInstances = []StreamlinedInstance{ + {Name: "SNTRUP761", P: 761, Q: 4591, Rounded_bytes: 1007, Rq_bytes: 1158, W: 286, SharedKeySize: 32, CiphertextSize: 1039, PublicKeySize: 1158, PrivateKeySize: 1763}, + {Name: "SNTRUP653", P: 653, Q: 4621, Rounded_bytes: 865, Rq_bytes: 994, W: 288, SharedKeySize: 32, CiphertextSize: 897, PublicKeySize: 994, PrivateKeySize: 1518}, + {Name: "SNTRUP857", P: 857, Q: 5167, Rounded_bytes: 1152, Rq_bytes: 1322, W: 322, SharedKeySize: 32, CiphertextSize: 1184, PublicKeySize: 1322, PrivateKeySize: 1999}, + {Name: "SNTRUP953", P: 953, Q: 6343, Rounded_bytes: 1317, Rq_bytes: 1505, W: 396, SharedKeySize: 32, CiphertextSize: 1349, PublicKeySize: 1505, PrivateKeySize: 2254}, + {Name: "SNTRUP1013", P: 1013, Q: 7177, Rounded_bytes: 1423, Rq_bytes: 1623, W: 448, SharedKeySize: 32, CiphertextSize: 1455, PublicKeySize: 1623, PrivateKeySize: 2417}, + {Name: "SNTRUP1277", P: 1277, Q: 7879, Rounded_bytes: 1815, Rq_bytes: 2067, W: 492, SharedKeySize: 32, CiphertextSize: 1847, PublicKeySize: 2067, PrivateKeySize: 3059}, + } + LPRInstances = []LPRInstance{ + {Name: "NTRULPR653", P: 653, Q: 4621, Rounded_bytes: 865, W: 252, Tau0: 2175, Tau1: 113, Tau2: 2031, Tau3: 290, SharedKeySize: 32, CiphertextSize: 1025, PublicKeySize: 897, PrivateKeySize: 1125}, + {Name: "NTRULPR761", P: 761, Q: 4591, Rounded_bytes: 1007, W: 250, Tau0: 2156, Tau1: 114, Tau2: 2007, Tau3: 287, SharedKeySize: 32, CiphertextSize: 1167, PublicKeySize: 1039, PrivateKeySize: 1294}, + {Name: "NTRULPR857", P: 857, Q: 5167, Rounded_bytes: 1152, W: 281, Tau0: 2433, Tau1: 101, Tau2: 2265, Tau3: 324, SharedKeySize: 32, CiphertextSize: 1312, PublicKeySize: 1184, PrivateKeySize: 1463}, + {Name: "NTRULPR953", P: 953, Q: 6343, Rounded_bytes: 1317, W: 345, Tau0: 2997, Tau1: 82, Tau2: 2798, Tau3: 400, SharedKeySize: 32, CiphertextSize: 1477, PublicKeySize: 1349, PrivateKeySize: 1652}, + {Name: "NTRULPR1013", P: 1013, Q: 7177, Rounded_bytes: 1423, W: 392, Tau0: 3367, Tau1: 73, Tau2: 3143, Tau3: 449, SharedKeySize: 32, CiphertextSize: 1583, PublicKeySize: 1455, PrivateKeySize: 1773}, + {Name: "NTRULPR1277", P: 1277, Q: 7879, Rounded_bytes: 1815, W: 429, Tau0: 3724, Tau1: 66, Tau2: 3469, Tau3: 496, SharedKeySize: 32, CiphertextSize: 1975, PublicKeySize: 1847, PrivateKeySize: 2231}, + } + TemplateWarning = "// Code generated from" +) + +func main() { + generatePackageFiles() + generateLPRFiles() +} + +func generatePackageFiles() { + template, err := template.ParseFiles("templates/sntrup.params.templ.go") + if err != nil { + panic(err) + } + + for _, mode := range StreamlinedInstances { + buf := new(bytes.Buffer) + err := template.Execute(buf, mode) + if err != nil { + panic(err) + } + + // Formating output code + code, err := format.Source(buf.Bytes()) + if err != nil { + panic("error formating code") + } + + res := string(code) + offset := strings.Index(res, TemplateWarning) + if offset == -1 { + panic("Missing template warning in pkg.templ.go") + } + err = ioutil.WriteFile(mode.Pkg()+"/params.go", []byte(res[offset:]), 0o644) + if err != nil { + panic(err) + } + } +} + +func generateLPRFiles() { + template, err := template.ParseFiles("templates/ntrulpr.params.templ.go") + if err != nil { + panic(err) + } + + for _, mode := range LPRInstances { + buf := new(bytes.Buffer) + err := template.Execute(buf, mode) + if err != nil { + panic(err) + } + + // Formating output code + code, err := format.Source(buf.Bytes()) + if err != nil { + panic("error formating code") + } + + res := string(code) + offset := strings.Index(res, TemplateWarning) + if offset == -1 { + panic("Missing template warning in pkg.templ.go") + } + err = ioutil.WriteFile(mode.Pkg()+"/params.go", []byte(res[offset:]), 0o644) + if err != nil { + panic(err) + } + } +} diff --git a/pke/ntruprime/kem/kem.go b/pke/ntruprime/kem/kem.go new file mode 100644 index 000000000..dab6ec3ec --- /dev/null +++ b/pke/ntruprime/kem/kem.go @@ -0,0 +1,101 @@ +// Package kem provides a unified interface for Streamlined NTRU Prime KEM schemes. +// +// # A register of schemes is available in the package +// +// github.com/cloudflare/circl/pke/ntruprime/kem/schemes/sntrup +package kem + +import ( + "encoding" + "errors" + + "github.com/cloudflare/circl/internal/nist" +) + +// A KEM public key +type PublicKey interface { + // Returns the scheme for this public key + Scheme() Scheme + + encoding.BinaryMarshaler + Equal(PublicKey) bool +} + +// A KEM private key +type PrivateKey interface { + // Returns the scheme for this private key + Scheme() Scheme + + encoding.BinaryMarshaler + Equal(PrivateKey) bool + Public() PublicKey +} + +// A Scheme represents a specific instance of a NTRU PRIME KEM. +type Scheme interface { + // Name of the scheme + Name() string + + // GenerateKeyPair creates a new key pair. + GenerateKeyPair() (PublicKey, PrivateKey, error) + + // Encapsulate generates a shared key ss for the public key and + // encapsulates it into a ciphertext ct. + Encapsulate(pk PublicKey) (ct, ss []byte, err error) + + // Returns the shared key encapsulated in ciphertext ct for the + // private key sk. + Decapsulate(sk PrivateKey, ct []byte) ([]byte, error) + + // Unmarshals a PublicKey from the provided buffer. + UnmarshalBinaryPublicKey([]byte) (PublicKey, error) + + // Unmarshals a PrivateKey from the provided buffer. + UnmarshalBinaryPrivateKey([]byte) (PrivateKey, error) + + // Size of encapsulated keys. + CiphertextSize() int + + // Size of established shared keys. + SharedKeySize() int + + // Size of packed private keys. + PrivateKeySize() int + + // Size of packed public keys. + PublicKeySize() int + + // DeriveKeyPair deterministicallly derives a pair of keys from a nist DRBG. + // Panics if the DRBG is nil + DeriveKeyPairFromGen(gen *nist.DRBG) (PublicKey, PrivateKey) + + // EncapsulateDeterministically generates a shared key ss for the public + // key deterministically from the given nist DRBG and encapsulates it into + // a ciphertext ct. If unsure, you're better off using Encapsulate(). + EncapsulateDeterministicallyFromGen(pk PublicKey, gen *nist.DRBG) ( + ct, ss []byte, err error) +} + +var ( + // ErrTypeMismatch is the error used if types of, for instance, private + // and public keys don't match + ErrTypeMismatch = errors.New("types mismatch") + + // ErrPubKeySize is the error used if the provided public key is of + // the wrong size. + ErrPubKeySize = errors.New("wrong size for public key") + + // ErrCiphertextSize is the error used if the provided ciphertext + // is of the wrong size. + ErrCiphertextSize = errors.New("wrong size for ciphertext") + + // ErrPrivKeySize is the error used if the provided private key is of + // the wrong size. + ErrPrivKeySize = errors.New("wrong size for private key") + + // ErrPubKey is the error used if the provided public key is invalid. + ErrPubKey = errors.New("invalid public key") + + // ErrCipherText is the error used if the provided ciphertext is invalid. + ErrCipherText = errors.New("invalid ciphertext") +) diff --git a/pke/ntruprime/kem/schemes/sntrup/schemes.go b/pke/ntruprime/kem/schemes/sntrup/schemes.go new file mode 100644 index 000000000..3bb2edadf --- /dev/null +++ b/pke/ntruprime/kem/schemes/sntrup/schemes.go @@ -0,0 +1,49 @@ +// Package schemes contains a register of Streamlined NTRU Prime KEM schemes. +// +// # Schemes Implemented +// +// Post-quantum kems: +// +// SNTRUP653, SNTRUP761, SNTRUP857, SNTRUP953, SNTRUP1013, SNTRUP1277 +package schemes + +import ( + "strings" + + "github.com/cloudflare/circl/kem/ntruprime/sntrup1013" + "github.com/cloudflare/circl/kem/ntruprime/sntrup1277" + "github.com/cloudflare/circl/kem/ntruprime/sntrup653" + "github.com/cloudflare/circl/kem/ntruprime/sntrup761" + "github.com/cloudflare/circl/kem/ntruprime/sntrup857" + "github.com/cloudflare/circl/kem/ntruprime/sntrup953" + "github.com/cloudflare/circl/pke/ntruprime/kem" +) + +var allSchemes = [...]kem.Scheme{ + sntrup653.Scheme(), + sntrup761.Scheme(), + sntrup857.Scheme(), + sntrup953.Scheme(), + sntrup1013.Scheme(), + sntrup1277.Scheme(), +} + +var allSchemeNames map[string]kem.Scheme + +func init() { + allSchemeNames = make(map[string]kem.Scheme) + for _, scheme := range allSchemes { + allSchemeNames[strings.ToLower(scheme.Name())] = scheme + } +} + +// ByName returns the scheme with the given name and nil if it is not +// supported. +// +// Names are case insensitive. +func ByName(name string) kem.Scheme { + return allSchemeNames[strings.ToLower(name)] +} + +// All returns all KEM schemes supported. +func All() []kem.Scheme { a := allSchemes; return a[:] } diff --git a/pke/ntruprime/ntrulpr1013/params.go b/pke/ntruprime/ntrulpr1013/params.go new file mode 100644 index 000000000..0f767d5a9 --- /dev/null +++ b/pke/ntruprime/ntrulpr1013/params.go @@ -0,0 +1,31 @@ +// Code generated from ntrulpr.params.templ.go +package ntruprime + +const ( + P = 1013 + Q = 7177 + RoundedBytes = 1423 + + W = 392 + Tau0 = 3367 + Tau1 = 73 + Tau2 = 3143 + Tau3 = 449 + + I = 256 +) + +const ( + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = 1583 + + // Size of a packed public key. + PublicKeySize = 1455 + + // Size of a packed private key. + PrivateKeySize = 1773 +) diff --git a/pke/ntruprime/ntrulpr1277/params.go b/pke/ntruprime/ntrulpr1277/params.go new file mode 100644 index 000000000..3837b235b --- /dev/null +++ b/pke/ntruprime/ntrulpr1277/params.go @@ -0,0 +1,31 @@ +// Code generated from ntrulpr.params.templ.go +package ntruprime + +const ( + P = 1277 + Q = 7879 + RoundedBytes = 1815 + + W = 429 + Tau0 = 3724 + Tau1 = 66 + Tau2 = 3469 + Tau3 = 496 + + I = 256 +) + +const ( + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = 1975 + + // Size of a packed public key. + PublicKeySize = 1847 + + // Size of a packed private key. + PrivateKeySize = 2231 +) diff --git a/pke/ntruprime/ntrulpr653/params.go b/pke/ntruprime/ntrulpr653/params.go new file mode 100644 index 000000000..01b2164de --- /dev/null +++ b/pke/ntruprime/ntrulpr653/params.go @@ -0,0 +1,31 @@ +// Code generated from ntrulpr.params.templ.go +package ntruprime + +const ( + P = 653 + Q = 4621 + RoundedBytes = 865 + + W = 252 + Tau0 = 2175 + Tau1 = 113 + Tau2 = 2031 + Tau3 = 290 + + I = 256 +) + +const ( + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = 1025 + + // Size of a packed public key. + PublicKeySize = 897 + + // Size of a packed private key. + PrivateKeySize = 1125 +) diff --git a/pke/ntruprime/ntrulpr761/params.go b/pke/ntruprime/ntrulpr761/params.go new file mode 100644 index 000000000..085eeb002 --- /dev/null +++ b/pke/ntruprime/ntrulpr761/params.go @@ -0,0 +1,31 @@ +// Code generated from ntrulpr.params.templ.go +package ntruprime + +const ( + P = 761 + Q = 4591 + RoundedBytes = 1007 + + W = 250 + Tau0 = 2156 + Tau1 = 114 + Tau2 = 2007 + Tau3 = 287 + + I = 256 +) + +const ( + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = 1167 + + // Size of a packed public key. + PublicKeySize = 1039 + + // Size of a packed private key. + PrivateKeySize = 1294 +) diff --git a/pke/ntruprime/ntrulpr857/params.go b/pke/ntruprime/ntrulpr857/params.go new file mode 100644 index 000000000..d8bdaea78 --- /dev/null +++ b/pke/ntruprime/ntrulpr857/params.go @@ -0,0 +1,31 @@ +// Code generated from ntrulpr.params.templ.go +package ntruprime + +const ( + P = 857 + Q = 5167 + RoundedBytes = 1152 + + W = 281 + Tau0 = 2433 + Tau1 = 101 + Tau2 = 2265 + Tau3 = 324 + + I = 256 +) + +const ( + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = 1312 + + // Size of a packed public key. + PublicKeySize = 1184 + + // Size of a packed private key. + PrivateKeySize = 1463 +) diff --git a/pke/ntruprime/ntrulpr953/params.go b/pke/ntruprime/ntrulpr953/params.go new file mode 100644 index 000000000..236dac9bd --- /dev/null +++ b/pke/ntruprime/ntrulpr953/params.go @@ -0,0 +1,31 @@ +// Code generated from ntrulpr.params.templ.go +package ntruprime + +const ( + P = 953 + Q = 6343 + RoundedBytes = 1317 + + W = 345 + Tau0 = 2997 + Tau1 = 82 + Tau2 = 2798 + Tau3 = 400 + + I = 256 +) + +const ( + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = 1477 + + // Size of a packed public key. + PublicKeySize = 1349 + + // Size of a packed private key. + PrivateKeySize = 1652 +) diff --git a/pke/ntruprime/sntrup1013/params.go b/pke/ntruprime/sntrup1013/params.go new file mode 100644 index 000000000..c0705606d --- /dev/null +++ b/pke/ntruprime/sntrup1013/params.go @@ -0,0 +1,25 @@ +// Code generated from sntrup.params.templ.go. DO NOT EDIT. +package ntruprime + +const ( + P = 1013 + Q = 7177 + RoundedBytes = 1423 + RqBytes = 1623 + W = 448 +) + +const ( + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = 1455 + + // Size of a packed public key. + PublicKeySize = 1623 + + // Size of a packed private key. + PrivateKeySize = 2417 +) diff --git a/pke/ntruprime/sntrup1277/params.go b/pke/ntruprime/sntrup1277/params.go new file mode 100644 index 000000000..ffce7712d --- /dev/null +++ b/pke/ntruprime/sntrup1277/params.go @@ -0,0 +1,25 @@ +// Code generated from sntrup.params.templ.go. DO NOT EDIT. +package ntruprime + +const ( + P = 1277 + Q = 7879 + RoundedBytes = 1815 + RqBytes = 2067 + W = 492 +) + +const ( + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = 1847 + + // Size of a packed public key. + PublicKeySize = 2067 + + // Size of a packed private key. + PrivateKeySize = 3059 +) diff --git a/pke/ntruprime/sntrup653/params.go b/pke/ntruprime/sntrup653/params.go new file mode 100644 index 000000000..c2623eb74 --- /dev/null +++ b/pke/ntruprime/sntrup653/params.go @@ -0,0 +1,25 @@ +// Code generated from sntrup.params.templ.go. DO NOT EDIT. +package ntruprime + +const ( + P = 653 + Q = 4621 + RoundedBytes = 865 + RqBytes = 994 + W = 288 +) + +const ( + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = 897 + + // Size of a packed public key. + PublicKeySize = 994 + + // Size of a packed private key. + PrivateKeySize = 1518 +) diff --git a/pke/ntruprime/sntrup761/params.go b/pke/ntruprime/sntrup761/params.go new file mode 100644 index 000000000..dde2fd93c --- /dev/null +++ b/pke/ntruprime/sntrup761/params.go @@ -0,0 +1,25 @@ +// Code generated from sntrup.params.templ.go. DO NOT EDIT. +package ntruprime + +const ( + P = 761 + Q = 4591 + RoundedBytes = 1007 + RqBytes = 1158 + W = 286 +) + +const ( + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = 1039 + + // Size of a packed public key. + PublicKeySize = 1158 + + // Size of a packed private key. + PrivateKeySize = 1763 +) diff --git a/pke/ntruprime/sntrup857/params.go b/pke/ntruprime/sntrup857/params.go new file mode 100644 index 000000000..c88d27812 --- /dev/null +++ b/pke/ntruprime/sntrup857/params.go @@ -0,0 +1,25 @@ +// Code generated from sntrup.params.templ.go. DO NOT EDIT. +package ntruprime + +const ( + P = 857 + Q = 5167 + RoundedBytes = 1152 + RqBytes = 1322 + W = 322 +) + +const ( + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = 1184 + + // Size of a packed public key. + PublicKeySize = 1322 + + // Size of a packed private key. + PrivateKeySize = 1999 +) diff --git a/pke/ntruprime/sntrup953/params.go b/pke/ntruprime/sntrup953/params.go new file mode 100644 index 000000000..437b3efe6 --- /dev/null +++ b/pke/ntruprime/sntrup953/params.go @@ -0,0 +1,25 @@ +// Code generated from sntrup.params.templ.go. DO NOT EDIT. +package ntruprime + +const ( + P = 953 + Q = 6343 + RoundedBytes = 1317 + RqBytes = 1505 + W = 396 +) + +const ( + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = 1349 + + // Size of a packed public key. + PublicKeySize = 1505 + + // Size of a packed private key. + PrivateKeySize = 2254 +) diff --git a/pke/ntruprime/templates/ntrulpr.params.templ.go b/pke/ntruprime/templates/ntrulpr.params.templ.go new file mode 100644 index 000000000..e2a5e44fa --- /dev/null +++ b/pke/ntruprime/templates/ntrulpr.params.templ.go @@ -0,0 +1,35 @@ +// +build ignore +// The previous line (and this one up to the warning below) is removed by the +// template generator. + +// Code generated from ntrulpr.params.templ.go +package ntruprime + +const ( + P = {{.P}} + Q = {{.Q}} + RoundedBytes = {{.Rounded_bytes}} + + W = {{.W}} + Tau0 = {{.Tau0}} + Tau1 = {{.Tau1}} + Tau2 = {{.Tau2}} + Tau3 = {{.Tau3}} + + I = 256 +) + +const ( + + // Size of the established shared key. + SharedKeySize = {{.SharedKeySize}} + + // Size of the encapsulated shared key. + CiphertextSize = {{.CiphertextSize}} + + // Size of a packed public key. + PublicKeySize = {{.PublicKeySize}} + + // Size of a packed private key. + PrivateKeySize = {{.PrivateKeySize}} +) diff --git a/pke/ntruprime/templates/sntrup.params.templ.go b/pke/ntruprime/templates/sntrup.params.templ.go new file mode 100644 index 000000000..d1e4a83ab --- /dev/null +++ b/pke/ntruprime/templates/sntrup.params.templ.go @@ -0,0 +1,28 @@ +// +build ignore +// The previous line (and this one up to the warning below) is removed by the +// template generator. + +// Code generated from sntrup.params.templ.go. DO NOT EDIT. +package ntruprime +const ( + P = {{.P}} + Q = {{.Q}} + RoundedBytes = {{.Rounded_bytes}} + RqBytes = {{.Rq_bytes}} + W = {{.W}} +) + +const ( + + // Size of the established shared key. + SharedKeySize = {{.SharedKeySize}} + + // Size of the encapsulated shared key. + CiphertextSize = {{.CiphertextSize}} + + // Size of a packed public key. + PublicKeySize = {{.PublicKeySize}} + + // Size of a packed private key. + PrivateKeySize = {{.PrivateKeySize}} +) From cb7d06a5506376bfacb37d850313dc95bd2af6c4 Mon Sep 17 00:00:00 2001 From: Keelan10 Date: Sun, 18 Dec 2022 13:49:07 +0400 Subject: [PATCH 2/5] Optimize code --- kem/ntruprime/kat_test.go | 24 +-- kem/ntruprime/ntrulpr1013/ntruprime.go | 142 +++++++--------- kem/ntruprime/ntrulpr1277/ntruprime.go | 142 +++++++--------- kem/ntruprime/ntrulpr653/ntruprime.go | 142 +++++++--------- kem/ntruprime/ntrulpr761/ntruprime.go | 142 +++++++--------- kem/ntruprime/ntrulpr857/ntruprime.go | 142 +++++++--------- kem/ntruprime/ntrulpr953/ntruprime.go | 142 +++++++--------- kem/ntruprime/sntrup1013/ntruprime.go | 139 ++++++++-------- kem/ntruprime/sntrup1277/ntruprime.go | 139 ++++++++-------- kem/ntruprime/sntrup653/ntruprime.go | 139 ++++++++-------- kem/ntruprime/sntrup761/ntruprime.go | 139 ++++++++-------- kem/ntruprime/sntrup857/ntruprime.go | 139 ++++++++-------- kem/ntruprime/sntrup953/ntruprime.go | 139 ++++++++-------- kem/ntruprime/templates/ntrulpr.templ.go | 140 +++++++--------- kem/ntruprime/templates/sntrup.templ.go | 142 ++++++++-------- kem/schemes/schemes_test.go | 7 + pke/ntruprime/kem/kem.go | 7 +- .../kem/schemes/sntrup/schemes_test.go | 152 ++++++++++++++++++ 18 files changed, 1083 insertions(+), 1075 deletions(-) create mode 100644 pke/ntruprime/kem/schemes/sntrup/schemes_test.go diff --git a/kem/ntruprime/kat_test.go b/kem/ntruprime/kat_test.go index bee29fe9b..a9e9b755d 100644 --- a/kem/ntruprime/kat_test.go +++ b/kem/ntruprime/kat_test.go @@ -15,20 +15,21 @@ func TestPQCgenStreamlinedKATKem(t *testing.T) { kats := []struct { name string want string + p int }{ // Computed from reference implementation - {"sntrup653", "82249a46c1bc538e980a2335764c81f70701e6374eed3e1d0457e18c57ec2cee64280dcc75504c2648eb3e37ab3eee37955c1114d851f755a28cc997aba781c8"}, - {"sntrup761", "1a687f42261c47fe4421b35c5d9faf035433fcb2101458680c66c8d54caafec5fb767ea7725d6681ab100912ef06c38d88862a5d2d86786af2989b7dad33813a"}, - {"sntrup857", "79473d6c709dbbc99528886bf2c1d033c409dab1755299154f33232bc57ba1fbe91322fcb741df5252d575a77aa5ca000d52a44c17f1ab64a299884d0f101519"}, - {"sntrup953", "6fe0cf3b8cb62a3011c1870ec9eb3cd8825c06993a213e01ecd0f21f5dee670838fe1c89dd120086a09e8227496a00e22188c8f947618a35764c5a24726ce16c"}, - {"sntrup1013", "195a38eb843fdda53241f65b641ab925f61fb1cf5b0fffcb5891115da121a85174a796d69c75b86c4e92193453155aef9d27ce53aa268076617be55ee6f5da4f"}, - {"sntrup1277", "ada8a0cbe6b077dc563874fd372f60779bbee1524f576c2931cf9c804163b9632163610d6e380f889170cdf4d9928de0782368a43413f2b6976897ba0e19a828"}, + {"sntrup653", "82249a46c1bc538e980a2335764c81f70701e6374eed3e1d0457e18c57ec2cee64280dcc75504c2648eb3e37ab3eee37955c1114d851f755a28cc997aba781c8", 653}, + {"sntrup761", "1a687f42261c47fe4421b35c5d9faf035433fcb2101458680c66c8d54caafec5fb767ea7725d6681ab100912ef06c38d88862a5d2d86786af2989b7dad33813a", 761}, + {"sntrup857", "79473d6c709dbbc99528886bf2c1d033c409dab1755299154f33232bc57ba1fbe91322fcb741df5252d575a77aa5ca000d52a44c17f1ab64a299884d0f101519", 857}, + {"sntrup953", "6fe0cf3b8cb62a3011c1870ec9eb3cd8825c06993a213e01ecd0f21f5dee670838fe1c89dd120086a09e8227496a00e22188c8f947618a35764c5a24726ce16c", 953}, + {"sntrup1013", "195a38eb843fdda53241f65b641ab925f61fb1cf5b0fffcb5891115da121a85174a796d69c75b86c4e92193453155aef9d27ce53aa268076617be55ee6f5da4f", 1013}, + {"sntrup1277", "ada8a0cbe6b077dc563874fd372f60779bbee1524f576c2931cf9c804163b9632163610d6e380f889170cdf4d9928de0782368a43413f2b6976897ba0e19a828", 1277}, } for _, kat := range kats { kat := kat t.Run(kat.name, func(t *testing.T) { - testPQCgenStreamlinedKATKem(t, kat.name, kat.want) + testPQCgenStreamlinedKATKem(t, kat.name, kat.want, kat.p) }) } } @@ -115,13 +116,14 @@ func testPQCgenLPRKATKem(t *testing.T, name, expected string, p int) { } } -func testPQCgenStreamlinedKATKem(t *testing.T, name, expected string) { +func testPQCgenStreamlinedKATKem(t *testing.T, name, expected string, p int) { scheme := sntrupSchemes.ByName(name) if scheme == nil { t.Fatal() } var seed [48]byte + eseed := make([]byte, scheme.EncapsulationSeedSize()) for i := 0; i < 48; i++ { seed[i] = byte(i) @@ -143,7 +145,11 @@ func testPQCgenStreamlinedKATKem(t *testing.T, name, expected string) { ppk, _ := pk.MarshalBinary() psk, _ := sk.MarshalBinary() - ct, ss1, _ := scheme.EncapsulateDeterministicallyFromGen(pk, &g2) + for i := 0; i < p; i++ { + g2.Fill(eseed[4*i : 4*i+4]) + } + ct, ss1, _ := scheme.EncapsulateDeterministically(pk, eseed) + ss2, _ := scheme.Decapsulate(sk, ct) if !bytes.Equal(ss1, ss2) { t.Fatal() diff --git a/kem/ntruprime/ntrulpr1013/ntruprime.go b/kem/ntruprime/ntrulpr1013/ntruprime.go index 229c608ff..a847cb7c4 100644 --- a/kem/ntruprime/ntrulpr1013/ntruprime.go +++ b/kem/ntruprime/ntrulpr1013/ntruprime.go @@ -108,8 +108,7 @@ func right(T int8) Fq { // Polynomials mod q // h = f*g in the ring Rq */ -func rqMultSmall(f []Fq, g []small) (h []Fq) { - h = make([]Fq, p) +func rqMultSmall(h []Fq, f []Fq, g []small) { fg := make([]Fq, p+p-1) var result Fq @@ -138,17 +137,14 @@ func rqMultSmall(f []Fq, g []small) (h []Fq) { for i := 0; i < p; i++ { h[i] = fg[i] } - return h } // Rounding all coefficients of a polynomial to the nearest multiple of 3 // Rounded polynomials mod q -func round(a []Fq) []Fq { - out := make([]Fq, p) +func round(out []Fq, a []Fq) { for i := 0; i < p; i++ { out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) } - return out } // Returns (min(x, y), max(x, y)), executes in constant time @@ -193,8 +189,7 @@ func cryptoSortUint32(x []uint32, n int) { } // Sorting to generate short polynomial -func shortFromList(in []int32) []small { - out := make([]small, p) +func shortFromList(out []small, in []int32) { L := make([]uint32, p) var neg2, neg3 int = -2, -3 @@ -212,7 +207,6 @@ func shortFromList(in []int32) []small { for i := 0; i < p; i++ { out[i] = small((L[i] & 3) - 1) } - return out } // Underlying hash function @@ -223,14 +217,14 @@ func shortFromList(in []int32) []small { // e.g., b = 0 means out = Hash0(in) func hashPrefix(out []byte, b int, in []byte, inlen int) { x := make([]byte, inlen+1) - h := make([]byte, 64) + // h := make([]byte, 64) x[0] = byte(b) copy(x[1:], in) hash := sha512.New() hash.Write([]byte(x)) - h = hash.Sum(nil) + h := hash.Sum(nil) copy(out, h[:32]) @@ -240,26 +234,17 @@ func hashPrefix(out []byte, b int, in []byte, inlen int) { // Returns a random unsigned integer // generator can be passed for deterministic number generation func urandom32(seed []byte) uint32 { - - c := make([]byte, 4) var out [4]uint32 - if seed != nil { - copy(c, seed) - } else { - cryptoRand.Read(c) - - } - - out[0] = uint32(c[0]) - out[1] = uint32(c[1]) << 8 - out[2] = uint32(c[2]) << 16 - out[3] = uint32(c[3]) << 24 + out[0] = uint32(seed[0]) + out[1] = uint32(seed[1]) << 8 + out[2] = uint32(seed[2]) << 16 + out[3] = uint32(seed[3]) << 24 return out[0] + out[1] + out[2] + out[3] } // Generates a random short polynomial -func shortRandom(seed []byte) []small { +func shortRandom(out []small, seed []byte) { L := make([]uint32, p) @@ -278,42 +263,40 @@ func shortRandom(seed []byte) []small { for i := 0; i < len(L); i++ { L_int32[i] = int32(L[i]) } - out := shortFromList(L_int32) - - return out - + shortFromList(out, L_int32) } // NTRU LPRime Core // (G,A),a = keyGen(G); leaves G unchanged func keyGen(A []Fq, a []small, G []Fq, seed []byte) { - - copy(a, shortRandom(seed)) - aG := rqMultSmall(G, a) - copy(A, round(aG)) + aG := make([]Fq, p) + shortRandom(a, seed) + rqMultSmall(aG, G, a) + round(A, aG) } // B,T = encrypt(r,(G,A),b) -func encrypt(r []int8, G []Fq, A []Fq, b []small) ([]Fq, []int8) { - T := make([]int8, I) - bG := rqMultSmall(G, b) - B := round(bG) - bA := rqMultSmall(A, b) +func encrypt(B []Fq, T []int8, r []int8, G []Fq, A []Fq, b []small) { + bG := make([]Fq, p) + bA := make([]Fq, p) + + rqMultSmall(bG, G, b) + round(B, bG) + rqMultSmall(bA, A, b) for i := 0; i < I; i++ { T[i] = top(fqFreeze(int32(bA[i]) + int32(r[i])*q12)) } - - return B, T } // r = decrypt((B,T),a) func decrypt(B []Fq, T []int8, a []small) []int8 { + aB := make([]Fq, p) r := make([]int8, I) - aB := rqMultSmall(B, a) + rqMultSmall(aB, B, a) for i := 0; i < I; i++ { r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) @@ -336,18 +319,17 @@ func inputsEncode(s []byte, r Inputs) { // Expand func expand(L []uint32, k []byte) { - temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) - aes_nonce := make([]byte, aes.BlockSize) // IV + temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) + ciphertext := make([]byte, aes.BlockSize+len(temp)) block, err := aes.NewCipher(k[:32]) if err != nil { panic(err) } - ciphertext := make([]byte, len(temp)) // same length as plaintext - - stream := cipher.NewCTR(block, aes_nonce) - stream.XORKeyStream(ciphertext, temp) + stream := cipher.NewCTR(block, ciphertext[:aes.BlockSize]) + stream.XORKeyStream(ciphertext[aes.BlockSize:], temp) + ciphertext = ciphertext[aes.BlockSize:] // convert byte to uint32 for i := 0; i < len(temp); i++ { @@ -365,18 +347,16 @@ func expand(L []uint32, k []byte) { // generator, hashShort // G = generator(k) -func generator(k []byte) (G []Fq) { - G = make([]Fq, p) +func generator(G []Fq, k []byte) { L := make([]uint32, 4*p) expand(L, k) for i := 0; i < p; i++ { G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) } - return G } // out = hashShort(r) -func hashShort(r Inputs) (out []small) { +func hashShort(out []small, r Inputs) { s := make([]byte, inputsBytes) inputsEncode(s, r) h := make([]byte, hashBytes) @@ -391,8 +371,7 @@ func hashShort(r Inputs) (out []small) { for i := 0; i < p; i++ { L_int32[i] = int32(L[i]) } - out = shortFromList(L_int32) - return out + shortFromList(out, L_int32) } // NTRU LPRime expand @@ -400,21 +379,21 @@ func hashShort(r Inputs) (out []small) { // (S,A),a = xKeyGen() func xKeyGen(S []byte, A []Fq, a []small, seed []byte) { - if seed == nil { - cryptoRand.Read(S) - } else { - copy(S, seed[:seedBytes]) - seed = seed[seedBytes:] - } + copy(S, seed[:seedBytes]) + seed = seed[seedBytes:] + G := make([]Fq, p) - G := generator(S) + generator(G, S) keyGen(A, a, G, seed) } // B,T = xEncrypt(r,(S,A)) -func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { - G := generator(S) +func xEncrypt(B []Fq, T []int8, r []int8, S []byte, A []Fq) { + G := make([]Fq, p) + + generator(G, S) + b := make([]small, p) // convert []int8 to Inputs var r_inputs Inputs @@ -422,9 +401,9 @@ func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { r_inputs[i] = r[i] } - b := hashShort(r_inputs) + hashShort(b, r_inputs) - return encrypt(r, G, A, b) + encrypt(B, T, r, G, A, b) } // Encoding small polynomials (including short polynomials) @@ -510,13 +489,11 @@ func roundedDecode(r []Fq, s []byte) { // Encoding top polynomials -func topEncode(T []int8) (s []byte) { - s = make([]byte, topBytes) +func topEncode(s []byte, T []int8) { for i := 0; i < topBytes; i++ { s[i] = byte(T[2*i] + (T[2*i+1] << 4)) } - return s } func topDecode(s []byte) (T []int8) { @@ -533,19 +510,9 @@ func topDecode(s []byte) (T []int8) { // Streamlined NTRU Prime Core plus encoding func inputsRandom(seed []byte) (r Inputs) { - - s := make([]byte, inputsBytes) - - if seed != nil { - copy(s, seed) - } else { - cryptoRand.Read(s) - } - for i := 0; i < I; i++ { - r[i] = int8(1 & (s[i>>3] >> (i & 7))) + r[i] = int8(1 & (seed[i>>3] >> (i & 7))) } - return r } @@ -566,13 +533,15 @@ func zKeyGen(pk []byte, sk []byte, seed []byte) { // c = zEncrypt(r,pk) func zEncrypt(c []byte, r Inputs, pk []byte) { A := make([]Fq, p) + B := make([]Fq, p) + T := make([]int8, I) roundedDecode(A, pk[seedBytes:]) - B, T := xEncrypt(r[:], pk[:seedBytes], A) + xEncrypt(B, T, r[:], pk[:seedBytes], A) roundedEncode(c, B) c = c[roundedBytes:] - copy(c, topEncode(T)) + topEncode(c, T) } // r = zDecrypt(C,sk) @@ -632,12 +601,8 @@ func kemKeyGen(pk []byte, sk []byte, seed []byte) { copy(sk, pk) sk = sk[publicKeysBytes:] - if seed != nil { - copy(sk[:inputsBytes], seed) + copy(sk[:inputsBytes], seed) - } else { - cryptoRand.Read(sk[:inputsBytes]) - } sk = sk[inputsBytes:] hashPrefix(sk, 4, pk, publicKeysBytes) @@ -658,7 +623,12 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { // c,k = encap(pk) func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { - if seed != nil && len(seed) != EncapsulationSeedSize { + if seed == nil { + seed = make([]byte, EncapsulationSeedSize) + cryptoRand.Read(seed) + } + + if len(seed) != EncapsulationSeedSize { panic("seed must be of length EncapsulationSeedSize") } if len(c) != CiphertextSize { diff --git a/kem/ntruprime/ntrulpr1277/ntruprime.go b/kem/ntruprime/ntrulpr1277/ntruprime.go index 8c983e926..f6ecffaa9 100644 --- a/kem/ntruprime/ntrulpr1277/ntruprime.go +++ b/kem/ntruprime/ntrulpr1277/ntruprime.go @@ -108,8 +108,7 @@ func right(T int8) Fq { // Polynomials mod q // h = f*g in the ring Rq */ -func rqMultSmall(f []Fq, g []small) (h []Fq) { - h = make([]Fq, p) +func rqMultSmall(h []Fq, f []Fq, g []small) { fg := make([]Fq, p+p-1) var result Fq @@ -138,17 +137,14 @@ func rqMultSmall(f []Fq, g []small) (h []Fq) { for i := 0; i < p; i++ { h[i] = fg[i] } - return h } // Rounding all coefficients of a polynomial to the nearest multiple of 3 // Rounded polynomials mod q -func round(a []Fq) []Fq { - out := make([]Fq, p) +func round(out []Fq, a []Fq) { for i := 0; i < p; i++ { out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) } - return out } // Returns (min(x, y), max(x, y)), executes in constant time @@ -193,8 +189,7 @@ func cryptoSortUint32(x []uint32, n int) { } // Sorting to generate short polynomial -func shortFromList(in []int32) []small { - out := make([]small, p) +func shortFromList(out []small, in []int32) { L := make([]uint32, p) var neg2, neg3 int = -2, -3 @@ -212,7 +207,6 @@ func shortFromList(in []int32) []small { for i := 0; i < p; i++ { out[i] = small((L[i] & 3) - 1) } - return out } // Underlying hash function @@ -223,14 +217,14 @@ func shortFromList(in []int32) []small { // e.g., b = 0 means out = Hash0(in) func hashPrefix(out []byte, b int, in []byte, inlen int) { x := make([]byte, inlen+1) - h := make([]byte, 64) + // h := make([]byte, 64) x[0] = byte(b) copy(x[1:], in) hash := sha512.New() hash.Write([]byte(x)) - h = hash.Sum(nil) + h := hash.Sum(nil) copy(out, h[:32]) @@ -240,26 +234,17 @@ func hashPrefix(out []byte, b int, in []byte, inlen int) { // Returns a random unsigned integer // generator can be passed for deterministic number generation func urandom32(seed []byte) uint32 { - - c := make([]byte, 4) var out [4]uint32 - if seed != nil { - copy(c, seed) - } else { - cryptoRand.Read(c) - - } - - out[0] = uint32(c[0]) - out[1] = uint32(c[1]) << 8 - out[2] = uint32(c[2]) << 16 - out[3] = uint32(c[3]) << 24 + out[0] = uint32(seed[0]) + out[1] = uint32(seed[1]) << 8 + out[2] = uint32(seed[2]) << 16 + out[3] = uint32(seed[3]) << 24 return out[0] + out[1] + out[2] + out[3] } // Generates a random short polynomial -func shortRandom(seed []byte) []small { +func shortRandom(out []small, seed []byte) { L := make([]uint32, p) @@ -278,42 +263,40 @@ func shortRandom(seed []byte) []small { for i := 0; i < len(L); i++ { L_int32[i] = int32(L[i]) } - out := shortFromList(L_int32) - - return out - + shortFromList(out, L_int32) } // NTRU LPRime Core // (G,A),a = keyGen(G); leaves G unchanged func keyGen(A []Fq, a []small, G []Fq, seed []byte) { - - copy(a, shortRandom(seed)) - aG := rqMultSmall(G, a) - copy(A, round(aG)) + aG := make([]Fq, p) + shortRandom(a, seed) + rqMultSmall(aG, G, a) + round(A, aG) } // B,T = encrypt(r,(G,A),b) -func encrypt(r []int8, G []Fq, A []Fq, b []small) ([]Fq, []int8) { - T := make([]int8, I) - bG := rqMultSmall(G, b) - B := round(bG) - bA := rqMultSmall(A, b) +func encrypt(B []Fq, T []int8, r []int8, G []Fq, A []Fq, b []small) { + bG := make([]Fq, p) + bA := make([]Fq, p) + + rqMultSmall(bG, G, b) + round(B, bG) + rqMultSmall(bA, A, b) for i := 0; i < I; i++ { T[i] = top(fqFreeze(int32(bA[i]) + int32(r[i])*q12)) } - - return B, T } // r = decrypt((B,T),a) func decrypt(B []Fq, T []int8, a []small) []int8 { + aB := make([]Fq, p) r := make([]int8, I) - aB := rqMultSmall(B, a) + rqMultSmall(aB, B, a) for i := 0; i < I; i++ { r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) @@ -336,18 +319,17 @@ func inputsEncode(s []byte, r Inputs) { // Expand func expand(L []uint32, k []byte) { - temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) - aes_nonce := make([]byte, aes.BlockSize) // IV + temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) + ciphertext := make([]byte, aes.BlockSize+len(temp)) block, err := aes.NewCipher(k[:32]) if err != nil { panic(err) } - ciphertext := make([]byte, len(temp)) // same length as plaintext - - stream := cipher.NewCTR(block, aes_nonce) - stream.XORKeyStream(ciphertext, temp) + stream := cipher.NewCTR(block, ciphertext[:aes.BlockSize]) + stream.XORKeyStream(ciphertext[aes.BlockSize:], temp) + ciphertext = ciphertext[aes.BlockSize:] // convert byte to uint32 for i := 0; i < len(temp); i++ { @@ -365,18 +347,16 @@ func expand(L []uint32, k []byte) { // generator, hashShort // G = generator(k) -func generator(k []byte) (G []Fq) { - G = make([]Fq, p) +func generator(G []Fq, k []byte) { L := make([]uint32, 4*p) expand(L, k) for i := 0; i < p; i++ { G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) } - return G } // out = hashShort(r) -func hashShort(r Inputs) (out []small) { +func hashShort(out []small, r Inputs) { s := make([]byte, inputsBytes) inputsEncode(s, r) h := make([]byte, hashBytes) @@ -391,8 +371,7 @@ func hashShort(r Inputs) (out []small) { for i := 0; i < p; i++ { L_int32[i] = int32(L[i]) } - out = shortFromList(L_int32) - return out + shortFromList(out, L_int32) } // NTRU LPRime expand @@ -400,21 +379,21 @@ func hashShort(r Inputs) (out []small) { // (S,A),a = xKeyGen() func xKeyGen(S []byte, A []Fq, a []small, seed []byte) { - if seed == nil { - cryptoRand.Read(S) - } else { - copy(S, seed[:seedBytes]) - seed = seed[seedBytes:] - } + copy(S, seed[:seedBytes]) + seed = seed[seedBytes:] + G := make([]Fq, p) - G := generator(S) + generator(G, S) keyGen(A, a, G, seed) } // B,T = xEncrypt(r,(S,A)) -func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { - G := generator(S) +func xEncrypt(B []Fq, T []int8, r []int8, S []byte, A []Fq) { + G := make([]Fq, p) + + generator(G, S) + b := make([]small, p) // convert []int8 to Inputs var r_inputs Inputs @@ -422,9 +401,9 @@ func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { r_inputs[i] = r[i] } - b := hashShort(r_inputs) + hashShort(b, r_inputs) - return encrypt(r, G, A, b) + encrypt(B, T, r, G, A, b) } // Encoding small polynomials (including short polynomials) @@ -510,13 +489,11 @@ func roundedDecode(r []Fq, s []byte) { // Encoding top polynomials -func topEncode(T []int8) (s []byte) { - s = make([]byte, topBytes) +func topEncode(s []byte, T []int8) { for i := 0; i < topBytes; i++ { s[i] = byte(T[2*i] + (T[2*i+1] << 4)) } - return s } func topDecode(s []byte) (T []int8) { @@ -533,19 +510,9 @@ func topDecode(s []byte) (T []int8) { // Streamlined NTRU Prime Core plus encoding func inputsRandom(seed []byte) (r Inputs) { - - s := make([]byte, inputsBytes) - - if seed != nil { - copy(s, seed) - } else { - cryptoRand.Read(s) - } - for i := 0; i < I; i++ { - r[i] = int8(1 & (s[i>>3] >> (i & 7))) + r[i] = int8(1 & (seed[i>>3] >> (i & 7))) } - return r } @@ -566,13 +533,15 @@ func zKeyGen(pk []byte, sk []byte, seed []byte) { // c = zEncrypt(r,pk) func zEncrypt(c []byte, r Inputs, pk []byte) { A := make([]Fq, p) + B := make([]Fq, p) + T := make([]int8, I) roundedDecode(A, pk[seedBytes:]) - B, T := xEncrypt(r[:], pk[:seedBytes], A) + xEncrypt(B, T, r[:], pk[:seedBytes], A) roundedEncode(c, B) c = c[roundedBytes:] - copy(c, topEncode(T)) + topEncode(c, T) } // r = zDecrypt(C,sk) @@ -632,12 +601,8 @@ func kemKeyGen(pk []byte, sk []byte, seed []byte) { copy(sk, pk) sk = sk[publicKeysBytes:] - if seed != nil { - copy(sk[:inputsBytes], seed) + copy(sk[:inputsBytes], seed) - } else { - cryptoRand.Read(sk[:inputsBytes]) - } sk = sk[inputsBytes:] hashPrefix(sk, 4, pk, publicKeysBytes) @@ -658,7 +623,12 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { // c,k = encap(pk) func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { - if seed != nil && len(seed) != EncapsulationSeedSize { + if seed == nil { + seed = make([]byte, EncapsulationSeedSize) + cryptoRand.Read(seed) + } + + if len(seed) != EncapsulationSeedSize { panic("seed must be of length EncapsulationSeedSize") } if len(c) != CiphertextSize { diff --git a/kem/ntruprime/ntrulpr653/ntruprime.go b/kem/ntruprime/ntrulpr653/ntruprime.go index 2c02d3df9..896f6c47f 100644 --- a/kem/ntruprime/ntrulpr653/ntruprime.go +++ b/kem/ntruprime/ntrulpr653/ntruprime.go @@ -108,8 +108,7 @@ func right(T int8) Fq { // Polynomials mod q // h = f*g in the ring Rq */ -func rqMultSmall(f []Fq, g []small) (h []Fq) { - h = make([]Fq, p) +func rqMultSmall(h []Fq, f []Fq, g []small) { fg := make([]Fq, p+p-1) var result Fq @@ -138,17 +137,14 @@ func rqMultSmall(f []Fq, g []small) (h []Fq) { for i := 0; i < p; i++ { h[i] = fg[i] } - return h } // Rounding all coefficients of a polynomial to the nearest multiple of 3 // Rounded polynomials mod q -func round(a []Fq) []Fq { - out := make([]Fq, p) +func round(out []Fq, a []Fq) { for i := 0; i < p; i++ { out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) } - return out } // Returns (min(x, y), max(x, y)), executes in constant time @@ -193,8 +189,7 @@ func cryptoSortUint32(x []uint32, n int) { } // Sorting to generate short polynomial -func shortFromList(in []int32) []small { - out := make([]small, p) +func shortFromList(out []small, in []int32) { L := make([]uint32, p) var neg2, neg3 int = -2, -3 @@ -212,7 +207,6 @@ func shortFromList(in []int32) []small { for i := 0; i < p; i++ { out[i] = small((L[i] & 3) - 1) } - return out } // Underlying hash function @@ -223,14 +217,14 @@ func shortFromList(in []int32) []small { // e.g., b = 0 means out = Hash0(in) func hashPrefix(out []byte, b int, in []byte, inlen int) { x := make([]byte, inlen+1) - h := make([]byte, 64) + // h := make([]byte, 64) x[0] = byte(b) copy(x[1:], in) hash := sha512.New() hash.Write([]byte(x)) - h = hash.Sum(nil) + h := hash.Sum(nil) copy(out, h[:32]) @@ -240,26 +234,17 @@ func hashPrefix(out []byte, b int, in []byte, inlen int) { // Returns a random unsigned integer // generator can be passed for deterministic number generation func urandom32(seed []byte) uint32 { - - c := make([]byte, 4) var out [4]uint32 - if seed != nil { - copy(c, seed) - } else { - cryptoRand.Read(c) - - } - - out[0] = uint32(c[0]) - out[1] = uint32(c[1]) << 8 - out[2] = uint32(c[2]) << 16 - out[3] = uint32(c[3]) << 24 + out[0] = uint32(seed[0]) + out[1] = uint32(seed[1]) << 8 + out[2] = uint32(seed[2]) << 16 + out[3] = uint32(seed[3]) << 24 return out[0] + out[1] + out[2] + out[3] } // Generates a random short polynomial -func shortRandom(seed []byte) []small { +func shortRandom(out []small, seed []byte) { L := make([]uint32, p) @@ -278,42 +263,40 @@ func shortRandom(seed []byte) []small { for i := 0; i < len(L); i++ { L_int32[i] = int32(L[i]) } - out := shortFromList(L_int32) - - return out - + shortFromList(out, L_int32) } // NTRU LPRime Core // (G,A),a = keyGen(G); leaves G unchanged func keyGen(A []Fq, a []small, G []Fq, seed []byte) { - - copy(a, shortRandom(seed)) - aG := rqMultSmall(G, a) - copy(A, round(aG)) + aG := make([]Fq, p) + shortRandom(a, seed) + rqMultSmall(aG, G, a) + round(A, aG) } // B,T = encrypt(r,(G,A),b) -func encrypt(r []int8, G []Fq, A []Fq, b []small) ([]Fq, []int8) { - T := make([]int8, I) - bG := rqMultSmall(G, b) - B := round(bG) - bA := rqMultSmall(A, b) +func encrypt(B []Fq, T []int8, r []int8, G []Fq, A []Fq, b []small) { + bG := make([]Fq, p) + bA := make([]Fq, p) + + rqMultSmall(bG, G, b) + round(B, bG) + rqMultSmall(bA, A, b) for i := 0; i < I; i++ { T[i] = top(fqFreeze(int32(bA[i]) + int32(r[i])*q12)) } - - return B, T } // r = decrypt((B,T),a) func decrypt(B []Fq, T []int8, a []small) []int8 { + aB := make([]Fq, p) r := make([]int8, I) - aB := rqMultSmall(B, a) + rqMultSmall(aB, B, a) for i := 0; i < I; i++ { r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) @@ -336,18 +319,17 @@ func inputsEncode(s []byte, r Inputs) { // Expand func expand(L []uint32, k []byte) { - temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) - aes_nonce := make([]byte, aes.BlockSize) // IV + temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) + ciphertext := make([]byte, aes.BlockSize+len(temp)) block, err := aes.NewCipher(k[:32]) if err != nil { panic(err) } - ciphertext := make([]byte, len(temp)) // same length as plaintext - - stream := cipher.NewCTR(block, aes_nonce) - stream.XORKeyStream(ciphertext, temp) + stream := cipher.NewCTR(block, ciphertext[:aes.BlockSize]) + stream.XORKeyStream(ciphertext[aes.BlockSize:], temp) + ciphertext = ciphertext[aes.BlockSize:] // convert byte to uint32 for i := 0; i < len(temp); i++ { @@ -365,18 +347,16 @@ func expand(L []uint32, k []byte) { // generator, hashShort // G = generator(k) -func generator(k []byte) (G []Fq) { - G = make([]Fq, p) +func generator(G []Fq, k []byte) { L := make([]uint32, 4*p) expand(L, k) for i := 0; i < p; i++ { G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) } - return G } // out = hashShort(r) -func hashShort(r Inputs) (out []small) { +func hashShort(out []small, r Inputs) { s := make([]byte, inputsBytes) inputsEncode(s, r) h := make([]byte, hashBytes) @@ -391,8 +371,7 @@ func hashShort(r Inputs) (out []small) { for i := 0; i < p; i++ { L_int32[i] = int32(L[i]) } - out = shortFromList(L_int32) - return out + shortFromList(out, L_int32) } // NTRU LPRime expand @@ -400,21 +379,21 @@ func hashShort(r Inputs) (out []small) { // (S,A),a = xKeyGen() func xKeyGen(S []byte, A []Fq, a []small, seed []byte) { - if seed == nil { - cryptoRand.Read(S) - } else { - copy(S, seed[:seedBytes]) - seed = seed[seedBytes:] - } + copy(S, seed[:seedBytes]) + seed = seed[seedBytes:] + G := make([]Fq, p) - G := generator(S) + generator(G, S) keyGen(A, a, G, seed) } // B,T = xEncrypt(r,(S,A)) -func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { - G := generator(S) +func xEncrypt(B []Fq, T []int8, r []int8, S []byte, A []Fq) { + G := make([]Fq, p) + + generator(G, S) + b := make([]small, p) // convert []int8 to Inputs var r_inputs Inputs @@ -422,9 +401,9 @@ func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { r_inputs[i] = r[i] } - b := hashShort(r_inputs) + hashShort(b, r_inputs) - return encrypt(r, G, A, b) + encrypt(B, T, r, G, A, b) } // Encoding small polynomials (including short polynomials) @@ -510,13 +489,11 @@ func roundedDecode(r []Fq, s []byte) { // Encoding top polynomials -func topEncode(T []int8) (s []byte) { - s = make([]byte, topBytes) +func topEncode(s []byte, T []int8) { for i := 0; i < topBytes; i++ { s[i] = byte(T[2*i] + (T[2*i+1] << 4)) } - return s } func topDecode(s []byte) (T []int8) { @@ -533,19 +510,9 @@ func topDecode(s []byte) (T []int8) { // Streamlined NTRU Prime Core plus encoding func inputsRandom(seed []byte) (r Inputs) { - - s := make([]byte, inputsBytes) - - if seed != nil { - copy(s, seed) - } else { - cryptoRand.Read(s) - } - for i := 0; i < I; i++ { - r[i] = int8(1 & (s[i>>3] >> (i & 7))) + r[i] = int8(1 & (seed[i>>3] >> (i & 7))) } - return r } @@ -566,13 +533,15 @@ func zKeyGen(pk []byte, sk []byte, seed []byte) { // c = zEncrypt(r,pk) func zEncrypt(c []byte, r Inputs, pk []byte) { A := make([]Fq, p) + B := make([]Fq, p) + T := make([]int8, I) roundedDecode(A, pk[seedBytes:]) - B, T := xEncrypt(r[:], pk[:seedBytes], A) + xEncrypt(B, T, r[:], pk[:seedBytes], A) roundedEncode(c, B) c = c[roundedBytes:] - copy(c, topEncode(T)) + topEncode(c, T) } // r = zDecrypt(C,sk) @@ -632,12 +601,8 @@ func kemKeyGen(pk []byte, sk []byte, seed []byte) { copy(sk, pk) sk = sk[publicKeysBytes:] - if seed != nil { - copy(sk[:inputsBytes], seed) + copy(sk[:inputsBytes], seed) - } else { - cryptoRand.Read(sk[:inputsBytes]) - } sk = sk[inputsBytes:] hashPrefix(sk, 4, pk, publicKeysBytes) @@ -658,7 +623,12 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { // c,k = encap(pk) func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { - if seed != nil && len(seed) != EncapsulationSeedSize { + if seed == nil { + seed = make([]byte, EncapsulationSeedSize) + cryptoRand.Read(seed) + } + + if len(seed) != EncapsulationSeedSize { panic("seed must be of length EncapsulationSeedSize") } if len(c) != CiphertextSize { diff --git a/kem/ntruprime/ntrulpr761/ntruprime.go b/kem/ntruprime/ntrulpr761/ntruprime.go index d1c0968d5..514c1a3f3 100644 --- a/kem/ntruprime/ntrulpr761/ntruprime.go +++ b/kem/ntruprime/ntrulpr761/ntruprime.go @@ -108,8 +108,7 @@ func right(T int8) Fq { // Polynomials mod q // h = f*g in the ring Rq */ -func rqMultSmall(f []Fq, g []small) (h []Fq) { - h = make([]Fq, p) +func rqMultSmall(h []Fq, f []Fq, g []small) { fg := make([]Fq, p+p-1) var result Fq @@ -138,17 +137,14 @@ func rqMultSmall(f []Fq, g []small) (h []Fq) { for i := 0; i < p; i++ { h[i] = fg[i] } - return h } // Rounding all coefficients of a polynomial to the nearest multiple of 3 // Rounded polynomials mod q -func round(a []Fq) []Fq { - out := make([]Fq, p) +func round(out []Fq, a []Fq) { for i := 0; i < p; i++ { out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) } - return out } // Returns (min(x, y), max(x, y)), executes in constant time @@ -193,8 +189,7 @@ func cryptoSortUint32(x []uint32, n int) { } // Sorting to generate short polynomial -func shortFromList(in []int32) []small { - out := make([]small, p) +func shortFromList(out []small, in []int32) { L := make([]uint32, p) var neg2, neg3 int = -2, -3 @@ -212,7 +207,6 @@ func shortFromList(in []int32) []small { for i := 0; i < p; i++ { out[i] = small((L[i] & 3) - 1) } - return out } // Underlying hash function @@ -223,14 +217,14 @@ func shortFromList(in []int32) []small { // e.g., b = 0 means out = Hash0(in) func hashPrefix(out []byte, b int, in []byte, inlen int) { x := make([]byte, inlen+1) - h := make([]byte, 64) + // h := make([]byte, 64) x[0] = byte(b) copy(x[1:], in) hash := sha512.New() hash.Write([]byte(x)) - h = hash.Sum(nil) + h := hash.Sum(nil) copy(out, h[:32]) @@ -240,26 +234,17 @@ func hashPrefix(out []byte, b int, in []byte, inlen int) { // Returns a random unsigned integer // generator can be passed for deterministic number generation func urandom32(seed []byte) uint32 { - - c := make([]byte, 4) var out [4]uint32 - if seed != nil { - copy(c, seed) - } else { - cryptoRand.Read(c) - - } - - out[0] = uint32(c[0]) - out[1] = uint32(c[1]) << 8 - out[2] = uint32(c[2]) << 16 - out[3] = uint32(c[3]) << 24 + out[0] = uint32(seed[0]) + out[1] = uint32(seed[1]) << 8 + out[2] = uint32(seed[2]) << 16 + out[3] = uint32(seed[3]) << 24 return out[0] + out[1] + out[2] + out[3] } // Generates a random short polynomial -func shortRandom(seed []byte) []small { +func shortRandom(out []small, seed []byte) { L := make([]uint32, p) @@ -278,42 +263,40 @@ func shortRandom(seed []byte) []small { for i := 0; i < len(L); i++ { L_int32[i] = int32(L[i]) } - out := shortFromList(L_int32) - - return out - + shortFromList(out, L_int32) } // NTRU LPRime Core // (G,A),a = keyGen(G); leaves G unchanged func keyGen(A []Fq, a []small, G []Fq, seed []byte) { - - copy(a, shortRandom(seed)) - aG := rqMultSmall(G, a) - copy(A, round(aG)) + aG := make([]Fq, p) + shortRandom(a, seed) + rqMultSmall(aG, G, a) + round(A, aG) } // B,T = encrypt(r,(G,A),b) -func encrypt(r []int8, G []Fq, A []Fq, b []small) ([]Fq, []int8) { - T := make([]int8, I) - bG := rqMultSmall(G, b) - B := round(bG) - bA := rqMultSmall(A, b) +func encrypt(B []Fq, T []int8, r []int8, G []Fq, A []Fq, b []small) { + bG := make([]Fq, p) + bA := make([]Fq, p) + + rqMultSmall(bG, G, b) + round(B, bG) + rqMultSmall(bA, A, b) for i := 0; i < I; i++ { T[i] = top(fqFreeze(int32(bA[i]) + int32(r[i])*q12)) } - - return B, T } // r = decrypt((B,T),a) func decrypt(B []Fq, T []int8, a []small) []int8 { + aB := make([]Fq, p) r := make([]int8, I) - aB := rqMultSmall(B, a) + rqMultSmall(aB, B, a) for i := 0; i < I; i++ { r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) @@ -336,18 +319,17 @@ func inputsEncode(s []byte, r Inputs) { // Expand func expand(L []uint32, k []byte) { - temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) - aes_nonce := make([]byte, aes.BlockSize) // IV + temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) + ciphertext := make([]byte, aes.BlockSize+len(temp)) block, err := aes.NewCipher(k[:32]) if err != nil { panic(err) } - ciphertext := make([]byte, len(temp)) // same length as plaintext - - stream := cipher.NewCTR(block, aes_nonce) - stream.XORKeyStream(ciphertext, temp) + stream := cipher.NewCTR(block, ciphertext[:aes.BlockSize]) + stream.XORKeyStream(ciphertext[aes.BlockSize:], temp) + ciphertext = ciphertext[aes.BlockSize:] // convert byte to uint32 for i := 0; i < len(temp); i++ { @@ -365,18 +347,16 @@ func expand(L []uint32, k []byte) { // generator, hashShort // G = generator(k) -func generator(k []byte) (G []Fq) { - G = make([]Fq, p) +func generator(G []Fq, k []byte) { L := make([]uint32, 4*p) expand(L, k) for i := 0; i < p; i++ { G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) } - return G } // out = hashShort(r) -func hashShort(r Inputs) (out []small) { +func hashShort(out []small, r Inputs) { s := make([]byte, inputsBytes) inputsEncode(s, r) h := make([]byte, hashBytes) @@ -391,8 +371,7 @@ func hashShort(r Inputs) (out []small) { for i := 0; i < p; i++ { L_int32[i] = int32(L[i]) } - out = shortFromList(L_int32) - return out + shortFromList(out, L_int32) } // NTRU LPRime expand @@ -400,21 +379,21 @@ func hashShort(r Inputs) (out []small) { // (S,A),a = xKeyGen() func xKeyGen(S []byte, A []Fq, a []small, seed []byte) { - if seed == nil { - cryptoRand.Read(S) - } else { - copy(S, seed[:seedBytes]) - seed = seed[seedBytes:] - } + copy(S, seed[:seedBytes]) + seed = seed[seedBytes:] + G := make([]Fq, p) - G := generator(S) + generator(G, S) keyGen(A, a, G, seed) } // B,T = xEncrypt(r,(S,A)) -func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { - G := generator(S) +func xEncrypt(B []Fq, T []int8, r []int8, S []byte, A []Fq) { + G := make([]Fq, p) + + generator(G, S) + b := make([]small, p) // convert []int8 to Inputs var r_inputs Inputs @@ -422,9 +401,9 @@ func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { r_inputs[i] = r[i] } - b := hashShort(r_inputs) + hashShort(b, r_inputs) - return encrypt(r, G, A, b) + encrypt(B, T, r, G, A, b) } // Encoding small polynomials (including short polynomials) @@ -510,13 +489,11 @@ func roundedDecode(r []Fq, s []byte) { // Encoding top polynomials -func topEncode(T []int8) (s []byte) { - s = make([]byte, topBytes) +func topEncode(s []byte, T []int8) { for i := 0; i < topBytes; i++ { s[i] = byte(T[2*i] + (T[2*i+1] << 4)) } - return s } func topDecode(s []byte) (T []int8) { @@ -533,19 +510,9 @@ func topDecode(s []byte) (T []int8) { // Streamlined NTRU Prime Core plus encoding func inputsRandom(seed []byte) (r Inputs) { - - s := make([]byte, inputsBytes) - - if seed != nil { - copy(s, seed) - } else { - cryptoRand.Read(s) - } - for i := 0; i < I; i++ { - r[i] = int8(1 & (s[i>>3] >> (i & 7))) + r[i] = int8(1 & (seed[i>>3] >> (i & 7))) } - return r } @@ -566,13 +533,15 @@ func zKeyGen(pk []byte, sk []byte, seed []byte) { // c = zEncrypt(r,pk) func zEncrypt(c []byte, r Inputs, pk []byte) { A := make([]Fq, p) + B := make([]Fq, p) + T := make([]int8, I) roundedDecode(A, pk[seedBytes:]) - B, T := xEncrypt(r[:], pk[:seedBytes], A) + xEncrypt(B, T, r[:], pk[:seedBytes], A) roundedEncode(c, B) c = c[roundedBytes:] - copy(c, topEncode(T)) + topEncode(c, T) } // r = zDecrypt(C,sk) @@ -632,12 +601,8 @@ func kemKeyGen(pk []byte, sk []byte, seed []byte) { copy(sk, pk) sk = sk[publicKeysBytes:] - if seed != nil { - copy(sk[:inputsBytes], seed) + copy(sk[:inputsBytes], seed) - } else { - cryptoRand.Read(sk[:inputsBytes]) - } sk = sk[inputsBytes:] hashPrefix(sk, 4, pk, publicKeysBytes) @@ -658,7 +623,12 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { // c,k = encap(pk) func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { - if seed != nil && len(seed) != EncapsulationSeedSize { + if seed == nil { + seed = make([]byte, EncapsulationSeedSize) + cryptoRand.Read(seed) + } + + if len(seed) != EncapsulationSeedSize { panic("seed must be of length EncapsulationSeedSize") } if len(c) != CiphertextSize { diff --git a/kem/ntruprime/ntrulpr857/ntruprime.go b/kem/ntruprime/ntrulpr857/ntruprime.go index 9bc18cfb9..68d47c3c5 100644 --- a/kem/ntruprime/ntrulpr857/ntruprime.go +++ b/kem/ntruprime/ntrulpr857/ntruprime.go @@ -108,8 +108,7 @@ func right(T int8) Fq { // Polynomials mod q // h = f*g in the ring Rq */ -func rqMultSmall(f []Fq, g []small) (h []Fq) { - h = make([]Fq, p) +func rqMultSmall(h []Fq, f []Fq, g []small) { fg := make([]Fq, p+p-1) var result Fq @@ -138,17 +137,14 @@ func rqMultSmall(f []Fq, g []small) (h []Fq) { for i := 0; i < p; i++ { h[i] = fg[i] } - return h } // Rounding all coefficients of a polynomial to the nearest multiple of 3 // Rounded polynomials mod q -func round(a []Fq) []Fq { - out := make([]Fq, p) +func round(out []Fq, a []Fq) { for i := 0; i < p; i++ { out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) } - return out } // Returns (min(x, y), max(x, y)), executes in constant time @@ -193,8 +189,7 @@ func cryptoSortUint32(x []uint32, n int) { } // Sorting to generate short polynomial -func shortFromList(in []int32) []small { - out := make([]small, p) +func shortFromList(out []small, in []int32) { L := make([]uint32, p) var neg2, neg3 int = -2, -3 @@ -212,7 +207,6 @@ func shortFromList(in []int32) []small { for i := 0; i < p; i++ { out[i] = small((L[i] & 3) - 1) } - return out } // Underlying hash function @@ -223,14 +217,14 @@ func shortFromList(in []int32) []small { // e.g., b = 0 means out = Hash0(in) func hashPrefix(out []byte, b int, in []byte, inlen int) { x := make([]byte, inlen+1) - h := make([]byte, 64) + // h := make([]byte, 64) x[0] = byte(b) copy(x[1:], in) hash := sha512.New() hash.Write([]byte(x)) - h = hash.Sum(nil) + h := hash.Sum(nil) copy(out, h[:32]) @@ -240,26 +234,17 @@ func hashPrefix(out []byte, b int, in []byte, inlen int) { // Returns a random unsigned integer // generator can be passed for deterministic number generation func urandom32(seed []byte) uint32 { - - c := make([]byte, 4) var out [4]uint32 - if seed != nil { - copy(c, seed) - } else { - cryptoRand.Read(c) - - } - - out[0] = uint32(c[0]) - out[1] = uint32(c[1]) << 8 - out[2] = uint32(c[2]) << 16 - out[3] = uint32(c[3]) << 24 + out[0] = uint32(seed[0]) + out[1] = uint32(seed[1]) << 8 + out[2] = uint32(seed[2]) << 16 + out[3] = uint32(seed[3]) << 24 return out[0] + out[1] + out[2] + out[3] } // Generates a random short polynomial -func shortRandom(seed []byte) []small { +func shortRandom(out []small, seed []byte) { L := make([]uint32, p) @@ -278,42 +263,40 @@ func shortRandom(seed []byte) []small { for i := 0; i < len(L); i++ { L_int32[i] = int32(L[i]) } - out := shortFromList(L_int32) - - return out - + shortFromList(out, L_int32) } // NTRU LPRime Core // (G,A),a = keyGen(G); leaves G unchanged func keyGen(A []Fq, a []small, G []Fq, seed []byte) { - - copy(a, shortRandom(seed)) - aG := rqMultSmall(G, a) - copy(A, round(aG)) + aG := make([]Fq, p) + shortRandom(a, seed) + rqMultSmall(aG, G, a) + round(A, aG) } // B,T = encrypt(r,(G,A),b) -func encrypt(r []int8, G []Fq, A []Fq, b []small) ([]Fq, []int8) { - T := make([]int8, I) - bG := rqMultSmall(G, b) - B := round(bG) - bA := rqMultSmall(A, b) +func encrypt(B []Fq, T []int8, r []int8, G []Fq, A []Fq, b []small) { + bG := make([]Fq, p) + bA := make([]Fq, p) + + rqMultSmall(bG, G, b) + round(B, bG) + rqMultSmall(bA, A, b) for i := 0; i < I; i++ { T[i] = top(fqFreeze(int32(bA[i]) + int32(r[i])*q12)) } - - return B, T } // r = decrypt((B,T),a) func decrypt(B []Fq, T []int8, a []small) []int8 { + aB := make([]Fq, p) r := make([]int8, I) - aB := rqMultSmall(B, a) + rqMultSmall(aB, B, a) for i := 0; i < I; i++ { r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) @@ -336,18 +319,17 @@ func inputsEncode(s []byte, r Inputs) { // Expand func expand(L []uint32, k []byte) { - temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) - aes_nonce := make([]byte, aes.BlockSize) // IV + temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) + ciphertext := make([]byte, aes.BlockSize+len(temp)) block, err := aes.NewCipher(k[:32]) if err != nil { panic(err) } - ciphertext := make([]byte, len(temp)) // same length as plaintext - - stream := cipher.NewCTR(block, aes_nonce) - stream.XORKeyStream(ciphertext, temp) + stream := cipher.NewCTR(block, ciphertext[:aes.BlockSize]) + stream.XORKeyStream(ciphertext[aes.BlockSize:], temp) + ciphertext = ciphertext[aes.BlockSize:] // convert byte to uint32 for i := 0; i < len(temp); i++ { @@ -365,18 +347,16 @@ func expand(L []uint32, k []byte) { // generator, hashShort // G = generator(k) -func generator(k []byte) (G []Fq) { - G = make([]Fq, p) +func generator(G []Fq, k []byte) { L := make([]uint32, 4*p) expand(L, k) for i := 0; i < p; i++ { G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) } - return G } // out = hashShort(r) -func hashShort(r Inputs) (out []small) { +func hashShort(out []small, r Inputs) { s := make([]byte, inputsBytes) inputsEncode(s, r) h := make([]byte, hashBytes) @@ -391,8 +371,7 @@ func hashShort(r Inputs) (out []small) { for i := 0; i < p; i++ { L_int32[i] = int32(L[i]) } - out = shortFromList(L_int32) - return out + shortFromList(out, L_int32) } // NTRU LPRime expand @@ -400,21 +379,21 @@ func hashShort(r Inputs) (out []small) { // (S,A),a = xKeyGen() func xKeyGen(S []byte, A []Fq, a []small, seed []byte) { - if seed == nil { - cryptoRand.Read(S) - } else { - copy(S, seed[:seedBytes]) - seed = seed[seedBytes:] - } + copy(S, seed[:seedBytes]) + seed = seed[seedBytes:] + G := make([]Fq, p) - G := generator(S) + generator(G, S) keyGen(A, a, G, seed) } // B,T = xEncrypt(r,(S,A)) -func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { - G := generator(S) +func xEncrypt(B []Fq, T []int8, r []int8, S []byte, A []Fq) { + G := make([]Fq, p) + + generator(G, S) + b := make([]small, p) // convert []int8 to Inputs var r_inputs Inputs @@ -422,9 +401,9 @@ func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { r_inputs[i] = r[i] } - b := hashShort(r_inputs) + hashShort(b, r_inputs) - return encrypt(r, G, A, b) + encrypt(B, T, r, G, A, b) } // Encoding small polynomials (including short polynomials) @@ -510,13 +489,11 @@ func roundedDecode(r []Fq, s []byte) { // Encoding top polynomials -func topEncode(T []int8) (s []byte) { - s = make([]byte, topBytes) +func topEncode(s []byte, T []int8) { for i := 0; i < topBytes; i++ { s[i] = byte(T[2*i] + (T[2*i+1] << 4)) } - return s } func topDecode(s []byte) (T []int8) { @@ -533,19 +510,9 @@ func topDecode(s []byte) (T []int8) { // Streamlined NTRU Prime Core plus encoding func inputsRandom(seed []byte) (r Inputs) { - - s := make([]byte, inputsBytes) - - if seed != nil { - copy(s, seed) - } else { - cryptoRand.Read(s) - } - for i := 0; i < I; i++ { - r[i] = int8(1 & (s[i>>3] >> (i & 7))) + r[i] = int8(1 & (seed[i>>3] >> (i & 7))) } - return r } @@ -566,13 +533,15 @@ func zKeyGen(pk []byte, sk []byte, seed []byte) { // c = zEncrypt(r,pk) func zEncrypt(c []byte, r Inputs, pk []byte) { A := make([]Fq, p) + B := make([]Fq, p) + T := make([]int8, I) roundedDecode(A, pk[seedBytes:]) - B, T := xEncrypt(r[:], pk[:seedBytes], A) + xEncrypt(B, T, r[:], pk[:seedBytes], A) roundedEncode(c, B) c = c[roundedBytes:] - copy(c, topEncode(T)) + topEncode(c, T) } // r = zDecrypt(C,sk) @@ -632,12 +601,8 @@ func kemKeyGen(pk []byte, sk []byte, seed []byte) { copy(sk, pk) sk = sk[publicKeysBytes:] - if seed != nil { - copy(sk[:inputsBytes], seed) + copy(sk[:inputsBytes], seed) - } else { - cryptoRand.Read(sk[:inputsBytes]) - } sk = sk[inputsBytes:] hashPrefix(sk, 4, pk, publicKeysBytes) @@ -658,7 +623,12 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { // c,k = encap(pk) func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { - if seed != nil && len(seed) != EncapsulationSeedSize { + if seed == nil { + seed = make([]byte, EncapsulationSeedSize) + cryptoRand.Read(seed) + } + + if len(seed) != EncapsulationSeedSize { panic("seed must be of length EncapsulationSeedSize") } if len(c) != CiphertextSize { diff --git a/kem/ntruprime/ntrulpr953/ntruprime.go b/kem/ntruprime/ntrulpr953/ntruprime.go index 9a047f29c..b11bdcb76 100644 --- a/kem/ntruprime/ntrulpr953/ntruprime.go +++ b/kem/ntruprime/ntrulpr953/ntruprime.go @@ -108,8 +108,7 @@ func right(T int8) Fq { // Polynomials mod q // h = f*g in the ring Rq */ -func rqMultSmall(f []Fq, g []small) (h []Fq) { - h = make([]Fq, p) +func rqMultSmall(h []Fq, f []Fq, g []small) { fg := make([]Fq, p+p-1) var result Fq @@ -138,17 +137,14 @@ func rqMultSmall(f []Fq, g []small) (h []Fq) { for i := 0; i < p; i++ { h[i] = fg[i] } - return h } // Rounding all coefficients of a polynomial to the nearest multiple of 3 // Rounded polynomials mod q -func round(a []Fq) []Fq { - out := make([]Fq, p) +func round(out []Fq, a []Fq) { for i := 0; i < p; i++ { out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) } - return out } // Returns (min(x, y), max(x, y)), executes in constant time @@ -193,8 +189,7 @@ func cryptoSortUint32(x []uint32, n int) { } // Sorting to generate short polynomial -func shortFromList(in []int32) []small { - out := make([]small, p) +func shortFromList(out []small, in []int32) { L := make([]uint32, p) var neg2, neg3 int = -2, -3 @@ -212,7 +207,6 @@ func shortFromList(in []int32) []small { for i := 0; i < p; i++ { out[i] = small((L[i] & 3) - 1) } - return out } // Underlying hash function @@ -223,14 +217,14 @@ func shortFromList(in []int32) []small { // e.g., b = 0 means out = Hash0(in) func hashPrefix(out []byte, b int, in []byte, inlen int) { x := make([]byte, inlen+1) - h := make([]byte, 64) + // h := make([]byte, 64) x[0] = byte(b) copy(x[1:], in) hash := sha512.New() hash.Write([]byte(x)) - h = hash.Sum(nil) + h := hash.Sum(nil) copy(out, h[:32]) @@ -240,26 +234,17 @@ func hashPrefix(out []byte, b int, in []byte, inlen int) { // Returns a random unsigned integer // generator can be passed for deterministic number generation func urandom32(seed []byte) uint32 { - - c := make([]byte, 4) var out [4]uint32 - if seed != nil { - copy(c, seed) - } else { - cryptoRand.Read(c) - - } - - out[0] = uint32(c[0]) - out[1] = uint32(c[1]) << 8 - out[2] = uint32(c[2]) << 16 - out[3] = uint32(c[3]) << 24 + out[0] = uint32(seed[0]) + out[1] = uint32(seed[1]) << 8 + out[2] = uint32(seed[2]) << 16 + out[3] = uint32(seed[3]) << 24 return out[0] + out[1] + out[2] + out[3] } // Generates a random short polynomial -func shortRandom(seed []byte) []small { +func shortRandom(out []small, seed []byte) { L := make([]uint32, p) @@ -278,42 +263,40 @@ func shortRandom(seed []byte) []small { for i := 0; i < len(L); i++ { L_int32[i] = int32(L[i]) } - out := shortFromList(L_int32) - - return out - + shortFromList(out, L_int32) } // NTRU LPRime Core // (G,A),a = keyGen(G); leaves G unchanged func keyGen(A []Fq, a []small, G []Fq, seed []byte) { - - copy(a, shortRandom(seed)) - aG := rqMultSmall(G, a) - copy(A, round(aG)) + aG := make([]Fq, p) + shortRandom(a, seed) + rqMultSmall(aG, G, a) + round(A, aG) } // B,T = encrypt(r,(G,A),b) -func encrypt(r []int8, G []Fq, A []Fq, b []small) ([]Fq, []int8) { - T := make([]int8, I) - bG := rqMultSmall(G, b) - B := round(bG) - bA := rqMultSmall(A, b) +func encrypt(B []Fq, T []int8, r []int8, G []Fq, A []Fq, b []small) { + bG := make([]Fq, p) + bA := make([]Fq, p) + + rqMultSmall(bG, G, b) + round(B, bG) + rqMultSmall(bA, A, b) for i := 0; i < I; i++ { T[i] = top(fqFreeze(int32(bA[i]) + int32(r[i])*q12)) } - - return B, T } // r = decrypt((B,T),a) func decrypt(B []Fq, T []int8, a []small) []int8 { + aB := make([]Fq, p) r := make([]int8, I) - aB := rqMultSmall(B, a) + rqMultSmall(aB, B, a) for i := 0; i < I; i++ { r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) @@ -336,18 +319,17 @@ func inputsEncode(s []byte, r Inputs) { // Expand func expand(L []uint32, k []byte) { - temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) - aes_nonce := make([]byte, aes.BlockSize) // IV + temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) + ciphertext := make([]byte, aes.BlockSize+len(temp)) block, err := aes.NewCipher(k[:32]) if err != nil { panic(err) } - ciphertext := make([]byte, len(temp)) // same length as plaintext - - stream := cipher.NewCTR(block, aes_nonce) - stream.XORKeyStream(ciphertext, temp) + stream := cipher.NewCTR(block, ciphertext[:aes.BlockSize]) + stream.XORKeyStream(ciphertext[aes.BlockSize:], temp) + ciphertext = ciphertext[aes.BlockSize:] // convert byte to uint32 for i := 0; i < len(temp); i++ { @@ -365,18 +347,16 @@ func expand(L []uint32, k []byte) { // generator, hashShort // G = generator(k) -func generator(k []byte) (G []Fq) { - G = make([]Fq, p) +func generator(G []Fq, k []byte) { L := make([]uint32, 4*p) expand(L, k) for i := 0; i < p; i++ { G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) } - return G } // out = hashShort(r) -func hashShort(r Inputs) (out []small) { +func hashShort(out []small, r Inputs) { s := make([]byte, inputsBytes) inputsEncode(s, r) h := make([]byte, hashBytes) @@ -391,8 +371,7 @@ func hashShort(r Inputs) (out []small) { for i := 0; i < p; i++ { L_int32[i] = int32(L[i]) } - out = shortFromList(L_int32) - return out + shortFromList(out, L_int32) } // NTRU LPRime expand @@ -400,21 +379,21 @@ func hashShort(r Inputs) (out []small) { // (S,A),a = xKeyGen() func xKeyGen(S []byte, A []Fq, a []small, seed []byte) { - if seed == nil { - cryptoRand.Read(S) - } else { - copy(S, seed[:seedBytes]) - seed = seed[seedBytes:] - } + copy(S, seed[:seedBytes]) + seed = seed[seedBytes:] + G := make([]Fq, p) - G := generator(S) + generator(G, S) keyGen(A, a, G, seed) } // B,T = xEncrypt(r,(S,A)) -func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { - G := generator(S) +func xEncrypt(B []Fq, T []int8, r []int8, S []byte, A []Fq) { + G := make([]Fq, p) + + generator(G, S) + b := make([]small, p) // convert []int8 to Inputs var r_inputs Inputs @@ -422,9 +401,9 @@ func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { r_inputs[i] = r[i] } - b := hashShort(r_inputs) + hashShort(b, r_inputs) - return encrypt(r, G, A, b) + encrypt(B, T, r, G, A, b) } // Encoding small polynomials (including short polynomials) @@ -510,13 +489,11 @@ func roundedDecode(r []Fq, s []byte) { // Encoding top polynomials -func topEncode(T []int8) (s []byte) { - s = make([]byte, topBytes) +func topEncode(s []byte, T []int8) { for i := 0; i < topBytes; i++ { s[i] = byte(T[2*i] + (T[2*i+1] << 4)) } - return s } func topDecode(s []byte) (T []int8) { @@ -533,19 +510,9 @@ func topDecode(s []byte) (T []int8) { // Streamlined NTRU Prime Core plus encoding func inputsRandom(seed []byte) (r Inputs) { - - s := make([]byte, inputsBytes) - - if seed != nil { - copy(s, seed) - } else { - cryptoRand.Read(s) - } - for i := 0; i < I; i++ { - r[i] = int8(1 & (s[i>>3] >> (i & 7))) + r[i] = int8(1 & (seed[i>>3] >> (i & 7))) } - return r } @@ -566,13 +533,15 @@ func zKeyGen(pk []byte, sk []byte, seed []byte) { // c = zEncrypt(r,pk) func zEncrypt(c []byte, r Inputs, pk []byte) { A := make([]Fq, p) + B := make([]Fq, p) + T := make([]int8, I) roundedDecode(A, pk[seedBytes:]) - B, T := xEncrypt(r[:], pk[:seedBytes], A) + xEncrypt(B, T, r[:], pk[:seedBytes], A) roundedEncode(c, B) c = c[roundedBytes:] - copy(c, topEncode(T)) + topEncode(c, T) } // r = zDecrypt(C,sk) @@ -632,12 +601,8 @@ func kemKeyGen(pk []byte, sk []byte, seed []byte) { copy(sk, pk) sk = sk[publicKeysBytes:] - if seed != nil { - copy(sk[:inputsBytes], seed) + copy(sk[:inputsBytes], seed) - } else { - cryptoRand.Read(sk[:inputsBytes]) - } sk = sk[inputsBytes:] hashPrefix(sk, 4, pk, publicKeysBytes) @@ -658,7 +623,12 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { // c,k = encap(pk) func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { - if seed != nil && len(seed) != EncapsulationSeedSize { + if seed == nil { + seed = make([]byte, EncapsulationSeedSize) + cryptoRand.Read(seed) + } + + if len(seed) != EncapsulationSeedSize { panic("seed must be of length EncapsulationSeedSize") } if len(c) != CiphertextSize { diff --git a/kem/ntruprime/sntrup1013/ntruprime.go b/kem/ntruprime/sntrup1013/ntruprime.go index 3edc4d670..df0be85e4 100644 --- a/kem/ntruprime/sntrup1013/ntruprime.go +++ b/kem/ntruprime/sntrup1013/ntruprime.go @@ -45,6 +45,9 @@ const ( ) const ( + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = 4 * p + // Size of the established shared key. SharedKeySize = ntrup.SharedKeySize @@ -153,8 +156,8 @@ func r3Mult(f []small, g []small) (h []small) { // Calculates the reciprocal of R3 polynomials // Returns 0 if recip succeeded; else -1 -func r3Recip(in []small) ([]small, int) { - out := make([]small, p) +func r3Recip(out []small, in []small) int { + // out := make([]small, p) f := make([]small, p+1) g := make([]small, p+1) v := make([]small, p+1) @@ -217,15 +220,14 @@ func r3Recip(in []small) ([]small, int) { out[i] = small(sign * int(v[p-1-i])) } - return out, internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16_nonzero_mask(int16(delta)) } // Polynomials mod q // h = f*g in the ring Rq */ -func rqMultSmall(f []Fq, g []small) (h []Fq) { - h = make([]Fq, p) +func rqMultSmall(h []Fq, f []Fq, g []small) { fg := make([]Fq, p+p-1) var result Fq @@ -254,7 +256,6 @@ func rqMultSmall(f []Fq, g []small) (h []Fq) { for i := 0; i < p; i++ { h[i] = fg[i] } - return h } // h = 3f in Rq @@ -386,8 +387,7 @@ func cryptoSortUint32(x []uint32, n int) { } // Sorting to generate short polynomial -func shortFromList(in []int32) []small { - out := make([]small, p) +func shortFromList(out []small, in []int32) { L := make([]uint32, p) var neg2, neg3 int = -2, -3 @@ -405,7 +405,6 @@ func shortFromList(in []int32) []small { for i := 0; i < p; i++ { out[i] = small((L[i] & 3) - 1) } - return out } // Underlying hash function @@ -431,33 +430,23 @@ func hashPrefix(out []byte, b int, in []byte, inlen int) { // Higher level randomness // Returns a random unsigned integer -// A generator can be passed for deterministic number generation -func urandom32(gen *nist.DRBG) uint32 { - - c := make([]byte, 4) +func urandom32(seed []byte) uint32 { var out [4]uint32 - if gen != nil { - gen.Fill(c) - } else { - cryptoRand.Read(c) - - } - - out[0] = uint32(c[0]) - out[1] = uint32(c[1]) << 8 - out[2] = uint32(c[2]) << 16 - out[3] = uint32(c[3]) << 24 + out[0] = uint32(seed[0]) + out[1] = uint32(seed[1]) << 8 + out[2] = uint32(seed[2]) << 16 + out[3] = uint32(seed[3]) << 24 return out[0] + out[1] + out[2] + out[3] } // Generates a random short polynomial -func shortRandom(gen *nist.DRBG) []small { +func shortRandom(out []small, seed []byte) { L := make([]uint32, p) for i := 0; i < p; i++ { - L[i] = urandom32(gen) + L[i] = urandom32(seed[4*i : 4*i+4]) } // Converts uint32 array to int32 array @@ -465,52 +454,58 @@ func shortRandom(gen *nist.DRBG) []small { for i := 0; i < len(L); i++ { L_int32[i] = int32(L[i]) } - out := shortFromList(L_int32) - - return out - + shortFromList(out, L_int32) } // Generates a random list of small -func smallRandom(gen *nist.DRBG) []small { - - out := make([]small, p) +func smallRandom(out []small, seed []byte) { for i := 0; i < p; i++ { - out[i] = small(((urandom32(gen)&0x3fffffff)*3)>>30) - 1 + out[i] = small(((urandom32(seed[4*i:4*i+4])&0x3fffffff)*3)>>30) - 1 } - return out } // Streamlined NTRU Prime Core // h,(f,ginv) = keyGen() -func keyGen(gen *nist.DRBG) (h []Fq, f []small, ginv []small) { +func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { g := make([]small, p) - var err int - for { - g = smallRandom(gen) - ginv, err = r3Recip(g) - if err == 0 { - break - } + seed := make([]byte, 4*p+4*p) + if gen == nil { + for { + cryptoRand.Read(seed[:4*p]) + smallRandom(g, seed[:4*p]) + if r3Recip(ginv, g) == 0 { + break + } + } + cryptoRand.Read(seed[4*p:]) + } else { + for { + for i := 0; i < p; i++ { + gen.Fill(seed[4*i : 4*i+4]) + } + smallRandom(g, seed[:4*p]) + if r3Recip(ginv, g) == 0 { + break + } + } + for i := 0; i < p; i++ { + gen.Fill(seed[4*p+4*i : 4*p+4*i+4]) + } } - - f = shortRandom(gen) + shortRandom(f, seed[4*p:]) finv, _ := rqRecip3(f) /* always works */ - h = rqMultSmall(finv, g) - return h, f, ginv + rqMultSmall(h, finv, g) } // c = encrypt(r,h) func encrypt(r []small, h []Fq) []Fq { - - hr := rqMultSmall(h, r) + hr := make([]Fq, p) + rqMultSmall(hr, h, r) c := round(hr) - return c - } // r = decrypt(c,(f,ginv)) @@ -518,7 +513,7 @@ func decrypt(c []Fq, f []small, ginv []small) []small { r := make([]small, p) cf := make([]Fq, p) - cf = rqMultSmall(c, f) + rqMultSmall(cf, c, f) cf3 := rqMult3(cf) e := r3FromRq(cf3) ev := r3Mult(e, ginv) @@ -651,7 +646,10 @@ func roundedDecode(r []Fq, s []byte) { // pk,sk = zKeyGen() func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { - h, f, v := keyGen(gen) + h := make([]Fq, p) + f := make([]small, p) + v := make([]small, p) + keyGen(h, f, v, gen) rqEncode(pk, h) smallEncode(sk, f) @@ -743,7 +741,14 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { // Takes as input a public key // Returns ciphertext and shared key // c,k = encap(pk) -func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { + if seed == nil { + seed = make([]byte, 4*p) + cryptoRand.Read(seed) + } + if len(seed) != 4*p { + panic("seed must be of length EncapsulationSeedSize") + } if len(c) != CiphertextSize { panic("ct must be of length CiphertextSize") } @@ -758,7 +763,7 @@ func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { cache := make([]byte, hashBytes) hashPrefix(cache, 4, pk, publicKeysBytes) - copy(r[:], shortRandom(gen)) + shortRandom(r[:], seed) hide(c, r_enc, r, pk, cache) hashSession(k, 1, r_enc, c) @@ -824,11 +829,12 @@ var sch kem.Scheme = &scheme{} // Scheme returns a KEM interface. func Scheme() kem.Scheme { return sch } -func (*scheme) Name() string { return "sntrup1013" } -func (*scheme) PublicKeySize() int { return PublicKeySize } -func (*scheme) PrivateKeySize() int { return PrivateKeySize } -func (*scheme) SharedKeySize() int { return SharedKeySize } -func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) Name() string { return "sntrup1013" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } func (sk *PrivateKey) Scheme() kem.Scheme { return sch } func (pk *PublicKey) Scheme() kem.Scheme { return sch } @@ -879,6 +885,11 @@ func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { } func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { + + if gen == nil { + panic("A nist DRBG must be provided") + } + var pk [PublicKeySize]byte var sk [PrivateKeySize]byte @@ -902,11 +913,7 @@ func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { } -func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DRBG) (ct, ss []byte, err error) { - - if gen == nil { - panic("A nist DRBG must be provided") - } +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) (ct, ss []byte, err error) { ct = make([]byte, CiphertextSize) ss = make([]byte, SharedKeySize) @@ -916,7 +923,7 @@ func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.D return nil, nil, kem.ErrTypeMismatch } - pub.EncapsulateTo(ct, ss, gen) + pub.EncapsulateTo(ct, ss, seed) return ct, ss, nil } diff --git a/kem/ntruprime/sntrup1277/ntruprime.go b/kem/ntruprime/sntrup1277/ntruprime.go index 561beee71..b40549562 100644 --- a/kem/ntruprime/sntrup1277/ntruprime.go +++ b/kem/ntruprime/sntrup1277/ntruprime.go @@ -45,6 +45,9 @@ const ( ) const ( + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = 4 * p + // Size of the established shared key. SharedKeySize = ntrup.SharedKeySize @@ -153,8 +156,8 @@ func r3Mult(f []small, g []small) (h []small) { // Calculates the reciprocal of R3 polynomials // Returns 0 if recip succeeded; else -1 -func r3Recip(in []small) ([]small, int) { - out := make([]small, p) +func r3Recip(out []small, in []small) int { + // out := make([]small, p) f := make([]small, p+1) g := make([]small, p+1) v := make([]small, p+1) @@ -217,15 +220,14 @@ func r3Recip(in []small) ([]small, int) { out[i] = small(sign * int(v[p-1-i])) } - return out, internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16_nonzero_mask(int16(delta)) } // Polynomials mod q // h = f*g in the ring Rq */ -func rqMultSmall(f []Fq, g []small) (h []Fq) { - h = make([]Fq, p) +func rqMultSmall(h []Fq, f []Fq, g []small) { fg := make([]Fq, p+p-1) var result Fq @@ -254,7 +256,6 @@ func rqMultSmall(f []Fq, g []small) (h []Fq) { for i := 0; i < p; i++ { h[i] = fg[i] } - return h } // h = 3f in Rq @@ -386,8 +387,7 @@ func cryptoSortUint32(x []uint32, n int) { } // Sorting to generate short polynomial -func shortFromList(in []int32) []small { - out := make([]small, p) +func shortFromList(out []small, in []int32) { L := make([]uint32, p) var neg2, neg3 int = -2, -3 @@ -405,7 +405,6 @@ func shortFromList(in []int32) []small { for i := 0; i < p; i++ { out[i] = small((L[i] & 3) - 1) } - return out } // Underlying hash function @@ -431,33 +430,23 @@ func hashPrefix(out []byte, b int, in []byte, inlen int) { // Higher level randomness // Returns a random unsigned integer -// A generator can be passed for deterministic number generation -func urandom32(gen *nist.DRBG) uint32 { - - c := make([]byte, 4) +func urandom32(seed []byte) uint32 { var out [4]uint32 - if gen != nil { - gen.Fill(c) - } else { - cryptoRand.Read(c) - - } - - out[0] = uint32(c[0]) - out[1] = uint32(c[1]) << 8 - out[2] = uint32(c[2]) << 16 - out[3] = uint32(c[3]) << 24 + out[0] = uint32(seed[0]) + out[1] = uint32(seed[1]) << 8 + out[2] = uint32(seed[2]) << 16 + out[3] = uint32(seed[3]) << 24 return out[0] + out[1] + out[2] + out[3] } // Generates a random short polynomial -func shortRandom(gen *nist.DRBG) []small { +func shortRandom(out []small, seed []byte) { L := make([]uint32, p) for i := 0; i < p; i++ { - L[i] = urandom32(gen) + L[i] = urandom32(seed[4*i : 4*i+4]) } // Converts uint32 array to int32 array @@ -465,52 +454,58 @@ func shortRandom(gen *nist.DRBG) []small { for i := 0; i < len(L); i++ { L_int32[i] = int32(L[i]) } - out := shortFromList(L_int32) - - return out - + shortFromList(out, L_int32) } // Generates a random list of small -func smallRandom(gen *nist.DRBG) []small { - - out := make([]small, p) +func smallRandom(out []small, seed []byte) { for i := 0; i < p; i++ { - out[i] = small(((urandom32(gen)&0x3fffffff)*3)>>30) - 1 + out[i] = small(((urandom32(seed[4*i:4*i+4])&0x3fffffff)*3)>>30) - 1 } - return out } // Streamlined NTRU Prime Core // h,(f,ginv) = keyGen() -func keyGen(gen *nist.DRBG) (h []Fq, f []small, ginv []small) { +func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { g := make([]small, p) - var err int - for { - g = smallRandom(gen) - ginv, err = r3Recip(g) - if err == 0 { - break - } + seed := make([]byte, 4*p+4*p) + if gen == nil { + for { + cryptoRand.Read(seed[:4*p]) + smallRandom(g, seed[:4*p]) + if r3Recip(ginv, g) == 0 { + break + } + } + cryptoRand.Read(seed[4*p:]) + } else { + for { + for i := 0; i < p; i++ { + gen.Fill(seed[4*i : 4*i+4]) + } + smallRandom(g, seed[:4*p]) + if r3Recip(ginv, g) == 0 { + break + } + } + for i := 0; i < p; i++ { + gen.Fill(seed[4*p+4*i : 4*p+4*i+4]) + } } - - f = shortRandom(gen) + shortRandom(f, seed[4*p:]) finv, _ := rqRecip3(f) /* always works */ - h = rqMultSmall(finv, g) - return h, f, ginv + rqMultSmall(h, finv, g) } // c = encrypt(r,h) func encrypt(r []small, h []Fq) []Fq { - - hr := rqMultSmall(h, r) + hr := make([]Fq, p) + rqMultSmall(hr, h, r) c := round(hr) - return c - } // r = decrypt(c,(f,ginv)) @@ -518,7 +513,7 @@ func decrypt(c []Fq, f []small, ginv []small) []small { r := make([]small, p) cf := make([]Fq, p) - cf = rqMultSmall(c, f) + rqMultSmall(cf, c, f) cf3 := rqMult3(cf) e := r3FromRq(cf3) ev := r3Mult(e, ginv) @@ -651,7 +646,10 @@ func roundedDecode(r []Fq, s []byte) { // pk,sk = zKeyGen() func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { - h, f, v := keyGen(gen) + h := make([]Fq, p) + f := make([]small, p) + v := make([]small, p) + keyGen(h, f, v, gen) rqEncode(pk, h) smallEncode(sk, f) @@ -743,7 +741,14 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { // Takes as input a public key // Returns ciphertext and shared key // c,k = encap(pk) -func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { + if seed == nil { + seed = make([]byte, 4*p) + cryptoRand.Read(seed) + } + if len(seed) != 4*p { + panic("seed must be of length EncapsulationSeedSize") + } if len(c) != CiphertextSize { panic("ct must be of length CiphertextSize") } @@ -758,7 +763,7 @@ func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { cache := make([]byte, hashBytes) hashPrefix(cache, 4, pk, publicKeysBytes) - copy(r[:], shortRandom(gen)) + shortRandom(r[:], seed) hide(c, r_enc, r, pk, cache) hashSession(k, 1, r_enc, c) @@ -824,11 +829,12 @@ var sch kem.Scheme = &scheme{} // Scheme returns a KEM interface. func Scheme() kem.Scheme { return sch } -func (*scheme) Name() string { return "sntrup1277" } -func (*scheme) PublicKeySize() int { return PublicKeySize } -func (*scheme) PrivateKeySize() int { return PrivateKeySize } -func (*scheme) SharedKeySize() int { return SharedKeySize } -func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) Name() string { return "sntrup1277" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } func (sk *PrivateKey) Scheme() kem.Scheme { return sch } func (pk *PublicKey) Scheme() kem.Scheme { return sch } @@ -879,6 +885,11 @@ func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { } func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { + + if gen == nil { + panic("A nist DRBG must be provided") + } + var pk [PublicKeySize]byte var sk [PrivateKeySize]byte @@ -902,11 +913,7 @@ func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { } -func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DRBG) (ct, ss []byte, err error) { - - if gen == nil { - panic("A nist DRBG must be provided") - } +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) (ct, ss []byte, err error) { ct = make([]byte, CiphertextSize) ss = make([]byte, SharedKeySize) @@ -916,7 +923,7 @@ func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.D return nil, nil, kem.ErrTypeMismatch } - pub.EncapsulateTo(ct, ss, gen) + pub.EncapsulateTo(ct, ss, seed) return ct, ss, nil } diff --git a/kem/ntruprime/sntrup653/ntruprime.go b/kem/ntruprime/sntrup653/ntruprime.go index 203a6ea4f..4e9dea5cc 100644 --- a/kem/ntruprime/sntrup653/ntruprime.go +++ b/kem/ntruprime/sntrup653/ntruprime.go @@ -45,6 +45,9 @@ const ( ) const ( + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = 4 * p + // Size of the established shared key. SharedKeySize = ntrup.SharedKeySize @@ -153,8 +156,8 @@ func r3Mult(f []small, g []small) (h []small) { // Calculates the reciprocal of R3 polynomials // Returns 0 if recip succeeded; else -1 -func r3Recip(in []small) ([]small, int) { - out := make([]small, p) +func r3Recip(out []small, in []small) int { + // out := make([]small, p) f := make([]small, p+1) g := make([]small, p+1) v := make([]small, p+1) @@ -217,15 +220,14 @@ func r3Recip(in []small) ([]small, int) { out[i] = small(sign * int(v[p-1-i])) } - return out, internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16_nonzero_mask(int16(delta)) } // Polynomials mod q // h = f*g in the ring Rq */ -func rqMultSmall(f []Fq, g []small) (h []Fq) { - h = make([]Fq, p) +func rqMultSmall(h []Fq, f []Fq, g []small) { fg := make([]Fq, p+p-1) var result Fq @@ -254,7 +256,6 @@ func rqMultSmall(f []Fq, g []small) (h []Fq) { for i := 0; i < p; i++ { h[i] = fg[i] } - return h } // h = 3f in Rq @@ -386,8 +387,7 @@ func cryptoSortUint32(x []uint32, n int) { } // Sorting to generate short polynomial -func shortFromList(in []int32) []small { - out := make([]small, p) +func shortFromList(out []small, in []int32) { L := make([]uint32, p) var neg2, neg3 int = -2, -3 @@ -405,7 +405,6 @@ func shortFromList(in []int32) []small { for i := 0; i < p; i++ { out[i] = small((L[i] & 3) - 1) } - return out } // Underlying hash function @@ -431,33 +430,23 @@ func hashPrefix(out []byte, b int, in []byte, inlen int) { // Higher level randomness // Returns a random unsigned integer -// A generator can be passed for deterministic number generation -func urandom32(gen *nist.DRBG) uint32 { - - c := make([]byte, 4) +func urandom32(seed []byte) uint32 { var out [4]uint32 - if gen != nil { - gen.Fill(c) - } else { - cryptoRand.Read(c) - - } - - out[0] = uint32(c[0]) - out[1] = uint32(c[1]) << 8 - out[2] = uint32(c[2]) << 16 - out[3] = uint32(c[3]) << 24 + out[0] = uint32(seed[0]) + out[1] = uint32(seed[1]) << 8 + out[2] = uint32(seed[2]) << 16 + out[3] = uint32(seed[3]) << 24 return out[0] + out[1] + out[2] + out[3] } // Generates a random short polynomial -func shortRandom(gen *nist.DRBG) []small { +func shortRandom(out []small, seed []byte) { L := make([]uint32, p) for i := 0; i < p; i++ { - L[i] = urandom32(gen) + L[i] = urandom32(seed[4*i : 4*i+4]) } // Converts uint32 array to int32 array @@ -465,52 +454,58 @@ func shortRandom(gen *nist.DRBG) []small { for i := 0; i < len(L); i++ { L_int32[i] = int32(L[i]) } - out := shortFromList(L_int32) - - return out - + shortFromList(out, L_int32) } // Generates a random list of small -func smallRandom(gen *nist.DRBG) []small { - - out := make([]small, p) +func smallRandom(out []small, seed []byte) { for i := 0; i < p; i++ { - out[i] = small(((urandom32(gen)&0x3fffffff)*3)>>30) - 1 + out[i] = small(((urandom32(seed[4*i:4*i+4])&0x3fffffff)*3)>>30) - 1 } - return out } // Streamlined NTRU Prime Core // h,(f,ginv) = keyGen() -func keyGen(gen *nist.DRBG) (h []Fq, f []small, ginv []small) { +func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { g := make([]small, p) - var err int - for { - g = smallRandom(gen) - ginv, err = r3Recip(g) - if err == 0 { - break - } + seed := make([]byte, 4*p+4*p) + if gen == nil { + for { + cryptoRand.Read(seed[:4*p]) + smallRandom(g, seed[:4*p]) + if r3Recip(ginv, g) == 0 { + break + } + } + cryptoRand.Read(seed[4*p:]) + } else { + for { + for i := 0; i < p; i++ { + gen.Fill(seed[4*i : 4*i+4]) + } + smallRandom(g, seed[:4*p]) + if r3Recip(ginv, g) == 0 { + break + } + } + for i := 0; i < p; i++ { + gen.Fill(seed[4*p+4*i : 4*p+4*i+4]) + } } - - f = shortRandom(gen) + shortRandom(f, seed[4*p:]) finv, _ := rqRecip3(f) /* always works */ - h = rqMultSmall(finv, g) - return h, f, ginv + rqMultSmall(h, finv, g) } // c = encrypt(r,h) func encrypt(r []small, h []Fq) []Fq { - - hr := rqMultSmall(h, r) + hr := make([]Fq, p) + rqMultSmall(hr, h, r) c := round(hr) - return c - } // r = decrypt(c,(f,ginv)) @@ -518,7 +513,7 @@ func decrypt(c []Fq, f []small, ginv []small) []small { r := make([]small, p) cf := make([]Fq, p) - cf = rqMultSmall(c, f) + rqMultSmall(cf, c, f) cf3 := rqMult3(cf) e := r3FromRq(cf3) ev := r3Mult(e, ginv) @@ -651,7 +646,10 @@ func roundedDecode(r []Fq, s []byte) { // pk,sk = zKeyGen() func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { - h, f, v := keyGen(gen) + h := make([]Fq, p) + f := make([]small, p) + v := make([]small, p) + keyGen(h, f, v, gen) rqEncode(pk, h) smallEncode(sk, f) @@ -743,7 +741,14 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { // Takes as input a public key // Returns ciphertext and shared key // c,k = encap(pk) -func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { + if seed == nil { + seed = make([]byte, 4*p) + cryptoRand.Read(seed) + } + if len(seed) != 4*p { + panic("seed must be of length EncapsulationSeedSize") + } if len(c) != CiphertextSize { panic("ct must be of length CiphertextSize") } @@ -758,7 +763,7 @@ func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { cache := make([]byte, hashBytes) hashPrefix(cache, 4, pk, publicKeysBytes) - copy(r[:], shortRandom(gen)) + shortRandom(r[:], seed) hide(c, r_enc, r, pk, cache) hashSession(k, 1, r_enc, c) @@ -824,11 +829,12 @@ var sch kem.Scheme = &scheme{} // Scheme returns a KEM interface. func Scheme() kem.Scheme { return sch } -func (*scheme) Name() string { return "sntrup653" } -func (*scheme) PublicKeySize() int { return PublicKeySize } -func (*scheme) PrivateKeySize() int { return PrivateKeySize } -func (*scheme) SharedKeySize() int { return SharedKeySize } -func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) Name() string { return "sntrup653" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } func (sk *PrivateKey) Scheme() kem.Scheme { return sch } func (pk *PublicKey) Scheme() kem.Scheme { return sch } @@ -879,6 +885,11 @@ func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { } func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { + + if gen == nil { + panic("A nist DRBG must be provided") + } + var pk [PublicKeySize]byte var sk [PrivateKeySize]byte @@ -902,11 +913,7 @@ func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { } -func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DRBG) (ct, ss []byte, err error) { - - if gen == nil { - panic("A nist DRBG must be provided") - } +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) (ct, ss []byte, err error) { ct = make([]byte, CiphertextSize) ss = make([]byte, SharedKeySize) @@ -916,7 +923,7 @@ func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.D return nil, nil, kem.ErrTypeMismatch } - pub.EncapsulateTo(ct, ss, gen) + pub.EncapsulateTo(ct, ss, seed) return ct, ss, nil } diff --git a/kem/ntruprime/sntrup761/ntruprime.go b/kem/ntruprime/sntrup761/ntruprime.go index 05521828c..56f83b422 100644 --- a/kem/ntruprime/sntrup761/ntruprime.go +++ b/kem/ntruprime/sntrup761/ntruprime.go @@ -45,6 +45,9 @@ const ( ) const ( + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = 4 * p + // Size of the established shared key. SharedKeySize = ntrup.SharedKeySize @@ -153,8 +156,8 @@ func r3Mult(f []small, g []small) (h []small) { // Calculates the reciprocal of R3 polynomials // Returns 0 if recip succeeded; else -1 -func r3Recip(in []small) ([]small, int) { - out := make([]small, p) +func r3Recip(out []small, in []small) int { + // out := make([]small, p) f := make([]small, p+1) g := make([]small, p+1) v := make([]small, p+1) @@ -217,15 +220,14 @@ func r3Recip(in []small) ([]small, int) { out[i] = small(sign * int(v[p-1-i])) } - return out, internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16_nonzero_mask(int16(delta)) } // Polynomials mod q // h = f*g in the ring Rq */ -func rqMultSmall(f []Fq, g []small) (h []Fq) { - h = make([]Fq, p) +func rqMultSmall(h []Fq, f []Fq, g []small) { fg := make([]Fq, p+p-1) var result Fq @@ -254,7 +256,6 @@ func rqMultSmall(f []Fq, g []small) (h []Fq) { for i := 0; i < p; i++ { h[i] = fg[i] } - return h } // h = 3f in Rq @@ -386,8 +387,7 @@ func cryptoSortUint32(x []uint32, n int) { } // Sorting to generate short polynomial -func shortFromList(in []int32) []small { - out := make([]small, p) +func shortFromList(out []small, in []int32) { L := make([]uint32, p) var neg2, neg3 int = -2, -3 @@ -405,7 +405,6 @@ func shortFromList(in []int32) []small { for i := 0; i < p; i++ { out[i] = small((L[i] & 3) - 1) } - return out } // Underlying hash function @@ -431,33 +430,23 @@ func hashPrefix(out []byte, b int, in []byte, inlen int) { // Higher level randomness // Returns a random unsigned integer -// A generator can be passed for deterministic number generation -func urandom32(gen *nist.DRBG) uint32 { - - c := make([]byte, 4) +func urandom32(seed []byte) uint32 { var out [4]uint32 - if gen != nil { - gen.Fill(c) - } else { - cryptoRand.Read(c) - - } - - out[0] = uint32(c[0]) - out[1] = uint32(c[1]) << 8 - out[2] = uint32(c[2]) << 16 - out[3] = uint32(c[3]) << 24 + out[0] = uint32(seed[0]) + out[1] = uint32(seed[1]) << 8 + out[2] = uint32(seed[2]) << 16 + out[3] = uint32(seed[3]) << 24 return out[0] + out[1] + out[2] + out[3] } // Generates a random short polynomial -func shortRandom(gen *nist.DRBG) []small { +func shortRandom(out []small, seed []byte) { L := make([]uint32, p) for i := 0; i < p; i++ { - L[i] = urandom32(gen) + L[i] = urandom32(seed[4*i : 4*i+4]) } // Converts uint32 array to int32 array @@ -465,52 +454,58 @@ func shortRandom(gen *nist.DRBG) []small { for i := 0; i < len(L); i++ { L_int32[i] = int32(L[i]) } - out := shortFromList(L_int32) - - return out - + shortFromList(out, L_int32) } // Generates a random list of small -func smallRandom(gen *nist.DRBG) []small { - - out := make([]small, p) +func smallRandom(out []small, seed []byte) { for i := 0; i < p; i++ { - out[i] = small(((urandom32(gen)&0x3fffffff)*3)>>30) - 1 + out[i] = small(((urandom32(seed[4*i:4*i+4])&0x3fffffff)*3)>>30) - 1 } - return out } // Streamlined NTRU Prime Core // h,(f,ginv) = keyGen() -func keyGen(gen *nist.DRBG) (h []Fq, f []small, ginv []small) { +func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { g := make([]small, p) - var err int - for { - g = smallRandom(gen) - ginv, err = r3Recip(g) - if err == 0 { - break - } + seed := make([]byte, 4*p+4*p) + if gen == nil { + for { + cryptoRand.Read(seed[:4*p]) + smallRandom(g, seed[:4*p]) + if r3Recip(ginv, g) == 0 { + break + } + } + cryptoRand.Read(seed[4*p:]) + } else { + for { + for i := 0; i < p; i++ { + gen.Fill(seed[4*i : 4*i+4]) + } + smallRandom(g, seed[:4*p]) + if r3Recip(ginv, g) == 0 { + break + } + } + for i := 0; i < p; i++ { + gen.Fill(seed[4*p+4*i : 4*p+4*i+4]) + } } - - f = shortRandom(gen) + shortRandom(f, seed[4*p:]) finv, _ := rqRecip3(f) /* always works */ - h = rqMultSmall(finv, g) - return h, f, ginv + rqMultSmall(h, finv, g) } // c = encrypt(r,h) func encrypt(r []small, h []Fq) []Fq { - - hr := rqMultSmall(h, r) + hr := make([]Fq, p) + rqMultSmall(hr, h, r) c := round(hr) - return c - } // r = decrypt(c,(f,ginv)) @@ -518,7 +513,7 @@ func decrypt(c []Fq, f []small, ginv []small) []small { r := make([]small, p) cf := make([]Fq, p) - cf = rqMultSmall(c, f) + rqMultSmall(cf, c, f) cf3 := rqMult3(cf) e := r3FromRq(cf3) ev := r3Mult(e, ginv) @@ -651,7 +646,10 @@ func roundedDecode(r []Fq, s []byte) { // pk,sk = zKeyGen() func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { - h, f, v := keyGen(gen) + h := make([]Fq, p) + f := make([]small, p) + v := make([]small, p) + keyGen(h, f, v, gen) rqEncode(pk, h) smallEncode(sk, f) @@ -743,7 +741,14 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { // Takes as input a public key // Returns ciphertext and shared key // c,k = encap(pk) -func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { + if seed == nil { + seed = make([]byte, 4*p) + cryptoRand.Read(seed) + } + if len(seed) != 4*p { + panic("seed must be of length EncapsulationSeedSize") + } if len(c) != CiphertextSize { panic("ct must be of length CiphertextSize") } @@ -758,7 +763,7 @@ func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { cache := make([]byte, hashBytes) hashPrefix(cache, 4, pk, publicKeysBytes) - copy(r[:], shortRandom(gen)) + shortRandom(r[:], seed) hide(c, r_enc, r, pk, cache) hashSession(k, 1, r_enc, c) @@ -824,11 +829,12 @@ var sch kem.Scheme = &scheme{} // Scheme returns a KEM interface. func Scheme() kem.Scheme { return sch } -func (*scheme) Name() string { return "sntrup761" } -func (*scheme) PublicKeySize() int { return PublicKeySize } -func (*scheme) PrivateKeySize() int { return PrivateKeySize } -func (*scheme) SharedKeySize() int { return SharedKeySize } -func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) Name() string { return "sntrup761" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } func (sk *PrivateKey) Scheme() kem.Scheme { return sch } func (pk *PublicKey) Scheme() kem.Scheme { return sch } @@ -879,6 +885,11 @@ func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { } func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { + + if gen == nil { + panic("A nist DRBG must be provided") + } + var pk [PublicKeySize]byte var sk [PrivateKeySize]byte @@ -902,11 +913,7 @@ func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { } -func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DRBG) (ct, ss []byte, err error) { - - if gen == nil { - panic("A nist DRBG must be provided") - } +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) (ct, ss []byte, err error) { ct = make([]byte, CiphertextSize) ss = make([]byte, SharedKeySize) @@ -916,7 +923,7 @@ func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.D return nil, nil, kem.ErrTypeMismatch } - pub.EncapsulateTo(ct, ss, gen) + pub.EncapsulateTo(ct, ss, seed) return ct, ss, nil } diff --git a/kem/ntruprime/sntrup857/ntruprime.go b/kem/ntruprime/sntrup857/ntruprime.go index b475cdd39..fd3f01af8 100644 --- a/kem/ntruprime/sntrup857/ntruprime.go +++ b/kem/ntruprime/sntrup857/ntruprime.go @@ -45,6 +45,9 @@ const ( ) const ( + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = 4 * p + // Size of the established shared key. SharedKeySize = ntrup.SharedKeySize @@ -153,8 +156,8 @@ func r3Mult(f []small, g []small) (h []small) { // Calculates the reciprocal of R3 polynomials // Returns 0 if recip succeeded; else -1 -func r3Recip(in []small) ([]small, int) { - out := make([]small, p) +func r3Recip(out []small, in []small) int { + // out := make([]small, p) f := make([]small, p+1) g := make([]small, p+1) v := make([]small, p+1) @@ -217,15 +220,14 @@ func r3Recip(in []small) ([]small, int) { out[i] = small(sign * int(v[p-1-i])) } - return out, internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16_nonzero_mask(int16(delta)) } // Polynomials mod q // h = f*g in the ring Rq */ -func rqMultSmall(f []Fq, g []small) (h []Fq) { - h = make([]Fq, p) +func rqMultSmall(h []Fq, f []Fq, g []small) { fg := make([]Fq, p+p-1) var result Fq @@ -254,7 +256,6 @@ func rqMultSmall(f []Fq, g []small) (h []Fq) { for i := 0; i < p; i++ { h[i] = fg[i] } - return h } // h = 3f in Rq @@ -386,8 +387,7 @@ func cryptoSortUint32(x []uint32, n int) { } // Sorting to generate short polynomial -func shortFromList(in []int32) []small { - out := make([]small, p) +func shortFromList(out []small, in []int32) { L := make([]uint32, p) var neg2, neg3 int = -2, -3 @@ -405,7 +405,6 @@ func shortFromList(in []int32) []small { for i := 0; i < p; i++ { out[i] = small((L[i] & 3) - 1) } - return out } // Underlying hash function @@ -431,33 +430,23 @@ func hashPrefix(out []byte, b int, in []byte, inlen int) { // Higher level randomness // Returns a random unsigned integer -// A generator can be passed for deterministic number generation -func urandom32(gen *nist.DRBG) uint32 { - - c := make([]byte, 4) +func urandom32(seed []byte) uint32 { var out [4]uint32 - if gen != nil { - gen.Fill(c) - } else { - cryptoRand.Read(c) - - } - - out[0] = uint32(c[0]) - out[1] = uint32(c[1]) << 8 - out[2] = uint32(c[2]) << 16 - out[3] = uint32(c[3]) << 24 + out[0] = uint32(seed[0]) + out[1] = uint32(seed[1]) << 8 + out[2] = uint32(seed[2]) << 16 + out[3] = uint32(seed[3]) << 24 return out[0] + out[1] + out[2] + out[3] } // Generates a random short polynomial -func shortRandom(gen *nist.DRBG) []small { +func shortRandom(out []small, seed []byte) { L := make([]uint32, p) for i := 0; i < p; i++ { - L[i] = urandom32(gen) + L[i] = urandom32(seed[4*i : 4*i+4]) } // Converts uint32 array to int32 array @@ -465,52 +454,58 @@ func shortRandom(gen *nist.DRBG) []small { for i := 0; i < len(L); i++ { L_int32[i] = int32(L[i]) } - out := shortFromList(L_int32) - - return out - + shortFromList(out, L_int32) } // Generates a random list of small -func smallRandom(gen *nist.DRBG) []small { - - out := make([]small, p) +func smallRandom(out []small, seed []byte) { for i := 0; i < p; i++ { - out[i] = small(((urandom32(gen)&0x3fffffff)*3)>>30) - 1 + out[i] = small(((urandom32(seed[4*i:4*i+4])&0x3fffffff)*3)>>30) - 1 } - return out } // Streamlined NTRU Prime Core // h,(f,ginv) = keyGen() -func keyGen(gen *nist.DRBG) (h []Fq, f []small, ginv []small) { +func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { g := make([]small, p) - var err int - for { - g = smallRandom(gen) - ginv, err = r3Recip(g) - if err == 0 { - break - } + seed := make([]byte, 4*p+4*p) + if gen == nil { + for { + cryptoRand.Read(seed[:4*p]) + smallRandom(g, seed[:4*p]) + if r3Recip(ginv, g) == 0 { + break + } + } + cryptoRand.Read(seed[4*p:]) + } else { + for { + for i := 0; i < p; i++ { + gen.Fill(seed[4*i : 4*i+4]) + } + smallRandom(g, seed[:4*p]) + if r3Recip(ginv, g) == 0 { + break + } + } + for i := 0; i < p; i++ { + gen.Fill(seed[4*p+4*i : 4*p+4*i+4]) + } } - - f = shortRandom(gen) + shortRandom(f, seed[4*p:]) finv, _ := rqRecip3(f) /* always works */ - h = rqMultSmall(finv, g) - return h, f, ginv + rqMultSmall(h, finv, g) } // c = encrypt(r,h) func encrypt(r []small, h []Fq) []Fq { - - hr := rqMultSmall(h, r) + hr := make([]Fq, p) + rqMultSmall(hr, h, r) c := round(hr) - return c - } // r = decrypt(c,(f,ginv)) @@ -518,7 +513,7 @@ func decrypt(c []Fq, f []small, ginv []small) []small { r := make([]small, p) cf := make([]Fq, p) - cf = rqMultSmall(c, f) + rqMultSmall(cf, c, f) cf3 := rqMult3(cf) e := r3FromRq(cf3) ev := r3Mult(e, ginv) @@ -651,7 +646,10 @@ func roundedDecode(r []Fq, s []byte) { // pk,sk = zKeyGen() func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { - h, f, v := keyGen(gen) + h := make([]Fq, p) + f := make([]small, p) + v := make([]small, p) + keyGen(h, f, v, gen) rqEncode(pk, h) smallEncode(sk, f) @@ -743,7 +741,14 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { // Takes as input a public key // Returns ciphertext and shared key // c,k = encap(pk) -func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { + if seed == nil { + seed = make([]byte, 4*p) + cryptoRand.Read(seed) + } + if len(seed) != 4*p { + panic("seed must be of length EncapsulationSeedSize") + } if len(c) != CiphertextSize { panic("ct must be of length CiphertextSize") } @@ -758,7 +763,7 @@ func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { cache := make([]byte, hashBytes) hashPrefix(cache, 4, pk, publicKeysBytes) - copy(r[:], shortRandom(gen)) + shortRandom(r[:], seed) hide(c, r_enc, r, pk, cache) hashSession(k, 1, r_enc, c) @@ -824,11 +829,12 @@ var sch kem.Scheme = &scheme{} // Scheme returns a KEM interface. func Scheme() kem.Scheme { return sch } -func (*scheme) Name() string { return "sntrup857" } -func (*scheme) PublicKeySize() int { return PublicKeySize } -func (*scheme) PrivateKeySize() int { return PrivateKeySize } -func (*scheme) SharedKeySize() int { return SharedKeySize } -func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) Name() string { return "sntrup857" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } func (sk *PrivateKey) Scheme() kem.Scheme { return sch } func (pk *PublicKey) Scheme() kem.Scheme { return sch } @@ -879,6 +885,11 @@ func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { } func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { + + if gen == nil { + panic("A nist DRBG must be provided") + } + var pk [PublicKeySize]byte var sk [PrivateKeySize]byte @@ -902,11 +913,7 @@ func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { } -func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DRBG) (ct, ss []byte, err error) { - - if gen == nil { - panic("A nist DRBG must be provided") - } +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) (ct, ss []byte, err error) { ct = make([]byte, CiphertextSize) ss = make([]byte, SharedKeySize) @@ -916,7 +923,7 @@ func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.D return nil, nil, kem.ErrTypeMismatch } - pub.EncapsulateTo(ct, ss, gen) + pub.EncapsulateTo(ct, ss, seed) return ct, ss, nil } diff --git a/kem/ntruprime/sntrup953/ntruprime.go b/kem/ntruprime/sntrup953/ntruprime.go index 1c2d06ceb..5e327cfdd 100644 --- a/kem/ntruprime/sntrup953/ntruprime.go +++ b/kem/ntruprime/sntrup953/ntruprime.go @@ -45,6 +45,9 @@ const ( ) const ( + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = 4 * p + // Size of the established shared key. SharedKeySize = ntrup.SharedKeySize @@ -153,8 +156,8 @@ func r3Mult(f []small, g []small) (h []small) { // Calculates the reciprocal of R3 polynomials // Returns 0 if recip succeeded; else -1 -func r3Recip(in []small) ([]small, int) { - out := make([]small, p) +func r3Recip(out []small, in []small) int { + // out := make([]small, p) f := make([]small, p+1) g := make([]small, p+1) v := make([]small, p+1) @@ -217,15 +220,14 @@ func r3Recip(in []small) ([]small, int) { out[i] = small(sign * int(v[p-1-i])) } - return out, internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16_nonzero_mask(int16(delta)) } // Polynomials mod q // h = f*g in the ring Rq */ -func rqMultSmall(f []Fq, g []small) (h []Fq) { - h = make([]Fq, p) +func rqMultSmall(h []Fq, f []Fq, g []small) { fg := make([]Fq, p+p-1) var result Fq @@ -254,7 +256,6 @@ func rqMultSmall(f []Fq, g []small) (h []Fq) { for i := 0; i < p; i++ { h[i] = fg[i] } - return h } // h = 3f in Rq @@ -386,8 +387,7 @@ func cryptoSortUint32(x []uint32, n int) { } // Sorting to generate short polynomial -func shortFromList(in []int32) []small { - out := make([]small, p) +func shortFromList(out []small, in []int32) { L := make([]uint32, p) var neg2, neg3 int = -2, -3 @@ -405,7 +405,6 @@ func shortFromList(in []int32) []small { for i := 0; i < p; i++ { out[i] = small((L[i] & 3) - 1) } - return out } // Underlying hash function @@ -431,33 +430,23 @@ func hashPrefix(out []byte, b int, in []byte, inlen int) { // Higher level randomness // Returns a random unsigned integer -// A generator can be passed for deterministic number generation -func urandom32(gen *nist.DRBG) uint32 { - - c := make([]byte, 4) +func urandom32(seed []byte) uint32 { var out [4]uint32 - if gen != nil { - gen.Fill(c) - } else { - cryptoRand.Read(c) - - } - - out[0] = uint32(c[0]) - out[1] = uint32(c[1]) << 8 - out[2] = uint32(c[2]) << 16 - out[3] = uint32(c[3]) << 24 + out[0] = uint32(seed[0]) + out[1] = uint32(seed[1]) << 8 + out[2] = uint32(seed[2]) << 16 + out[3] = uint32(seed[3]) << 24 return out[0] + out[1] + out[2] + out[3] } // Generates a random short polynomial -func shortRandom(gen *nist.DRBG) []small { +func shortRandom(out []small, seed []byte) { L := make([]uint32, p) for i := 0; i < p; i++ { - L[i] = urandom32(gen) + L[i] = urandom32(seed[4*i : 4*i+4]) } // Converts uint32 array to int32 array @@ -465,52 +454,58 @@ func shortRandom(gen *nist.DRBG) []small { for i := 0; i < len(L); i++ { L_int32[i] = int32(L[i]) } - out := shortFromList(L_int32) - - return out - + shortFromList(out, L_int32) } // Generates a random list of small -func smallRandom(gen *nist.DRBG) []small { - - out := make([]small, p) +func smallRandom(out []small, seed []byte) { for i := 0; i < p; i++ { - out[i] = small(((urandom32(gen)&0x3fffffff)*3)>>30) - 1 + out[i] = small(((urandom32(seed[4*i:4*i+4])&0x3fffffff)*3)>>30) - 1 } - return out } // Streamlined NTRU Prime Core // h,(f,ginv) = keyGen() -func keyGen(gen *nist.DRBG) (h []Fq, f []small, ginv []small) { +func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { g := make([]small, p) - var err int - for { - g = smallRandom(gen) - ginv, err = r3Recip(g) - if err == 0 { - break - } + seed := make([]byte, 4*p+4*p) + if gen == nil { + for { + cryptoRand.Read(seed[:4*p]) + smallRandom(g, seed[:4*p]) + if r3Recip(ginv, g) == 0 { + break + } + } + cryptoRand.Read(seed[4*p:]) + } else { + for { + for i := 0; i < p; i++ { + gen.Fill(seed[4*i : 4*i+4]) + } + smallRandom(g, seed[:4*p]) + if r3Recip(ginv, g) == 0 { + break + } + } + for i := 0; i < p; i++ { + gen.Fill(seed[4*p+4*i : 4*p+4*i+4]) + } } - - f = shortRandom(gen) + shortRandom(f, seed[4*p:]) finv, _ := rqRecip3(f) /* always works */ - h = rqMultSmall(finv, g) - return h, f, ginv + rqMultSmall(h, finv, g) } // c = encrypt(r,h) func encrypt(r []small, h []Fq) []Fq { - - hr := rqMultSmall(h, r) + hr := make([]Fq, p) + rqMultSmall(hr, h, r) c := round(hr) - return c - } // r = decrypt(c,(f,ginv)) @@ -518,7 +513,7 @@ func decrypt(c []Fq, f []small, ginv []small) []small { r := make([]small, p) cf := make([]Fq, p) - cf = rqMultSmall(c, f) + rqMultSmall(cf, c, f) cf3 := rqMult3(cf) e := r3FromRq(cf3) ev := r3Mult(e, ginv) @@ -651,7 +646,10 @@ func roundedDecode(r []Fq, s []byte) { // pk,sk = zKeyGen() func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { - h, f, v := keyGen(gen) + h := make([]Fq, p) + f := make([]small, p) + v := make([]small, p) + keyGen(h, f, v, gen) rqEncode(pk, h) smallEncode(sk, f) @@ -743,7 +741,14 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { // Takes as input a public key // Returns ciphertext and shared key // c,k = encap(pk) -func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { + if seed == nil { + seed = make([]byte, 4*p) + cryptoRand.Read(seed) + } + if len(seed) != 4*p { + panic("seed must be of length EncapsulationSeedSize") + } if len(c) != CiphertextSize { panic("ct must be of length CiphertextSize") } @@ -758,7 +763,7 @@ func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { cache := make([]byte, hashBytes) hashPrefix(cache, 4, pk, publicKeysBytes) - copy(r[:], shortRandom(gen)) + shortRandom(r[:], seed) hide(c, r_enc, r, pk, cache) hashSession(k, 1, r_enc, c) @@ -824,11 +829,12 @@ var sch kem.Scheme = &scheme{} // Scheme returns a KEM interface. func Scheme() kem.Scheme { return sch } -func (*scheme) Name() string { return "sntrup953" } -func (*scheme) PublicKeySize() int { return PublicKeySize } -func (*scheme) PrivateKeySize() int { return PrivateKeySize } -func (*scheme) SharedKeySize() int { return SharedKeySize } -func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) Name() string { return "sntrup953" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } func (sk *PrivateKey) Scheme() kem.Scheme { return sch } func (pk *PublicKey) Scheme() kem.Scheme { return sch } @@ -879,6 +885,11 @@ func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { } func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { + + if gen == nil { + panic("A nist DRBG must be provided") + } + var pk [PublicKeySize]byte var sk [PrivateKeySize]byte @@ -902,11 +913,7 @@ func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { } -func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DRBG) (ct, ss []byte, err error) { - - if gen == nil { - panic("A nist DRBG must be provided") - } +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) (ct, ss []byte, err error) { ct = make([]byte, CiphertextSize) ss = make([]byte, SharedKeySize) @@ -916,7 +923,7 @@ func (*scheme) EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.D return nil, nil, kem.ErrTypeMismatch } - pub.EncapsulateTo(ct, ss, gen) + pub.EncapsulateTo(ct, ss, seed) return ct, ss, nil } diff --git a/kem/ntruprime/templates/ntrulpr.templ.go b/kem/ntruprime/templates/ntrulpr.templ.go index e482b1821..fbcb4ca88 100644 --- a/kem/ntruprime/templates/ntrulpr.templ.go +++ b/kem/ntruprime/templates/ntrulpr.templ.go @@ -112,8 +112,7 @@ func right(T int8) Fq { // Polynomials mod q // h = f*g in the ring Rq */ -func rqMultSmall(f []Fq, g []small) (h []Fq) { - h = make([]Fq, p) +func rqMultSmall(h []Fq, f []Fq, g []small) { fg := make([]Fq, p+p-1) var result Fq @@ -142,17 +141,14 @@ func rqMultSmall(f []Fq, g []small) (h []Fq) { for i := 0; i < p; i++ { h[i] = fg[i] } - return h } // Rounding all coefficients of a polynomial to the nearest multiple of 3 // Rounded polynomials mod q -func round(a []Fq) []Fq { - out := make([]Fq, p) +func round(out []Fq, a []Fq) { for i := 0; i < p; i++ { out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) } - return out } // Returns (min(x, y), max(x, y)), executes in constant time @@ -197,8 +193,7 @@ func cryptoSortUint32(x []uint32, n int) { } // Sorting to generate short polynomial -func shortFromList(in []int32) []small { - out := make([]small, p) +func shortFromList(out[]small,in []int32) { L := make([]uint32, p) var neg2, neg3 int = -2, -3 @@ -216,7 +211,6 @@ func shortFromList(in []int32) []small { for i := 0; i < p; i++ { out[i] = small((L[i] & 3) - 1) } - return out } // Underlying hash function @@ -227,14 +221,14 @@ func shortFromList(in []int32) []small { // e.g., b = 0 means out = Hash0(in) func hashPrefix(out []byte, b int, in []byte, inlen int) { x := make([]byte, inlen+1) - h := make([]byte, 64) + // h := make([]byte, 64) x[0] = byte(b) copy(x[1:], in) hash := sha512.New() hash.Write([]byte(x)) - h = hash.Sum(nil) + h := hash.Sum(nil) copy(out, h[:32]) @@ -244,26 +238,17 @@ func hashPrefix(out []byte, b int, in []byte, inlen int) { // Returns a random unsigned integer // generator can be passed for deterministic number generation func urandom32(seed []byte) uint32 { - - c := make([]byte, 4) var out [4]uint32 - if seed != nil { - copy(c, seed) - } else { - cryptoRand.Read(c) - - } - - out[0] = uint32(c[0]) - out[1] = uint32(c[1]) << 8 - out[2] = uint32(c[2]) << 16 - out[3] = uint32(c[3]) << 24 + out[0] = uint32(seed[0]) + out[1] = uint32(seed[1]) << 8 + out[2] = uint32(seed[2]) << 16 + out[3] = uint32(seed[3]) << 24 return out[0] + out[1] + out[2] + out[3] } // Generates a random short polynomial -func shortRandom(seed []byte) []small { +func shortRandom(out[]small,seed []byte) { L := make([]uint32, p) @@ -282,42 +267,40 @@ func shortRandom(seed []byte) []small { for i := 0; i < len(L); i++ { L_int32[i] = int32(L[i]) } - out := shortFromList(L_int32) - - return out - + shortFromList(out,L_int32) } // NTRU LPRime Core // (G,A),a = keyGen(G); leaves G unchanged func keyGen(A []Fq, a []small, G []Fq, seed []byte) { - - copy(a, shortRandom(seed)) - aG := rqMultSmall(G, a) - copy(A, round(aG)) + aG := make([]Fq, p) + shortRandom(a,seed) + rqMultSmall(aG, G, a) + round(A,aG) } // B,T = encrypt(r,(G,A),b) -func encrypt(r []int8, G []Fq, A []Fq, b []small) ([]Fq, []int8) { - T := make([]int8, I) - bG := rqMultSmall(G, b) - B := round(bG) - bA := rqMultSmall(A, b) +func encrypt(B []Fq, T []int8,r []int8, G []Fq, A []Fq, b []small) { + bG := make([]Fq, p) + bA := make([]Fq, p) + + rqMultSmall(bG, G, b) + round(B,bG) + rqMultSmall(bA, A, b) for i := 0; i < I; i++ { T[i] = top(fqFreeze(int32(bA[i]) + int32(r[i])*q12)) } - - return B, T } // r = decrypt((B,T),a) func decrypt(B []Fq, T []int8, a []small) []int8 { + aB := make([]Fq, p) r := make([]int8, I) - aB := rqMultSmall(B, a) + rqMultSmall(aB, B, a) for i := 0; i < I; i++ { r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) @@ -341,17 +324,16 @@ func inputsEncode(s []byte, r Inputs) { func expand(L []uint32, k []byte) { temp := make([]byte, len(L)) // plaintext to be encrypted. Should be of the same size as L (4*P) - aes_nonce := make([]byte, aes.BlockSize) // IV + ciphertext := make([]byte, aes.BlockSize+len(temp)) block, err := aes.NewCipher(k[:32]) if err != nil { panic(err) } - ciphertext := make([]byte, len(temp)) // same length as plaintext - - stream := cipher.NewCTR(block, aes_nonce) - stream.XORKeyStream(ciphertext, temp) + stream := cipher.NewCTR(block, ciphertext[:aes.BlockSize]) + stream.XORKeyStream(ciphertext[aes.BlockSize:], temp) + ciphertext=ciphertext[aes.BlockSize:] // convert byte to uint32 for i := 0; i < len(temp); i++ { @@ -369,18 +351,16 @@ func expand(L []uint32, k []byte) { // generator, hashShort // G = generator(k) -func generator(k []byte) (G []Fq) { - G = make([]Fq, p) +func generator(G []Fq, k []byte) { L := make([]uint32, 4*p) expand(L, k) for i := 0; i < p; i++ { G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) } - return G } // out = hashShort(r) -func hashShort(r Inputs) (out []small) { +func hashShort(out []small,r Inputs) { s := make([]byte, inputsBytes) inputsEncode(s, r) h := make([]byte, hashBytes) @@ -395,8 +375,7 @@ func hashShort(r Inputs) (out []small) { for i := 0; i < p; i++ { L_int32[i] = int32(L[i]) } - out = shortFromList(L_int32) - return out + shortFromList(out,L_int32) } // NTRU LPRime expand @@ -404,21 +383,21 @@ func hashShort(r Inputs) (out []small) { // (S,A),a = xKeyGen() func xKeyGen(S []byte, A []Fq, a []small, seed []byte) { - if seed == nil { - cryptoRand.Read(S) - } else { - copy(S, seed[:seedBytes]) - seed = seed[seedBytes:] - } + copy(S, seed[:seedBytes]) + seed = seed[seedBytes:] + G := make([]Fq, p) - G := generator(S) + generator(G,S) keyGen(A, a, G, seed) } // B,T = xEncrypt(r,(S,A)) -func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { - G := generator(S) +func xEncrypt(B []Fq, T []int8, r []int8, S []byte, A []Fq) { + G := make([]Fq, p) + + generator(G,S) + b := make([]small, p) // convert []int8 to Inputs var r_inputs Inputs @@ -426,9 +405,9 @@ func xEncrypt(r []int8, S []byte, A []Fq) (B []Fq, T []int8) { r_inputs[i] = r[i] } - b := hashShort(r_inputs) + hashShort(b, r_inputs) - return encrypt(r, G, A, b) + encrypt(B, T, r, G, A, b) } // Encoding small polynomials (including short polynomials) @@ -514,13 +493,11 @@ func roundedDecode(r []Fq, s []byte) { // Encoding top polynomials -func topEncode(T []int8) (s []byte) { - s = make([]byte, topBytes) +func topEncode(s []byte,T []int8) { for i := 0; i < topBytes; i++ { s[i] = byte(T[2*i] + (T[2*i+1] << 4)) } - return s } func topDecode(s []byte) (T []int8) { @@ -537,19 +514,9 @@ func topDecode(s []byte) (T []int8) { // Streamlined NTRU Prime Core plus encoding func inputsRandom(seed []byte) (r Inputs) { - - s := make([]byte, inputsBytes) - - if seed != nil { - copy(s, seed) - } else { - cryptoRand.Read(s) - } - for i := 0; i < I; i++ { - r[i] = int8(1 & (s[i>>3] >> (i & 7))) + r[i] = int8(1 & (seed[i>>3] >> (i & 7))) } - return r } @@ -570,13 +537,15 @@ func zKeyGen(pk []byte, sk []byte, seed []byte) { // c = zEncrypt(r,pk) func zEncrypt(c []byte, r Inputs, pk []byte) { A := make([]Fq, p) + B:=make([]Fq,p) + T := make([]int8, I) roundedDecode(A, pk[seedBytes:]) - B, T := xEncrypt(r[:], pk[:seedBytes], A) + xEncrypt(B,T,r[:], pk[:seedBytes], A) roundedEncode(c, B) c = c[roundedBytes:] - copy(c, topEncode(T)) + topEncode(c,T) } // r = zDecrypt(C,sk) @@ -636,12 +605,8 @@ func kemKeyGen(pk []byte, sk []byte, seed []byte) { copy(sk, pk) sk = sk[publicKeysBytes:] - if seed != nil { - copy(sk[:inputsBytes], seed) + copy(sk[:inputsBytes], seed) - } else { - cryptoRand.Read(sk[:inputsBytes]) - } sk = sk[inputsBytes:] hashPrefix(sk, 4, pk, publicKeysBytes) @@ -662,7 +627,12 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { // c,k = encap(pk) func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { - if seed != nil && len(seed) != EncapsulationSeedSize { + if seed == nil { + seed=make([]byte,EncapsulationSeedSize) + cryptoRand.Read(seed) + } + + if len(seed) != EncapsulationSeedSize { panic("seed must be of length EncapsulationSeedSize") } if len(c) != CiphertextSize { diff --git a/kem/ntruprime/templates/sntrup.templ.go b/kem/ntruprime/templates/sntrup.templ.go index de9cdd309..e205f711d 100644 --- a/kem/ntruprime/templates/sntrup.templ.go +++ b/kem/ntruprime/templates/sntrup.templ.go @@ -50,6 +50,9 @@ const ( ) const ( + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = 4*p + // Size of the established shared key. SharedKeySize = ntrup.SharedKeySize @@ -159,8 +162,8 @@ func r3Mult(f []small, g []small) (h []small) { // Calculates the reciprocal of R3 polynomials // Returns 0 if recip succeeded; else -1 -func r3Recip(in []small) ([]small, int) { - out := make([]small, p) +func r3Recip(out []small, in []small) int { + // out := make([]small, p) f := make([]small, p+1) g := make([]small, p+1) v := make([]small, p+1) @@ -223,15 +226,14 @@ func r3Recip(in []small) ([]small, int) { out[i] = small(sign * int(v[p-1-i])) } - return out, internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16_nonzero_mask(int16(delta)) } // Polynomials mod q // h = f*g in the ring Rq */ -func rqMultSmall(f []Fq, g []small) (h []Fq) { - h = make([]Fq, p) +func rqMultSmall(h []Fq, f []Fq, g []small) { fg := make([]Fq, p+p-1) var result Fq @@ -260,7 +262,6 @@ func rqMultSmall(f []Fq, g []small) (h []Fq) { for i := 0; i < p; i++ { h[i] = fg[i] } - return h } // h = 3f in Rq @@ -393,8 +394,7 @@ func cryptoSortUint32(x []uint32, n int) { } // Sorting to generate short polynomial -func shortFromList(in []int32) []small { - out := make([]small, p) +func shortFromList(out []small, in []int32) { L := make([]uint32, p) var neg2, neg3 int = -2, -3 @@ -412,7 +412,6 @@ func shortFromList(in []int32) []small { for i := 0; i < p; i++ { out[i] = small((L[i] & 3) - 1) } - return out } // Underlying hash function @@ -438,33 +437,23 @@ func hashPrefix(out []byte, b int, in []byte, inlen int) { // Higher level randomness // Returns a random unsigned integer -// A generator can be passed for deterministic number generation -func urandom32(gen *nist.DRBG) uint32 { - - c := make([]byte, 4) +func urandom32(seed []byte) uint32 { var out [4]uint32 - if gen != nil { - gen.Fill(c) - } else { - cryptoRand.Read(c) - - } - - out[0] = uint32(c[0]) - out[1] = uint32(c[1]) << 8 - out[2] = uint32(c[2]) << 16 - out[3] = uint32(c[3]) << 24 + out[0] = uint32(seed[0]) + out[1] = uint32(seed[1]) << 8 + out[2] = uint32(seed[2]) << 16 + out[3] = uint32(seed[3]) << 24 return out[0] + out[1] + out[2] + out[3] } // Generates a random short polynomial -func shortRandom(gen *nist.DRBG) []small { +func shortRandom(out []small, seed []byte) { L := make([]uint32, p) for i := 0; i < p; i++ { - L[i] = urandom32(gen) + L[i] = urandom32(seed[4*i : 4*i+4]) } // Converts uint32 array to int32 array @@ -472,52 +461,58 @@ func shortRandom(gen *nist.DRBG) []small { for i := 0; i < len(L); i++ { L_int32[i] = int32(L[i]) } - out := shortFromList(L_int32) - - return out - + shortFromList(out, L_int32) } -// Generates a random list of small -func smallRandom(gen *nist.DRBG) []small { - - out := make([]small, p) +// Generates a random list of small +func smallRandom(out []small, seed []byte){ for i := 0; i < p; i++ { - out[i] = small(((urandom32(gen)&0x3fffffff)*3)>>30) - 1 + out[i] = small(((urandom32(seed[4*i:4*i+4])&0x3fffffff)*3)>>30) - 1 } - return out } // Streamlined NTRU Prime Core // h,(f,ginv) = keyGen() -func keyGen(gen *nist.DRBG) (h []Fq, f []small, ginv []small) { +func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { g := make([]small, p) - var err int - for { - g = smallRandom(gen) - ginv, err = r3Recip(g) - if err == 0 { - break - } + seed := make([]byte, 4*p+4*p) + if gen == nil { + for { + cryptoRand.Read(seed[:4*p]) + smallRandom(g, seed[:4*p]) + if r3Recip(ginv, g) == 0 { + break + } + } + cryptoRand.Read(seed[4*p:]) + } else { + for { + for i := 0; i < p; i++ { + gen.Fill(seed[4*i : 4*i+4]) + } + smallRandom(g, seed[:4*p]) + if r3Recip(ginv, g) == 0 { + break + } + } + for i := 0; i < p; i++ { + gen.Fill(seed[4*p+4*i : 4*p+4*i+4]) + } } - - f = shortRandom(gen) + shortRandom(f, seed[4*p:]) finv, _ := rqRecip3(f) /* always works */ - h = rqMultSmall(finv, g) - return h, f, ginv + rqMultSmall(h, finv, g) } // c = encrypt(r,h) func encrypt(r []small, h []Fq) []Fq { - - hr := rqMultSmall(h, r) + hr := make([]Fq, p) + rqMultSmall(hr, h, r) c := round(hr) - return c - } // r = decrypt(c,(f,ginv)) @@ -525,7 +520,7 @@ func decrypt(c []Fq, f []small, ginv []small) []small { r := make([]small, p) cf := make([]Fq, p) - cf = rqMultSmall(c, f) + rqMultSmall(cf, c, f) cf3 := rqMult3(cf) e := r3FromRq(cf3) ev := r3Mult(e, ginv) @@ -659,7 +654,10 @@ func roundedDecode(r []Fq, s []byte) { // pk,sk = zKeyGen() func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { - h, f, v := keyGen(gen) + h := make([]Fq, p) + f := make([]small, p) + v := make([]small, p) + keyGen(h, f, v, gen) rqEncode(pk, h) smallEncode(sk, f) @@ -751,7 +749,14 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { // Takes as input a public key // Returns ciphertext and shared key // c,k = encap(pk) -func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { + if seed == nil { + seed = make([]byte, 4*p) + cryptoRand.Read(seed) + } + if len(seed) != 4*p{ + panic("seed must be of length EncapsulationSeedSize") + } if len(c) != CiphertextSize { panic("ct must be of length CiphertextSize") } @@ -766,7 +771,7 @@ func (pub PublicKey) EncapsulateTo(c []byte, k []byte, gen *nist.DRBG) { cache := make([]byte, hashBytes) hashPrefix(cache, 4, pk, publicKeysBytes) - copy(r[:], shortRandom(gen)) + shortRandom(r[:], seed) hide(c, r_enc, r, pk, cache) hashSession(k, 1, r_enc, c) @@ -832,11 +837,13 @@ var sch kem.Scheme = &scheme{} // Scheme returns a KEM interface. func Scheme() kem.Scheme { return sch } -func (*scheme) Name() string { return "{{.Pkg}}" } -func (*scheme) PublicKeySize() int { return PublicKeySize } -func (*scheme) PrivateKeySize() int { return PrivateKeySize } -func (*scheme) SharedKeySize() int { return SharedKeySize } -func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) Name() string { return "{{.Pkg}}" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } + func (sk *PrivateKey) Scheme() kem.Scheme { return sch } func (pk *PublicKey) Scheme() kem.Scheme { return sch } @@ -888,6 +895,11 @@ func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { } func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { + + if gen == nil { + panic("A nist DRBG must be provided") + } + var pk [PublicKeySize]byte var sk [PrivateKeySize]byte @@ -911,11 +923,7 @@ func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { } -func (*scheme)EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DRBG) (ct, ss []byte, err error) { - - if gen == nil { - panic("A nist DRBG must be provided") - } +func (*scheme)EncapsulateDeterministically(pk kem.PublicKey, seed []byte) (ct, ss []byte, err error) { ct = make([]byte, CiphertextSize) ss = make([]byte, SharedKeySize) @@ -925,7 +933,7 @@ func (*scheme)EncapsulateDeterministicallyFromGen(pk kem.PublicKey, gen *nist.DR return nil, nil, kem.ErrTypeMismatch } - pub.EncapsulateTo(ct, ss, gen) + pub.EncapsulateTo(ct, ss, seed) return ct, ss, nil } diff --git a/kem/schemes/schemes_test.go b/kem/schemes/schemes_test.go index e41840b34..4c8e1853d 100644 --- a/kem/schemes/schemes_test.go +++ b/kem/schemes/schemes_test.go @@ -160,4 +160,11 @@ func Example_schemes() { // Kyber768-X448 // Kyber1024-X448 // P256Kyber768Draft00 + // ntrulpr1013 + // ntrulpr1277 + // ntrulpr653 + // ntrulpr761 + // ntrulpr857 + // ntrulpr953 + } diff --git a/pke/ntruprime/kem/kem.go b/pke/ntruprime/kem/kem.go index dab6ec3ec..ec66b01fa 100644 --- a/pke/ntruprime/kem/kem.go +++ b/pke/ntruprime/kem/kem.go @@ -70,10 +70,13 @@ type Scheme interface { DeriveKeyPairFromGen(gen *nist.DRBG) (PublicKey, PrivateKey) // EncapsulateDeterministically generates a shared key ss for the public - // key deterministically from the given nist DRBG and encapsulates it into + // key deterministically from the given seed and encapsulates it into // a ciphertext ct. If unsure, you're better off using Encapsulate(). - EncapsulateDeterministicallyFromGen(pk PublicKey, gen *nist.DRBG) ( + EncapsulateDeterministically(pk PublicKey, seed []byte) ( ct, ss []byte, err error) + + // Size of seed used in EncapsulateDeterministically(). + EncapsulationSeedSize() int } var ( diff --git a/pke/ntruprime/kem/schemes/sntrup/schemes_test.go b/pke/ntruprime/kem/schemes/sntrup/schemes_test.go new file mode 100644 index 000000000..4e5f0ccdb --- /dev/null +++ b/pke/ntruprime/kem/schemes/sntrup/schemes_test.go @@ -0,0 +1,152 @@ +package schemes_test + +import ( + "bytes" + "fmt" + "testing" + + schemes "github.com/cloudflare/circl/pke/ntruprime/kem/schemes/sntrup" +) + +func TestCaseSensitivity(t *testing.T) { + if schemes.ByName("sntrup1277") != schemes.ByName("SNTRUP1277") { + t.Fatal() + } +} + +func BenchmarkGenerateKeyPair(b *testing.B) { + allSchemes := schemes.All() + for _, scheme := range allSchemes { + scheme := scheme + b.Run(scheme.Name(), func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _, _ = scheme.GenerateKeyPair() + } + }) + } +} + +func BenchmarkEncapsulate(b *testing.B) { + allSchemes := schemes.All() + for _, scheme := range allSchemes { + scheme := scheme + pk, _, _ := scheme.GenerateKeyPair() + b.Run(scheme.Name(), func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _, _ = scheme.Encapsulate(pk) + } + }) + } +} + +func BenchmarkDecapsulate(b *testing.B) { + allSchemes := schemes.All() + for _, scheme := range allSchemes { + scheme := scheme + pk, sk, _ := scheme.GenerateKeyPair() + ct, _, _ := scheme.Encapsulate(pk) + b.Run(scheme.Name(), func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _ = scheme.Decapsulate(sk, ct) + } + }) + } +} + +func TestApi(t *testing.T) { + allSchemes := schemes.All() + for _, scheme := range allSchemes { + scheme := scheme + t.Run(scheme.Name(), func(t *testing.T) { + if scheme == nil { + t.Fatal() + } + + pk, sk, err := scheme.GenerateKeyPair() + if err != nil { + t.Fatal() + } + + packedPk, err := pk.MarshalBinary() + if err != nil { + t.Fatal() + } + + if len(packedPk) != scheme.PublicKeySize() { + t.Fatal() + } + + packedSk, err := sk.MarshalBinary() + if err != nil { + t.Fatal() + } + + if len(packedSk) != scheme.PrivateKeySize() { + t.Fatal() + } + + pk2, err := scheme.UnmarshalBinaryPublicKey(packedPk) + if err != nil { + t.Fatal() + } + + sk2, err := scheme.UnmarshalBinaryPrivateKey(packedSk) + if err != nil { + t.Fatal() + } + + if !sk.Equal(sk2) { + t.Fatal() + } + + if !pk.Equal(pk2) { + t.Fatal() + } + + ct, ss, err := scheme.Encapsulate(pk2) + if err != nil { + t.Fatal(err) + } + if len(ct) != scheme.CiphertextSize() { + t.Fatal() + } + if len(ss) != scheme.SharedKeySize() { + t.Fatal() + } + + ct3, ss3, err := scheme.Encapsulate(pk2) + if err != nil { + t.Fatal(err) + } + if bytes.Equal(ss3, ss) { + t.Fatal() + } + if bytes.Equal(ct3, ct) { + t.Fatal() + } + + ss2, err := scheme.Decapsulate(sk2, ct) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(ss, ss2) { + t.Fatal() + } + }) + } +} + +func Example_schemes() { + // import "github.com/cloudflare/circl/pke/ntruprime/kem/schemes/sntrup" + + for _, sch := range schemes.All() { + fmt.Println(sch.Name()) + } + // Output: + // sntrup653 + // sntrup761 + // sntrup857 + // sntrup953 + // sntrup1013 + // sntrup1277 +} From 454239d3fd47bdcc049839d0788049690cfb1181 Mon Sep 17 00:00:00 2001 From: Keelan10 Date: Tue, 20 Dec 2022 17:35:41 +0400 Subject: [PATCH 3/5] Format files and add further optimization --- kem/ntruprime/doc.go | 2 + kem/ntruprime/gen.go | 2 +- kem/ntruprime/internal/Decode.go | 15 ++-- kem/ntruprime/internal/Divmod.go | 46 +++++----- kem/ntruprime/internal/Encode.go | 3 +- kem/ntruprime/kat_test.go | 1 - kem/ntruprime/ntrulpr1013/ntruprime.go | 29 +++--- kem/ntruprime/ntrulpr1277/ntruprime.go | 29 +++--- kem/ntruprime/ntrulpr653/ntruprime.go | 29 +++--- kem/ntruprime/ntrulpr761/ntruprime.go | 29 +++--- kem/ntruprime/ntrulpr857/ntruprime.go | 29 +++--- kem/ntruprime/ntrulpr953/ntruprime.go | 29 +++--- kem/ntruprime/sntrup1013/ntruprime.go | 64 ++++++-------- kem/ntruprime/sntrup1277/ntruprime.go | 64 ++++++-------- kem/ntruprime/sntrup653/ntruprime.go | 64 ++++++-------- kem/ntruprime/sntrup761/ntruprime.go | 64 ++++++-------- kem/ntruprime/sntrup857/ntruprime.go | 64 ++++++-------- kem/ntruprime/sntrup953/ntruprime.go | 64 ++++++-------- kem/ntruprime/templates/ntrulpr.templ.go | 45 +++++----- kem/ntruprime/templates/sntrup.templ.go | 88 +++++++++---------- kem/schemes/schemes_test.go | 1 - pke/ntruprime/gen.go | 38 ++++---- .../templates/ntrulpr.params.templ.go | 2 +- .../templates/sntrup.params.templ.go | 4 +- 24 files changed, 355 insertions(+), 450 deletions(-) diff --git a/kem/ntruprime/doc.go b/kem/ntruprime/doc.go index 2eb57bbe3..b6f770b32 100644 --- a/kem/ntruprime/doc.go +++ b/kem/ntruprime/doc.go @@ -5,4 +5,6 @@ // competition and described in // // https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf +// +// The code is translated from the C reference implementation. package ntruprime diff --git a/kem/ntruprime/gen.go b/kem/ntruprime/gen.go index 20277ab08..24f863af4 100644 --- a/kem/ntruprime/gen.go +++ b/kem/ntruprime/gen.go @@ -131,7 +131,7 @@ func generateKAT() { if offset == -1 { panic("Missing template warning in pkg.templ.go") } - err = ioutil.WriteFile(mode.Pkg()+"/kat_test.go", []byte(res[offset:]), 0o644) + err = ioutil.WriteFile(mode.Pkg()+"/kat_test.go", []byte(res[offset:]), 0o600) if err != nil { panic(err) } diff --git a/kem/ntruprime/internal/Decode.go b/kem/ntruprime/internal/Decode.go index 38721af9d..770112526 100644 --- a/kem/ntruprime/internal/Decode.go +++ b/kem/ntruprime/internal/Decode.go @@ -1,5 +1,6 @@ package internal +// TO DO: Optimize the Decode function /* Decode(R,s,M,len) */ /* assumes 0 < M[i] < 16384 */ /* produces 0 <= R[i] < M[i] */ @@ -9,9 +10,9 @@ func Decode(out []uint16, S []uint8, M []uint16, len int) { if M[0] == 1 { out[index] = 0 } else if M[0] <= 256 { - out[index] = Uint32_mod_uint14(uint32(S[0]), M[0]) + out[index] = Uint32ModUint14(uint32(S[0]), M[0]) } else { - out[index] = Uint32_mod_uint14(uint32(uint16(S[0])+((uint16(S[1]))<<8)), M[0]) + out[index] = Uint32ModUint14(uint32(uint16(S[0])+((uint16(S[1]))<<8)), M[0]) } } if len > 1 { @@ -33,7 +34,6 @@ func Decode(out []uint16, S []uint8, M []uint16, len int) { bottomr[i/2] = uint16(S[0]) S = S[1:] M2[i/2] = uint16((m + 255) >> 8) - } else { bottomt[i/2] = 1 bottomr[i/2] = 0 @@ -47,24 +47,21 @@ func Decode(out []uint16, S []uint8, M []uint16, len int) { Decode(R2, S, M2, (len+1)/2) for i = 0; i < len-1; i += 2 { - var r uint32 = uint32(bottomr[i/2]) + r := uint32(bottomr[i/2]) var r1 uint32 var r0 uint16 r += bottomt[i/2] * uint32(R2[i/2]) - Uint32_divmod_uint14(&r1, &r0, r, M[i]) - r1 = uint32(Uint32_mod_uint14(r1, M[i+1])) /* only needed for invalid inputs */ + Uint32DivmodUint14(&r1, &r0, r, M[i]) + r1 = uint32(Uint32ModUint14(r1, M[i+1])) /* only needed for invalid inputs */ out[index] = r0 index++ out[index] = uint16(r1) index++ - } if i < len { out[index] = R2[i/2] - index++ } - } } diff --git a/kem/ntruprime/internal/Divmod.go b/kem/ntruprime/internal/Divmod.go index c0622c417..8990c599c 100644 --- a/kem/ntruprime/internal/Divmod.go +++ b/kem/ntruprime/internal/Divmod.go @@ -10,14 +10,14 @@ There could also be compiler issues. */ // q, r = x/m // Returns quotient and remainder -func Uint32_divmod_uint14(q *uint32, r *uint16, x uint32, m uint16) { +func Uint32DivmodUint14(q *uint32, r *uint16, x uint32, m uint16) { var v uint32 = 0x80000000 v /= uint32(m) *q = 0 - var qpart uint32 = uint32(uint64(x) * uint64(v) >> 31) + qpart := uint32(uint64(x) * uint64(v) >> 31) x -= qpart * uint32(m) *q += qpart @@ -28,38 +28,37 @@ func Uint32_divmod_uint14(q *uint32, r *uint16, x uint32, m uint16) { x -= uint32(m) *q += 1 - var mask uint32 = -(x >> 31) + mask := -(x >> 31) x += mask & uint32(m) *q += mask *r = uint16(x) - } // Returns the quotient of x/m -func Uint32_div_uint14(x uint32, m uint16) uint32 { +func Uint32DivUint14(x uint32, m uint16) uint32 { var q uint32 var r uint16 - Uint32_divmod_uint14(&q, &r, x, m) + Uint32DivmodUint14(&q, &r, x, m) return q } // Returns the remainder of x/m -func Uint32_mod_uint14(x uint32, m uint16) uint16 { +func Uint32ModUint14(x uint32, m uint16) uint16 { var q uint32 var r uint16 - Uint32_divmod_uint14(&q, &r, x, m) + Uint32DivmodUint14(&q, &r, x, m) return r } // Calculates quotient and remainder -func Int32_divmod_uint14(q *int32, r *uint16, x int32, m uint16) { +func Int32DivmodUint14(q *int32, r *uint16, x int32, m uint16) { var uq, uq2 uint32 var ur, ur2 uint16 var mask uint32 - Uint32_divmod_uint14(&uq, &ur, 0x80000000+uint32(x), m) - Uint32_divmod_uint14(&uq2, &ur2, 0x80000000, m) + Uint32DivmodUint14(&uq, &ur, 0x80000000+uint32(x), m) + Uint32DivmodUint14(&uq2, &ur2, 0x80000000, m) ur -= ur2 uq -= uq2 @@ -71,34 +70,33 @@ func Int32_divmod_uint14(q *int32, r *uint16, x int32, m uint16) { } // Returns quotient of x/m -func Int32_div_uint14(x int32, m uint16) int32 { +func Int32DivUint14(x int32, m uint16) int32 { var q int32 var r uint16 - Int32_divmod_uint14(&q, &r, x, m) + Int32DivmodUint14(&q, &r, x, m) return q } // Returns remainder of x/m -func Int32_mod_uint14(x int32, m uint16) uint16 { +func Int32ModUint14(x int32, m uint16) uint16 { var q int32 var r uint16 - Int32_divmod_uint14(&q, &r, x, m) + Int32DivmodUint14(&q, &r, x, m) return r - } // Returns -1 if x!=0; else return 0 -func Int16_nonzero_mask(x int16) int { - var u uint16 = uint16(x) /* 0, else 1...65535 */ - var v uint32 = uint32(u) /* 0, else 1...65535 */ - v = -v /* 0, else 2^32-65535...2^32-1 */ - v >>= 31 /* 0, else 1 */ - return -int(v) /* 0, else -1 */ +func Int16NonzeroMask(x int16) int { + u := uint16(x) /* 0, else 1...65535 */ + v := uint32(u) /* 0, else 1...65535 */ + v = -v /* 0, else 2^32-65535...2^32-1 */ + v >>= 31 /* 0, else 1 */ + return -int(v) /* 0, else -1 */ } // Returns -1 if x<0; otherwise return 0 -func Int16_negative_mask(x int16) int { - var u uint16 = uint16(x) +func Int16NegativeMask(x int16) int { + u := uint16(x) u >>= 15 return -(int)(u) } diff --git a/kem/ntruprime/internal/Encode.go b/kem/ntruprime/internal/Encode.go index 68257455e..192503bda 100644 --- a/kem/ntruprime/internal/Encode.go +++ b/kem/ntruprime/internal/Encode.go @@ -1,5 +1,6 @@ package internal +// TO DO: Optimize the Encode function /* 0 <= R[i] < M[i] < 16384 */ func Encode(out []uint8, R []uint16, M []uint16, len int) { if len == 1 { @@ -15,7 +16,7 @@ func Encode(out []uint8, R []uint16, M []uint16, len int) { if len > 1 { R2 := make([]uint16, (len+1)/2) M2 := make([]uint16, (len+1)/2) - var i int = 0 + var i int for i = 0; i < len-1; i += 2 { m0 := uint32(M[i]) diff --git a/kem/ntruprime/kat_test.go b/kem/ntruprime/kat_test.go index a9e9b755d..1cb07c66f 100644 --- a/kem/ntruprime/kat_test.go +++ b/kem/ntruprime/kat_test.go @@ -158,7 +158,6 @@ func testPQCgenStreamlinedKATKem(t *testing.T, name, expected string, p int) { fmt.Fprintf(f, "sk = %X\n", psk) fmt.Fprintf(f, "ct = %X\n", ct) fmt.Fprintf(f, "ss = %X\n\n", ss1) - } if fmt.Sprintf("%x", f.Sum(nil)) != expected { t.Fatal() diff --git a/kem/ntruprime/ntrulpr1013/ntruprime.go b/kem/ntruprime/ntrulpr1013/ntruprime.go index a847cb7c4..fc7566330 100644 --- a/kem/ntruprime/ntrulpr1013/ntruprime.go +++ b/kem/ntruprime/ntrulpr1013/ntruprime.go @@ -82,7 +82,7 @@ type ( // x must not be close to top int16 func f3Freeze(x int16) small { - return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 + return small(internal.Int32ModUint14(int32(x)+1, 3)) - 1 } /* ----- arithmetic mod q */ @@ -94,7 +94,7 @@ func f3Freeze(x int16) small { /* x must not be close to top int32 */ func fqFreeze(x int32) Fq { - return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) + return Fq(internal.Int32ModUint14(x+q12, q) - q12) } func top(C Fq) int8 { @@ -291,18 +291,15 @@ func encrypt(B []Fq, T []int8, r []int8, G []Fq, A []Fq, b []small) { } // r = decrypt((B,T),a) -func decrypt(B []Fq, T []int8, a []small) []int8 { +func decrypt(r []int8, B []Fq, T []int8, a []small) { aB := make([]Fq, p) - r := make([]int8, I) - rqMultSmall(aB, B, a) for i := 0; i < I; i++ { - r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) + r[i] = int8(-internal.Int16NegativeMask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) } - return r } // Encoding I-bit inputs @@ -351,7 +348,7 @@ func generator(G []Fq, k []byte) { L := make([]uint32, 4*p) expand(L, k) for i := 0; i < p; i++ { - G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) + G[i] = Fq(internal.Uint32ModUint14(L[i], q) - q12) } } @@ -496,24 +493,20 @@ func topEncode(s []byte, T []int8) { } } -func topDecode(s []byte) (T []int8) { - - T = make([]int8, 2*topBytes+1) +func topDecode(T []int8, s []byte) { for i := 0; i < topBytes; i++ { T[2*i] = int8(s[i] & 15) T[2*i+1] = int8(s[i] >> 4) } - return T } // Streamlined NTRU Prime Core plus encoding -func inputsRandom(seed []byte) (r Inputs) { +func inputsRandom(r *Inputs, seed []byte) { for i := 0; i < I; i++ { r[i] = int8(1 & (seed[i>>3] >> (i & 7))) } - return r } // Generates public key and private key @@ -548,11 +541,12 @@ func zEncrypt(c []byte, r Inputs, pk []byte) { func zDecrypt(r *Inputs, c []byte, sk []byte) { a := make([]small, p) B := make([]Fq, p) + T := make([]int8, I) smallDecode(a, sk) roundedDecode(B, c) - T := topDecode(c[roundedBytes:]) - copy(r[:], decrypt(B, T, a)) + topDecode(T, c[roundedBytes:]) + decrypt(r[:], B, T, a) } // Confirmation hash @@ -640,9 +634,10 @@ func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { r_enc := make([]byte, inputsBytes) cache := make([]byte, hashBytes) + var r Inputs hashPrefix(cache, 4, pk.pk[:], publicKeysBytes) - r := inputsRandom(seed) + inputsRandom(&r, seed) hide(c, r_enc, r, pk.pk[:], cache) hashSession(k, 1, r_enc, c) } diff --git a/kem/ntruprime/ntrulpr1277/ntruprime.go b/kem/ntruprime/ntrulpr1277/ntruprime.go index f6ecffaa9..8634ea739 100644 --- a/kem/ntruprime/ntrulpr1277/ntruprime.go +++ b/kem/ntruprime/ntrulpr1277/ntruprime.go @@ -82,7 +82,7 @@ type ( // x must not be close to top int16 func f3Freeze(x int16) small { - return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 + return small(internal.Int32ModUint14(int32(x)+1, 3)) - 1 } /* ----- arithmetic mod q */ @@ -94,7 +94,7 @@ func f3Freeze(x int16) small { /* x must not be close to top int32 */ func fqFreeze(x int32) Fq { - return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) + return Fq(internal.Int32ModUint14(x+q12, q) - q12) } func top(C Fq) int8 { @@ -291,18 +291,15 @@ func encrypt(B []Fq, T []int8, r []int8, G []Fq, A []Fq, b []small) { } // r = decrypt((B,T),a) -func decrypt(B []Fq, T []int8, a []small) []int8 { +func decrypt(r []int8, B []Fq, T []int8, a []small) { aB := make([]Fq, p) - r := make([]int8, I) - rqMultSmall(aB, B, a) for i := 0; i < I; i++ { - r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) + r[i] = int8(-internal.Int16NegativeMask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) } - return r } // Encoding I-bit inputs @@ -351,7 +348,7 @@ func generator(G []Fq, k []byte) { L := make([]uint32, 4*p) expand(L, k) for i := 0; i < p; i++ { - G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) + G[i] = Fq(internal.Uint32ModUint14(L[i], q) - q12) } } @@ -496,24 +493,20 @@ func topEncode(s []byte, T []int8) { } } -func topDecode(s []byte) (T []int8) { - - T = make([]int8, 2*topBytes+1) +func topDecode(T []int8, s []byte) { for i := 0; i < topBytes; i++ { T[2*i] = int8(s[i] & 15) T[2*i+1] = int8(s[i] >> 4) } - return T } // Streamlined NTRU Prime Core plus encoding -func inputsRandom(seed []byte) (r Inputs) { +func inputsRandom(r *Inputs, seed []byte) { for i := 0; i < I; i++ { r[i] = int8(1 & (seed[i>>3] >> (i & 7))) } - return r } // Generates public key and private key @@ -548,11 +541,12 @@ func zEncrypt(c []byte, r Inputs, pk []byte) { func zDecrypt(r *Inputs, c []byte, sk []byte) { a := make([]small, p) B := make([]Fq, p) + T := make([]int8, I) smallDecode(a, sk) roundedDecode(B, c) - T := topDecode(c[roundedBytes:]) - copy(r[:], decrypt(B, T, a)) + topDecode(T, c[roundedBytes:]) + decrypt(r[:], B, T, a) } // Confirmation hash @@ -640,9 +634,10 @@ func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { r_enc := make([]byte, inputsBytes) cache := make([]byte, hashBytes) + var r Inputs hashPrefix(cache, 4, pk.pk[:], publicKeysBytes) - r := inputsRandom(seed) + inputsRandom(&r, seed) hide(c, r_enc, r, pk.pk[:], cache) hashSession(k, 1, r_enc, c) } diff --git a/kem/ntruprime/ntrulpr653/ntruprime.go b/kem/ntruprime/ntrulpr653/ntruprime.go index 896f6c47f..758f14313 100644 --- a/kem/ntruprime/ntrulpr653/ntruprime.go +++ b/kem/ntruprime/ntrulpr653/ntruprime.go @@ -82,7 +82,7 @@ type ( // x must not be close to top int16 func f3Freeze(x int16) small { - return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 + return small(internal.Int32ModUint14(int32(x)+1, 3)) - 1 } /* ----- arithmetic mod q */ @@ -94,7 +94,7 @@ func f3Freeze(x int16) small { /* x must not be close to top int32 */ func fqFreeze(x int32) Fq { - return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) + return Fq(internal.Int32ModUint14(x+q12, q) - q12) } func top(C Fq) int8 { @@ -291,18 +291,15 @@ func encrypt(B []Fq, T []int8, r []int8, G []Fq, A []Fq, b []small) { } // r = decrypt((B,T),a) -func decrypt(B []Fq, T []int8, a []small) []int8 { +func decrypt(r []int8, B []Fq, T []int8, a []small) { aB := make([]Fq, p) - r := make([]int8, I) - rqMultSmall(aB, B, a) for i := 0; i < I; i++ { - r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) + r[i] = int8(-internal.Int16NegativeMask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) } - return r } // Encoding I-bit inputs @@ -351,7 +348,7 @@ func generator(G []Fq, k []byte) { L := make([]uint32, 4*p) expand(L, k) for i := 0; i < p; i++ { - G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) + G[i] = Fq(internal.Uint32ModUint14(L[i], q) - q12) } } @@ -496,24 +493,20 @@ func topEncode(s []byte, T []int8) { } } -func topDecode(s []byte) (T []int8) { - - T = make([]int8, 2*topBytes+1) +func topDecode(T []int8, s []byte) { for i := 0; i < topBytes; i++ { T[2*i] = int8(s[i] & 15) T[2*i+1] = int8(s[i] >> 4) } - return T } // Streamlined NTRU Prime Core plus encoding -func inputsRandom(seed []byte) (r Inputs) { +func inputsRandom(r *Inputs, seed []byte) { for i := 0; i < I; i++ { r[i] = int8(1 & (seed[i>>3] >> (i & 7))) } - return r } // Generates public key and private key @@ -548,11 +541,12 @@ func zEncrypt(c []byte, r Inputs, pk []byte) { func zDecrypt(r *Inputs, c []byte, sk []byte) { a := make([]small, p) B := make([]Fq, p) + T := make([]int8, I) smallDecode(a, sk) roundedDecode(B, c) - T := topDecode(c[roundedBytes:]) - copy(r[:], decrypt(B, T, a)) + topDecode(T, c[roundedBytes:]) + decrypt(r[:], B, T, a) } // Confirmation hash @@ -640,9 +634,10 @@ func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { r_enc := make([]byte, inputsBytes) cache := make([]byte, hashBytes) + var r Inputs hashPrefix(cache, 4, pk.pk[:], publicKeysBytes) - r := inputsRandom(seed) + inputsRandom(&r, seed) hide(c, r_enc, r, pk.pk[:], cache) hashSession(k, 1, r_enc, c) } diff --git a/kem/ntruprime/ntrulpr761/ntruprime.go b/kem/ntruprime/ntrulpr761/ntruprime.go index 514c1a3f3..214afc58b 100644 --- a/kem/ntruprime/ntrulpr761/ntruprime.go +++ b/kem/ntruprime/ntrulpr761/ntruprime.go @@ -82,7 +82,7 @@ type ( // x must not be close to top int16 func f3Freeze(x int16) small { - return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 + return small(internal.Int32ModUint14(int32(x)+1, 3)) - 1 } /* ----- arithmetic mod q */ @@ -94,7 +94,7 @@ func f3Freeze(x int16) small { /* x must not be close to top int32 */ func fqFreeze(x int32) Fq { - return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) + return Fq(internal.Int32ModUint14(x+q12, q) - q12) } func top(C Fq) int8 { @@ -291,18 +291,15 @@ func encrypt(B []Fq, T []int8, r []int8, G []Fq, A []Fq, b []small) { } // r = decrypt((B,T),a) -func decrypt(B []Fq, T []int8, a []small) []int8 { +func decrypt(r []int8, B []Fq, T []int8, a []small) { aB := make([]Fq, p) - r := make([]int8, I) - rqMultSmall(aB, B, a) for i := 0; i < I; i++ { - r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) + r[i] = int8(-internal.Int16NegativeMask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) } - return r } // Encoding I-bit inputs @@ -351,7 +348,7 @@ func generator(G []Fq, k []byte) { L := make([]uint32, 4*p) expand(L, k) for i := 0; i < p; i++ { - G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) + G[i] = Fq(internal.Uint32ModUint14(L[i], q) - q12) } } @@ -496,24 +493,20 @@ func topEncode(s []byte, T []int8) { } } -func topDecode(s []byte) (T []int8) { - - T = make([]int8, 2*topBytes+1) +func topDecode(T []int8, s []byte) { for i := 0; i < topBytes; i++ { T[2*i] = int8(s[i] & 15) T[2*i+1] = int8(s[i] >> 4) } - return T } // Streamlined NTRU Prime Core plus encoding -func inputsRandom(seed []byte) (r Inputs) { +func inputsRandom(r *Inputs, seed []byte) { for i := 0; i < I; i++ { r[i] = int8(1 & (seed[i>>3] >> (i & 7))) } - return r } // Generates public key and private key @@ -548,11 +541,12 @@ func zEncrypt(c []byte, r Inputs, pk []byte) { func zDecrypt(r *Inputs, c []byte, sk []byte) { a := make([]small, p) B := make([]Fq, p) + T := make([]int8, I) smallDecode(a, sk) roundedDecode(B, c) - T := topDecode(c[roundedBytes:]) - copy(r[:], decrypt(B, T, a)) + topDecode(T, c[roundedBytes:]) + decrypt(r[:], B, T, a) } // Confirmation hash @@ -640,9 +634,10 @@ func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { r_enc := make([]byte, inputsBytes) cache := make([]byte, hashBytes) + var r Inputs hashPrefix(cache, 4, pk.pk[:], publicKeysBytes) - r := inputsRandom(seed) + inputsRandom(&r, seed) hide(c, r_enc, r, pk.pk[:], cache) hashSession(k, 1, r_enc, c) } diff --git a/kem/ntruprime/ntrulpr857/ntruprime.go b/kem/ntruprime/ntrulpr857/ntruprime.go index 68d47c3c5..d3f1543b5 100644 --- a/kem/ntruprime/ntrulpr857/ntruprime.go +++ b/kem/ntruprime/ntrulpr857/ntruprime.go @@ -82,7 +82,7 @@ type ( // x must not be close to top int16 func f3Freeze(x int16) small { - return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 + return small(internal.Int32ModUint14(int32(x)+1, 3)) - 1 } /* ----- arithmetic mod q */ @@ -94,7 +94,7 @@ func f3Freeze(x int16) small { /* x must not be close to top int32 */ func fqFreeze(x int32) Fq { - return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) + return Fq(internal.Int32ModUint14(x+q12, q) - q12) } func top(C Fq) int8 { @@ -291,18 +291,15 @@ func encrypt(B []Fq, T []int8, r []int8, G []Fq, A []Fq, b []small) { } // r = decrypt((B,T),a) -func decrypt(B []Fq, T []int8, a []small) []int8 { +func decrypt(r []int8, B []Fq, T []int8, a []small) { aB := make([]Fq, p) - r := make([]int8, I) - rqMultSmall(aB, B, a) for i := 0; i < I; i++ { - r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) + r[i] = int8(-internal.Int16NegativeMask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) } - return r } // Encoding I-bit inputs @@ -351,7 +348,7 @@ func generator(G []Fq, k []byte) { L := make([]uint32, 4*p) expand(L, k) for i := 0; i < p; i++ { - G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) + G[i] = Fq(internal.Uint32ModUint14(L[i], q) - q12) } } @@ -496,24 +493,20 @@ func topEncode(s []byte, T []int8) { } } -func topDecode(s []byte) (T []int8) { - - T = make([]int8, 2*topBytes+1) +func topDecode(T []int8, s []byte) { for i := 0; i < topBytes; i++ { T[2*i] = int8(s[i] & 15) T[2*i+1] = int8(s[i] >> 4) } - return T } // Streamlined NTRU Prime Core plus encoding -func inputsRandom(seed []byte) (r Inputs) { +func inputsRandom(r *Inputs, seed []byte) { for i := 0; i < I; i++ { r[i] = int8(1 & (seed[i>>3] >> (i & 7))) } - return r } // Generates public key and private key @@ -548,11 +541,12 @@ func zEncrypt(c []byte, r Inputs, pk []byte) { func zDecrypt(r *Inputs, c []byte, sk []byte) { a := make([]small, p) B := make([]Fq, p) + T := make([]int8, I) smallDecode(a, sk) roundedDecode(B, c) - T := topDecode(c[roundedBytes:]) - copy(r[:], decrypt(B, T, a)) + topDecode(T, c[roundedBytes:]) + decrypt(r[:], B, T, a) } // Confirmation hash @@ -640,9 +634,10 @@ func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { r_enc := make([]byte, inputsBytes) cache := make([]byte, hashBytes) + var r Inputs hashPrefix(cache, 4, pk.pk[:], publicKeysBytes) - r := inputsRandom(seed) + inputsRandom(&r, seed) hide(c, r_enc, r, pk.pk[:], cache) hashSession(k, 1, r_enc, c) } diff --git a/kem/ntruprime/ntrulpr953/ntruprime.go b/kem/ntruprime/ntrulpr953/ntruprime.go index b11bdcb76..6f56710bd 100644 --- a/kem/ntruprime/ntrulpr953/ntruprime.go +++ b/kem/ntruprime/ntrulpr953/ntruprime.go @@ -82,7 +82,7 @@ type ( // x must not be close to top int16 func f3Freeze(x int16) small { - return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 + return small(internal.Int32ModUint14(int32(x)+1, 3)) - 1 } /* ----- arithmetic mod q */ @@ -94,7 +94,7 @@ func f3Freeze(x int16) small { /* x must not be close to top int32 */ func fqFreeze(x int32) Fq { - return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) + return Fq(internal.Int32ModUint14(x+q12, q) - q12) } func top(C Fq) int8 { @@ -291,18 +291,15 @@ func encrypt(B []Fq, T []int8, r []int8, G []Fq, A []Fq, b []small) { } // r = decrypt((B,T),a) -func decrypt(B []Fq, T []int8, a []small) []int8 { +func decrypt(r []int8, B []Fq, T []int8, a []small) { aB := make([]Fq, p) - r := make([]int8, I) - rqMultSmall(aB, B, a) for i := 0; i < I; i++ { - r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) + r[i] = int8(-internal.Int16NegativeMask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) } - return r } // Encoding I-bit inputs @@ -351,7 +348,7 @@ func generator(G []Fq, k []byte) { L := make([]uint32, 4*p) expand(L, k) for i := 0; i < p; i++ { - G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) + G[i] = Fq(internal.Uint32ModUint14(L[i], q) - q12) } } @@ -496,24 +493,20 @@ func topEncode(s []byte, T []int8) { } } -func topDecode(s []byte) (T []int8) { - - T = make([]int8, 2*topBytes+1) +func topDecode(T []int8, s []byte) { for i := 0; i < topBytes; i++ { T[2*i] = int8(s[i] & 15) T[2*i+1] = int8(s[i] >> 4) } - return T } // Streamlined NTRU Prime Core plus encoding -func inputsRandom(seed []byte) (r Inputs) { +func inputsRandom(r *Inputs, seed []byte) { for i := 0; i < I; i++ { r[i] = int8(1 & (seed[i>>3] >> (i & 7))) } - return r } // Generates public key and private key @@ -548,11 +541,12 @@ func zEncrypt(c []byte, r Inputs, pk []byte) { func zDecrypt(r *Inputs, c []byte, sk []byte) { a := make([]small, p) B := make([]Fq, p) + T := make([]int8, I) smallDecode(a, sk) roundedDecode(B, c) - T := topDecode(c[roundedBytes:]) - copy(r[:], decrypt(B, T, a)) + topDecode(T, c[roundedBytes:]) + decrypt(r[:], B, T, a) } // Confirmation hash @@ -640,9 +634,10 @@ func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { r_enc := make([]byte, inputsBytes) cache := make([]byte, hashBytes) + var r Inputs hashPrefix(cache, 4, pk.pk[:], publicKeysBytes) - r := inputsRandom(seed) + inputsRandom(&r, seed) hide(c, r_enc, r, pk.pk[:], cache) hashSession(k, 1, r_enc, c) } diff --git a/kem/ntruprime/sntrup1013/ntruprime.go b/kem/ntruprime/sntrup1013/ntruprime.go index df0be85e4..680c754db 100644 --- a/kem/ntruprime/sntrup1013/ntruprime.go +++ b/kem/ntruprime/sntrup1013/ntruprime.go @@ -69,7 +69,7 @@ const ( // x must not be close to top int16 func f3Freeze(x int16) small { - return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 + return small(internal.Int32ModUint14(int32(x)+1, 3)) - 1 } // Arithmetic operations over GF(q) @@ -79,7 +79,7 @@ func f3Freeze(x int16) small { /* x must not be close to top int32 */ func fqFreeze(x int32) Fq { - return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) + return Fq(internal.Int32ModUint14(x+q12, q) - q12) } // Calculates reciprocal of Fq @@ -105,22 +105,19 @@ func weightwMask(r []small) int { // returns -1 if non zero // otherwise returns 0 if weight==w - return internal.Int16_nonzero_mask(int16(weight - w)) + return internal.Int16NonzeroMask(int16(weight - w)) } /* R3_fromR(R_fromRq(r)) */ -func r3FromRq(r []Fq) []small { - out := make([]small, p) +func r3FromRq(out []small, r []Fq) { for i := 0; i < p; i++ { out[i] = small(f3Freeze(int16(r[i]))) } - return out } // h = f*g in the ring R3 -func r3Mult(f []small, g []small) (h []small) { - h = make([]small, p) +func r3Mult(h []small, f []small, g []small) { fg := make([]small, p+p-1) var result small var i, j int @@ -149,9 +146,6 @@ func r3Mult(f []small, g []small) (h []small) { for i = 0; i < p; i++ { h[i] = fg[i] } - - return h - } // Calculates the reciprocal of R3 polynomials @@ -186,7 +180,7 @@ func r3Recip(out []small, in []small) int { v[0] = 0 sign = int(-g[0] * f[0]) - var swap int = int(internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0]))) + var swap int = int(internal.Int16NegativeMask(int16(-delta)) & internal.Int16NonzeroMask(int16(g[0]))) delta ^= swap & int(delta^-delta) delta += 1 @@ -220,7 +214,7 @@ func r3Recip(out []small, in []small) int { out[i] = small(sign * int(v[p-1-i])) } - return internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16NonzeroMask(int16(delta)) } @@ -259,18 +253,15 @@ func rqMultSmall(h []Fq, f []Fq, g []small) { } // h = 3f in Rq -func rqMult3(f []Fq) (h []Fq) { - h = make([]Fq, p) +func rqMult3(h []Fq, f []Fq) { for i := 0; i < p; i++ { h[i] = fqFreeze(int32(3 * f[i])) } - return h } // Returns 0 if recip succeeded; else -1 // out = 1/(3*in) in Rq -func rqRecip3(in []small) ([]Fq, int) { - out := make([]Fq, p) +func rqRecip3(out []Fq, in []small) int { f := make([]Fq, p+1) g := make([]Fq, p+1) v := make([]Fq, p+1) @@ -297,7 +288,7 @@ func rqRecip3(in []small) ([]Fq, int) { } v[0] = 0 - swap = internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0])) + swap = internal.Int16NegativeMask(int16(-delta)) & internal.Int16NonzeroMask(int16(g[0])) delta ^= swap & (delta ^ -delta) delta += 1 @@ -331,18 +322,16 @@ func rqRecip3(in []small) ([]Fq, int) { out[i] = fqFreeze(int32(scale) * (int32)(v[p-1-i])) } - return out, internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16NonzeroMask(int16(delta)) } // Rounding all coefficients of a polynomial to the nearest multiple of 3 // Rounded polynomials mod q -func round(a []Fq) []Fq { - out := make([]Fq, p) +func round(out []Fq, a []Fq) { for i := 0; i < p; i++ { out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) } - return out } // Returns (min(x, y), max(x, y)), executes in constant time @@ -496,27 +485,31 @@ func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { } shortRandom(f, seed[4*p:]) - finv, _ := rqRecip3(f) /* always works */ + finv := make([]Fq, p) + + rqRecip3(finv, f) /* always works */ rqMultSmall(h, finv, g) } // c = encrypt(r,h) -func encrypt(r []small, h []Fq) []Fq { +func encrypt(c []Fq, r []small, h []Fq) { hr := make([]Fq, p) + rqMultSmall(hr, h, r) - c := round(hr) - return c + round(c, hr) } // r = decrypt(c,(f,ginv)) -func decrypt(c []Fq, f []small, ginv []small) []small { - r := make([]small, p) +func decrypt(r []small, c []Fq, f []small, ginv []small) { cf := make([]Fq, p) + cf3 := make([]Fq, p) + e := make([]small, p) + ev := make([]small, p) rqMultSmall(cf, c, f) - cf3 := rqMult3(cf) - e := r3FromRq(cf3) - ev := r3Mult(e, ginv) + rqMult3(cf3, cf) + r3FromRq(e, cf3) + r3Mult(ev, e, ginv) mask := weightwMask(ev) /* 0 if weight w, else -1 */ for i := 0; i < w; i++ { @@ -526,8 +519,6 @@ func decrypt(c []Fq, f []small, ginv []small) []small { for i := w; i < p; i++ { r[i] = ev[i] & small(^mask) } - return r - } // Encoding small polynomials (including short polynomials) @@ -661,8 +652,9 @@ func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { // C = zEncrypt(r,pk) func zEncrypt(C []byte, r Inputs, pk []byte) { h := make([]Fq, p) + c := make([]Fq, p) rqDecode(h, pk) - c := encrypt(r[:], h) + encrypt(c, r[:], h) roundedEncode(C, c) } @@ -677,7 +669,7 @@ func zDecrypt(r *Inputs, C []byte, sk []byte) { smallDecode(v, sk) roundedDecode(c, C) - copy(r[:], decrypt(c, f, v)) + decrypt(r[:], c, f, v) } // Confirmation hash diff --git a/kem/ntruprime/sntrup1277/ntruprime.go b/kem/ntruprime/sntrup1277/ntruprime.go index b40549562..ab6e4b74f 100644 --- a/kem/ntruprime/sntrup1277/ntruprime.go +++ b/kem/ntruprime/sntrup1277/ntruprime.go @@ -69,7 +69,7 @@ const ( // x must not be close to top int16 func f3Freeze(x int16) small { - return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 + return small(internal.Int32ModUint14(int32(x)+1, 3)) - 1 } // Arithmetic operations over GF(q) @@ -79,7 +79,7 @@ func f3Freeze(x int16) small { /* x must not be close to top int32 */ func fqFreeze(x int32) Fq { - return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) + return Fq(internal.Int32ModUint14(x+q12, q) - q12) } // Calculates reciprocal of Fq @@ -105,22 +105,19 @@ func weightwMask(r []small) int { // returns -1 if non zero // otherwise returns 0 if weight==w - return internal.Int16_nonzero_mask(int16(weight - w)) + return internal.Int16NonzeroMask(int16(weight - w)) } /* R3_fromR(R_fromRq(r)) */ -func r3FromRq(r []Fq) []small { - out := make([]small, p) +func r3FromRq(out []small, r []Fq) { for i := 0; i < p; i++ { out[i] = small(f3Freeze(int16(r[i]))) } - return out } // h = f*g in the ring R3 -func r3Mult(f []small, g []small) (h []small) { - h = make([]small, p) +func r3Mult(h []small, f []small, g []small) { fg := make([]small, p+p-1) var result small var i, j int @@ -149,9 +146,6 @@ func r3Mult(f []small, g []small) (h []small) { for i = 0; i < p; i++ { h[i] = fg[i] } - - return h - } // Calculates the reciprocal of R3 polynomials @@ -186,7 +180,7 @@ func r3Recip(out []small, in []small) int { v[0] = 0 sign = int(-g[0] * f[0]) - var swap int = int(internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0]))) + var swap int = int(internal.Int16NegativeMask(int16(-delta)) & internal.Int16NonzeroMask(int16(g[0]))) delta ^= swap & int(delta^-delta) delta += 1 @@ -220,7 +214,7 @@ func r3Recip(out []small, in []small) int { out[i] = small(sign * int(v[p-1-i])) } - return internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16NonzeroMask(int16(delta)) } @@ -259,18 +253,15 @@ func rqMultSmall(h []Fq, f []Fq, g []small) { } // h = 3f in Rq -func rqMult3(f []Fq) (h []Fq) { - h = make([]Fq, p) +func rqMult3(h []Fq, f []Fq) { for i := 0; i < p; i++ { h[i] = fqFreeze(int32(3 * f[i])) } - return h } // Returns 0 if recip succeeded; else -1 // out = 1/(3*in) in Rq -func rqRecip3(in []small) ([]Fq, int) { - out := make([]Fq, p) +func rqRecip3(out []Fq, in []small) int { f := make([]Fq, p+1) g := make([]Fq, p+1) v := make([]Fq, p+1) @@ -297,7 +288,7 @@ func rqRecip3(in []small) ([]Fq, int) { } v[0] = 0 - swap = internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0])) + swap = internal.Int16NegativeMask(int16(-delta)) & internal.Int16NonzeroMask(int16(g[0])) delta ^= swap & (delta ^ -delta) delta += 1 @@ -331,18 +322,16 @@ func rqRecip3(in []small) ([]Fq, int) { out[i] = fqFreeze(int32(scale) * (int32)(v[p-1-i])) } - return out, internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16NonzeroMask(int16(delta)) } // Rounding all coefficients of a polynomial to the nearest multiple of 3 // Rounded polynomials mod q -func round(a []Fq) []Fq { - out := make([]Fq, p) +func round(out []Fq, a []Fq) { for i := 0; i < p; i++ { out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) } - return out } // Returns (min(x, y), max(x, y)), executes in constant time @@ -496,27 +485,31 @@ func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { } shortRandom(f, seed[4*p:]) - finv, _ := rqRecip3(f) /* always works */ + finv := make([]Fq, p) + + rqRecip3(finv, f) /* always works */ rqMultSmall(h, finv, g) } // c = encrypt(r,h) -func encrypt(r []small, h []Fq) []Fq { +func encrypt(c []Fq, r []small, h []Fq) { hr := make([]Fq, p) + rqMultSmall(hr, h, r) - c := round(hr) - return c + round(c, hr) } // r = decrypt(c,(f,ginv)) -func decrypt(c []Fq, f []small, ginv []small) []small { - r := make([]small, p) +func decrypt(r []small, c []Fq, f []small, ginv []small) { cf := make([]Fq, p) + cf3 := make([]Fq, p) + e := make([]small, p) + ev := make([]small, p) rqMultSmall(cf, c, f) - cf3 := rqMult3(cf) - e := r3FromRq(cf3) - ev := r3Mult(e, ginv) + rqMult3(cf3, cf) + r3FromRq(e, cf3) + r3Mult(ev, e, ginv) mask := weightwMask(ev) /* 0 if weight w, else -1 */ for i := 0; i < w; i++ { @@ -526,8 +519,6 @@ func decrypt(c []Fq, f []small, ginv []small) []small { for i := w; i < p; i++ { r[i] = ev[i] & small(^mask) } - return r - } // Encoding small polynomials (including short polynomials) @@ -661,8 +652,9 @@ func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { // C = zEncrypt(r,pk) func zEncrypt(C []byte, r Inputs, pk []byte) { h := make([]Fq, p) + c := make([]Fq, p) rqDecode(h, pk) - c := encrypt(r[:], h) + encrypt(c, r[:], h) roundedEncode(C, c) } @@ -677,7 +669,7 @@ func zDecrypt(r *Inputs, C []byte, sk []byte) { smallDecode(v, sk) roundedDecode(c, C) - copy(r[:], decrypt(c, f, v)) + decrypt(r[:], c, f, v) } // Confirmation hash diff --git a/kem/ntruprime/sntrup653/ntruprime.go b/kem/ntruprime/sntrup653/ntruprime.go index 4e9dea5cc..126367bd4 100644 --- a/kem/ntruprime/sntrup653/ntruprime.go +++ b/kem/ntruprime/sntrup653/ntruprime.go @@ -69,7 +69,7 @@ const ( // x must not be close to top int16 func f3Freeze(x int16) small { - return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 + return small(internal.Int32ModUint14(int32(x)+1, 3)) - 1 } // Arithmetic operations over GF(q) @@ -79,7 +79,7 @@ func f3Freeze(x int16) small { /* x must not be close to top int32 */ func fqFreeze(x int32) Fq { - return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) + return Fq(internal.Int32ModUint14(x+q12, q) - q12) } // Calculates reciprocal of Fq @@ -105,22 +105,19 @@ func weightwMask(r []small) int { // returns -1 if non zero // otherwise returns 0 if weight==w - return internal.Int16_nonzero_mask(int16(weight - w)) + return internal.Int16NonzeroMask(int16(weight - w)) } /* R3_fromR(R_fromRq(r)) */ -func r3FromRq(r []Fq) []small { - out := make([]small, p) +func r3FromRq(out []small, r []Fq) { for i := 0; i < p; i++ { out[i] = small(f3Freeze(int16(r[i]))) } - return out } // h = f*g in the ring R3 -func r3Mult(f []small, g []small) (h []small) { - h = make([]small, p) +func r3Mult(h []small, f []small, g []small) { fg := make([]small, p+p-1) var result small var i, j int @@ -149,9 +146,6 @@ func r3Mult(f []small, g []small) (h []small) { for i = 0; i < p; i++ { h[i] = fg[i] } - - return h - } // Calculates the reciprocal of R3 polynomials @@ -186,7 +180,7 @@ func r3Recip(out []small, in []small) int { v[0] = 0 sign = int(-g[0] * f[0]) - var swap int = int(internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0]))) + var swap int = int(internal.Int16NegativeMask(int16(-delta)) & internal.Int16NonzeroMask(int16(g[0]))) delta ^= swap & int(delta^-delta) delta += 1 @@ -220,7 +214,7 @@ func r3Recip(out []small, in []small) int { out[i] = small(sign * int(v[p-1-i])) } - return internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16NonzeroMask(int16(delta)) } @@ -259,18 +253,15 @@ func rqMultSmall(h []Fq, f []Fq, g []small) { } // h = 3f in Rq -func rqMult3(f []Fq) (h []Fq) { - h = make([]Fq, p) +func rqMult3(h []Fq, f []Fq) { for i := 0; i < p; i++ { h[i] = fqFreeze(int32(3 * f[i])) } - return h } // Returns 0 if recip succeeded; else -1 // out = 1/(3*in) in Rq -func rqRecip3(in []small) ([]Fq, int) { - out := make([]Fq, p) +func rqRecip3(out []Fq, in []small) int { f := make([]Fq, p+1) g := make([]Fq, p+1) v := make([]Fq, p+1) @@ -297,7 +288,7 @@ func rqRecip3(in []small) ([]Fq, int) { } v[0] = 0 - swap = internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0])) + swap = internal.Int16NegativeMask(int16(-delta)) & internal.Int16NonzeroMask(int16(g[0])) delta ^= swap & (delta ^ -delta) delta += 1 @@ -331,18 +322,16 @@ func rqRecip3(in []small) ([]Fq, int) { out[i] = fqFreeze(int32(scale) * (int32)(v[p-1-i])) } - return out, internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16NonzeroMask(int16(delta)) } // Rounding all coefficients of a polynomial to the nearest multiple of 3 // Rounded polynomials mod q -func round(a []Fq) []Fq { - out := make([]Fq, p) +func round(out []Fq, a []Fq) { for i := 0; i < p; i++ { out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) } - return out } // Returns (min(x, y), max(x, y)), executes in constant time @@ -496,27 +485,31 @@ func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { } shortRandom(f, seed[4*p:]) - finv, _ := rqRecip3(f) /* always works */ + finv := make([]Fq, p) + + rqRecip3(finv, f) /* always works */ rqMultSmall(h, finv, g) } // c = encrypt(r,h) -func encrypt(r []small, h []Fq) []Fq { +func encrypt(c []Fq, r []small, h []Fq) { hr := make([]Fq, p) + rqMultSmall(hr, h, r) - c := round(hr) - return c + round(c, hr) } // r = decrypt(c,(f,ginv)) -func decrypt(c []Fq, f []small, ginv []small) []small { - r := make([]small, p) +func decrypt(r []small, c []Fq, f []small, ginv []small) { cf := make([]Fq, p) + cf3 := make([]Fq, p) + e := make([]small, p) + ev := make([]small, p) rqMultSmall(cf, c, f) - cf3 := rqMult3(cf) - e := r3FromRq(cf3) - ev := r3Mult(e, ginv) + rqMult3(cf3, cf) + r3FromRq(e, cf3) + r3Mult(ev, e, ginv) mask := weightwMask(ev) /* 0 if weight w, else -1 */ for i := 0; i < w; i++ { @@ -526,8 +519,6 @@ func decrypt(c []Fq, f []small, ginv []small) []small { for i := w; i < p; i++ { r[i] = ev[i] & small(^mask) } - return r - } // Encoding small polynomials (including short polynomials) @@ -661,8 +652,9 @@ func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { // C = zEncrypt(r,pk) func zEncrypt(C []byte, r Inputs, pk []byte) { h := make([]Fq, p) + c := make([]Fq, p) rqDecode(h, pk) - c := encrypt(r[:], h) + encrypt(c, r[:], h) roundedEncode(C, c) } @@ -677,7 +669,7 @@ func zDecrypt(r *Inputs, C []byte, sk []byte) { smallDecode(v, sk) roundedDecode(c, C) - copy(r[:], decrypt(c, f, v)) + decrypt(r[:], c, f, v) } // Confirmation hash diff --git a/kem/ntruprime/sntrup761/ntruprime.go b/kem/ntruprime/sntrup761/ntruprime.go index 56f83b422..bb7ed200a 100644 --- a/kem/ntruprime/sntrup761/ntruprime.go +++ b/kem/ntruprime/sntrup761/ntruprime.go @@ -69,7 +69,7 @@ const ( // x must not be close to top int16 func f3Freeze(x int16) small { - return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 + return small(internal.Int32ModUint14(int32(x)+1, 3)) - 1 } // Arithmetic operations over GF(q) @@ -79,7 +79,7 @@ func f3Freeze(x int16) small { /* x must not be close to top int32 */ func fqFreeze(x int32) Fq { - return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) + return Fq(internal.Int32ModUint14(x+q12, q) - q12) } // Calculates reciprocal of Fq @@ -105,22 +105,19 @@ func weightwMask(r []small) int { // returns -1 if non zero // otherwise returns 0 if weight==w - return internal.Int16_nonzero_mask(int16(weight - w)) + return internal.Int16NonzeroMask(int16(weight - w)) } /* R3_fromR(R_fromRq(r)) */ -func r3FromRq(r []Fq) []small { - out := make([]small, p) +func r3FromRq(out []small, r []Fq) { for i := 0; i < p; i++ { out[i] = small(f3Freeze(int16(r[i]))) } - return out } // h = f*g in the ring R3 -func r3Mult(f []small, g []small) (h []small) { - h = make([]small, p) +func r3Mult(h []small, f []small, g []small) { fg := make([]small, p+p-1) var result small var i, j int @@ -149,9 +146,6 @@ func r3Mult(f []small, g []small) (h []small) { for i = 0; i < p; i++ { h[i] = fg[i] } - - return h - } // Calculates the reciprocal of R3 polynomials @@ -186,7 +180,7 @@ func r3Recip(out []small, in []small) int { v[0] = 0 sign = int(-g[0] * f[0]) - var swap int = int(internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0]))) + var swap int = int(internal.Int16NegativeMask(int16(-delta)) & internal.Int16NonzeroMask(int16(g[0]))) delta ^= swap & int(delta^-delta) delta += 1 @@ -220,7 +214,7 @@ func r3Recip(out []small, in []small) int { out[i] = small(sign * int(v[p-1-i])) } - return internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16NonzeroMask(int16(delta)) } @@ -259,18 +253,15 @@ func rqMultSmall(h []Fq, f []Fq, g []small) { } // h = 3f in Rq -func rqMult3(f []Fq) (h []Fq) { - h = make([]Fq, p) +func rqMult3(h []Fq, f []Fq) { for i := 0; i < p; i++ { h[i] = fqFreeze(int32(3 * f[i])) } - return h } // Returns 0 if recip succeeded; else -1 // out = 1/(3*in) in Rq -func rqRecip3(in []small) ([]Fq, int) { - out := make([]Fq, p) +func rqRecip3(out []Fq, in []small) int { f := make([]Fq, p+1) g := make([]Fq, p+1) v := make([]Fq, p+1) @@ -297,7 +288,7 @@ func rqRecip3(in []small) ([]Fq, int) { } v[0] = 0 - swap = internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0])) + swap = internal.Int16NegativeMask(int16(-delta)) & internal.Int16NonzeroMask(int16(g[0])) delta ^= swap & (delta ^ -delta) delta += 1 @@ -331,18 +322,16 @@ func rqRecip3(in []small) ([]Fq, int) { out[i] = fqFreeze(int32(scale) * (int32)(v[p-1-i])) } - return out, internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16NonzeroMask(int16(delta)) } // Rounding all coefficients of a polynomial to the nearest multiple of 3 // Rounded polynomials mod q -func round(a []Fq) []Fq { - out := make([]Fq, p) +func round(out []Fq, a []Fq) { for i := 0; i < p; i++ { out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) } - return out } // Returns (min(x, y), max(x, y)), executes in constant time @@ -496,27 +485,31 @@ func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { } shortRandom(f, seed[4*p:]) - finv, _ := rqRecip3(f) /* always works */ + finv := make([]Fq, p) + + rqRecip3(finv, f) /* always works */ rqMultSmall(h, finv, g) } // c = encrypt(r,h) -func encrypt(r []small, h []Fq) []Fq { +func encrypt(c []Fq, r []small, h []Fq) { hr := make([]Fq, p) + rqMultSmall(hr, h, r) - c := round(hr) - return c + round(c, hr) } // r = decrypt(c,(f,ginv)) -func decrypt(c []Fq, f []small, ginv []small) []small { - r := make([]small, p) +func decrypt(r []small, c []Fq, f []small, ginv []small) { cf := make([]Fq, p) + cf3 := make([]Fq, p) + e := make([]small, p) + ev := make([]small, p) rqMultSmall(cf, c, f) - cf3 := rqMult3(cf) - e := r3FromRq(cf3) - ev := r3Mult(e, ginv) + rqMult3(cf3, cf) + r3FromRq(e, cf3) + r3Mult(ev, e, ginv) mask := weightwMask(ev) /* 0 if weight w, else -1 */ for i := 0; i < w; i++ { @@ -526,8 +519,6 @@ func decrypt(c []Fq, f []small, ginv []small) []small { for i := w; i < p; i++ { r[i] = ev[i] & small(^mask) } - return r - } // Encoding small polynomials (including short polynomials) @@ -661,8 +652,9 @@ func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { // C = zEncrypt(r,pk) func zEncrypt(C []byte, r Inputs, pk []byte) { h := make([]Fq, p) + c := make([]Fq, p) rqDecode(h, pk) - c := encrypt(r[:], h) + encrypt(c, r[:], h) roundedEncode(C, c) } @@ -677,7 +669,7 @@ func zDecrypt(r *Inputs, C []byte, sk []byte) { smallDecode(v, sk) roundedDecode(c, C) - copy(r[:], decrypt(c, f, v)) + decrypt(r[:], c, f, v) } // Confirmation hash diff --git a/kem/ntruprime/sntrup857/ntruprime.go b/kem/ntruprime/sntrup857/ntruprime.go index fd3f01af8..8680ddedb 100644 --- a/kem/ntruprime/sntrup857/ntruprime.go +++ b/kem/ntruprime/sntrup857/ntruprime.go @@ -69,7 +69,7 @@ const ( // x must not be close to top int16 func f3Freeze(x int16) small { - return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 + return small(internal.Int32ModUint14(int32(x)+1, 3)) - 1 } // Arithmetic operations over GF(q) @@ -79,7 +79,7 @@ func f3Freeze(x int16) small { /* x must not be close to top int32 */ func fqFreeze(x int32) Fq { - return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) + return Fq(internal.Int32ModUint14(x+q12, q) - q12) } // Calculates reciprocal of Fq @@ -105,22 +105,19 @@ func weightwMask(r []small) int { // returns -1 if non zero // otherwise returns 0 if weight==w - return internal.Int16_nonzero_mask(int16(weight - w)) + return internal.Int16NonzeroMask(int16(weight - w)) } /* R3_fromR(R_fromRq(r)) */ -func r3FromRq(r []Fq) []small { - out := make([]small, p) +func r3FromRq(out []small, r []Fq) { for i := 0; i < p; i++ { out[i] = small(f3Freeze(int16(r[i]))) } - return out } // h = f*g in the ring R3 -func r3Mult(f []small, g []small) (h []small) { - h = make([]small, p) +func r3Mult(h []small, f []small, g []small) { fg := make([]small, p+p-1) var result small var i, j int @@ -149,9 +146,6 @@ func r3Mult(f []small, g []small) (h []small) { for i = 0; i < p; i++ { h[i] = fg[i] } - - return h - } // Calculates the reciprocal of R3 polynomials @@ -186,7 +180,7 @@ func r3Recip(out []small, in []small) int { v[0] = 0 sign = int(-g[0] * f[0]) - var swap int = int(internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0]))) + var swap int = int(internal.Int16NegativeMask(int16(-delta)) & internal.Int16NonzeroMask(int16(g[0]))) delta ^= swap & int(delta^-delta) delta += 1 @@ -220,7 +214,7 @@ func r3Recip(out []small, in []small) int { out[i] = small(sign * int(v[p-1-i])) } - return internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16NonzeroMask(int16(delta)) } @@ -259,18 +253,15 @@ func rqMultSmall(h []Fq, f []Fq, g []small) { } // h = 3f in Rq -func rqMult3(f []Fq) (h []Fq) { - h = make([]Fq, p) +func rqMult3(h []Fq, f []Fq) { for i := 0; i < p; i++ { h[i] = fqFreeze(int32(3 * f[i])) } - return h } // Returns 0 if recip succeeded; else -1 // out = 1/(3*in) in Rq -func rqRecip3(in []small) ([]Fq, int) { - out := make([]Fq, p) +func rqRecip3(out []Fq, in []small) int { f := make([]Fq, p+1) g := make([]Fq, p+1) v := make([]Fq, p+1) @@ -297,7 +288,7 @@ func rqRecip3(in []small) ([]Fq, int) { } v[0] = 0 - swap = internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0])) + swap = internal.Int16NegativeMask(int16(-delta)) & internal.Int16NonzeroMask(int16(g[0])) delta ^= swap & (delta ^ -delta) delta += 1 @@ -331,18 +322,16 @@ func rqRecip3(in []small) ([]Fq, int) { out[i] = fqFreeze(int32(scale) * (int32)(v[p-1-i])) } - return out, internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16NonzeroMask(int16(delta)) } // Rounding all coefficients of a polynomial to the nearest multiple of 3 // Rounded polynomials mod q -func round(a []Fq) []Fq { - out := make([]Fq, p) +func round(out []Fq, a []Fq) { for i := 0; i < p; i++ { out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) } - return out } // Returns (min(x, y), max(x, y)), executes in constant time @@ -496,27 +485,31 @@ func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { } shortRandom(f, seed[4*p:]) - finv, _ := rqRecip3(f) /* always works */ + finv := make([]Fq, p) + + rqRecip3(finv, f) /* always works */ rqMultSmall(h, finv, g) } // c = encrypt(r,h) -func encrypt(r []small, h []Fq) []Fq { +func encrypt(c []Fq, r []small, h []Fq) { hr := make([]Fq, p) + rqMultSmall(hr, h, r) - c := round(hr) - return c + round(c, hr) } // r = decrypt(c,(f,ginv)) -func decrypt(c []Fq, f []small, ginv []small) []small { - r := make([]small, p) +func decrypt(r []small, c []Fq, f []small, ginv []small) { cf := make([]Fq, p) + cf3 := make([]Fq, p) + e := make([]small, p) + ev := make([]small, p) rqMultSmall(cf, c, f) - cf3 := rqMult3(cf) - e := r3FromRq(cf3) - ev := r3Mult(e, ginv) + rqMult3(cf3, cf) + r3FromRq(e, cf3) + r3Mult(ev, e, ginv) mask := weightwMask(ev) /* 0 if weight w, else -1 */ for i := 0; i < w; i++ { @@ -526,8 +519,6 @@ func decrypt(c []Fq, f []small, ginv []small) []small { for i := w; i < p; i++ { r[i] = ev[i] & small(^mask) } - return r - } // Encoding small polynomials (including short polynomials) @@ -661,8 +652,9 @@ func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { // C = zEncrypt(r,pk) func zEncrypt(C []byte, r Inputs, pk []byte) { h := make([]Fq, p) + c := make([]Fq, p) rqDecode(h, pk) - c := encrypt(r[:], h) + encrypt(c, r[:], h) roundedEncode(C, c) } @@ -677,7 +669,7 @@ func zDecrypt(r *Inputs, C []byte, sk []byte) { smallDecode(v, sk) roundedDecode(c, C) - copy(r[:], decrypt(c, f, v)) + decrypt(r[:], c, f, v) } // Confirmation hash diff --git a/kem/ntruprime/sntrup953/ntruprime.go b/kem/ntruprime/sntrup953/ntruprime.go index 5e327cfdd..a7e36df7b 100644 --- a/kem/ntruprime/sntrup953/ntruprime.go +++ b/kem/ntruprime/sntrup953/ntruprime.go @@ -69,7 +69,7 @@ const ( // x must not be close to top int16 func f3Freeze(x int16) small { - return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 + return small(internal.Int32ModUint14(int32(x)+1, 3)) - 1 } // Arithmetic operations over GF(q) @@ -79,7 +79,7 @@ func f3Freeze(x int16) small { /* x must not be close to top int32 */ func fqFreeze(x int32) Fq { - return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) + return Fq(internal.Int32ModUint14(x+q12, q) - q12) } // Calculates reciprocal of Fq @@ -105,22 +105,19 @@ func weightwMask(r []small) int { // returns -1 if non zero // otherwise returns 0 if weight==w - return internal.Int16_nonzero_mask(int16(weight - w)) + return internal.Int16NonzeroMask(int16(weight - w)) } /* R3_fromR(R_fromRq(r)) */ -func r3FromRq(r []Fq) []small { - out := make([]small, p) +func r3FromRq(out []small, r []Fq) { for i := 0; i < p; i++ { out[i] = small(f3Freeze(int16(r[i]))) } - return out } // h = f*g in the ring R3 -func r3Mult(f []small, g []small) (h []small) { - h = make([]small, p) +func r3Mult(h []small, f []small, g []small) { fg := make([]small, p+p-1) var result small var i, j int @@ -149,9 +146,6 @@ func r3Mult(f []small, g []small) (h []small) { for i = 0; i < p; i++ { h[i] = fg[i] } - - return h - } // Calculates the reciprocal of R3 polynomials @@ -186,7 +180,7 @@ func r3Recip(out []small, in []small) int { v[0] = 0 sign = int(-g[0] * f[0]) - var swap int = int(internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0]))) + var swap int = int(internal.Int16NegativeMask(int16(-delta)) & internal.Int16NonzeroMask(int16(g[0]))) delta ^= swap & int(delta^-delta) delta += 1 @@ -220,7 +214,7 @@ func r3Recip(out []small, in []small) int { out[i] = small(sign * int(v[p-1-i])) } - return internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16NonzeroMask(int16(delta)) } @@ -259,18 +253,15 @@ func rqMultSmall(h []Fq, f []Fq, g []small) { } // h = 3f in Rq -func rqMult3(f []Fq) (h []Fq) { - h = make([]Fq, p) +func rqMult3(h []Fq, f []Fq) { for i := 0; i < p; i++ { h[i] = fqFreeze(int32(3 * f[i])) } - return h } // Returns 0 if recip succeeded; else -1 // out = 1/(3*in) in Rq -func rqRecip3(in []small) ([]Fq, int) { - out := make([]Fq, p) +func rqRecip3(out []Fq, in []small) int { f := make([]Fq, p+1) g := make([]Fq, p+1) v := make([]Fq, p+1) @@ -297,7 +288,7 @@ func rqRecip3(in []small) ([]Fq, int) { } v[0] = 0 - swap = internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0])) + swap = internal.Int16NegativeMask(int16(-delta)) & internal.Int16NonzeroMask(int16(g[0])) delta ^= swap & (delta ^ -delta) delta += 1 @@ -331,18 +322,16 @@ func rqRecip3(in []small) ([]Fq, int) { out[i] = fqFreeze(int32(scale) * (int32)(v[p-1-i])) } - return out, internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16NonzeroMask(int16(delta)) } // Rounding all coefficients of a polynomial to the nearest multiple of 3 // Rounded polynomials mod q -func round(a []Fq) []Fq { - out := make([]Fq, p) +func round(out []Fq, a []Fq) { for i := 0; i < p; i++ { out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) } - return out } // Returns (min(x, y), max(x, y)), executes in constant time @@ -496,27 +485,31 @@ func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { } shortRandom(f, seed[4*p:]) - finv, _ := rqRecip3(f) /* always works */ + finv := make([]Fq, p) + + rqRecip3(finv, f) /* always works */ rqMultSmall(h, finv, g) } // c = encrypt(r,h) -func encrypt(r []small, h []Fq) []Fq { +func encrypt(c []Fq, r []small, h []Fq) { hr := make([]Fq, p) + rqMultSmall(hr, h, r) - c := round(hr) - return c + round(c, hr) } // r = decrypt(c,(f,ginv)) -func decrypt(c []Fq, f []small, ginv []small) []small { - r := make([]small, p) +func decrypt(r []small, c []Fq, f []small, ginv []small) { cf := make([]Fq, p) + cf3 := make([]Fq, p) + e := make([]small, p) + ev := make([]small, p) rqMultSmall(cf, c, f) - cf3 := rqMult3(cf) - e := r3FromRq(cf3) - ev := r3Mult(e, ginv) + rqMult3(cf3, cf) + r3FromRq(e, cf3) + r3Mult(ev, e, ginv) mask := weightwMask(ev) /* 0 if weight w, else -1 */ for i := 0; i < w; i++ { @@ -526,8 +519,6 @@ func decrypt(c []Fq, f []small, ginv []small) []small { for i := w; i < p; i++ { r[i] = ev[i] & small(^mask) } - return r - } // Encoding small polynomials (including short polynomials) @@ -661,8 +652,9 @@ func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { // C = zEncrypt(r,pk) func zEncrypt(C []byte, r Inputs, pk []byte) { h := make([]Fq, p) + c := make([]Fq, p) rqDecode(h, pk) - c := encrypt(r[:], h) + encrypt(c, r[:], h) roundedEncode(C, c) } @@ -677,7 +669,7 @@ func zDecrypt(r *Inputs, C []byte, sk []byte) { smallDecode(v, sk) roundedDecode(c, C) - copy(r[:], decrypt(c, f, v)) + decrypt(r[:], c, f, v) } // Confirmation hash diff --git a/kem/ntruprime/templates/ntrulpr.templ.go b/kem/ntruprime/templates/ntrulpr.templ.go index fbcb4ca88..0556688dc 100644 --- a/kem/ntruprime/templates/ntrulpr.templ.go +++ b/kem/ntruprime/templates/ntrulpr.templ.go @@ -28,19 +28,19 @@ const ( q = ntrup.Q q12 = ((q - 1) / 2) roundedBytes = ntrup.RoundedBytes - + w = ntrup.W tau0 = ntrup.Tau0 tau1 = ntrup.Tau1 tau2 = ntrup.Tau2 tau3 = ntrup.Tau3 - + I = ntrup.I - + hashBytes = 32 smallBytes = ((p + 3) / 4) - + inputsBytes = I / 8 seedBytes = 32 ciphertextsBytes = roundedBytes + topBytes @@ -48,7 +48,7 @@ const ( publicKeysBytes = seedBytes + roundedBytes confirmBytes = 32 - + tau = 16 topBytes = I / 2 ) @@ -56,10 +56,10 @@ const ( const ( // Size of seed for NewKeyFromSeed KeySeedSize = seedBytes + p*4 + inputsBytes - + // Size of seed for EncapsulateTo. EncapsulationSeedSize = inputsBytes - + // Size of the established shared key. SharedKeySize = ntrup.SharedKeySize @@ -86,7 +86,7 @@ type ( // x must not be close to top int16 func f3Freeze(x int16) small { - return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 + return small(internal.Int32ModUint14(int32(x)+1, 3)) - 1 } /* ----- arithmetic mod q */ @@ -98,7 +98,7 @@ func f3Freeze(x int16) small { /* x must not be close to top int32 */ func fqFreeze(x int32) Fq { - return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) + return Fq(internal.Int32ModUint14(x+q12, q) - q12) } func top(C Fq) int8 { @@ -295,18 +295,15 @@ func encrypt(B []Fq, T []int8,r []int8, G []Fq, A []Fq, b []small) { } // r = decrypt((B,T),a) -func decrypt(B []Fq, T []int8, a []small) []int8 { +func decrypt(r []int8, B []Fq, T []int8, a []small) { aB := make([]Fq, p) - r := make([]int8, I) - rqMultSmall(aB, B, a) for i := 0; i < I; i++ { - r[i] = int8(-internal.Int16_negative_mask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) + r[i] = int8(-internal.Int16NegativeMask(int16(fqFreeze(int32(right(T[i])) - int32(aB[i]) + 4*w + 1)))) } - return r } // Encoding I-bit inputs @@ -355,7 +352,7 @@ func generator(G []Fq, k []byte) { L := make([]uint32, 4*p) expand(L, k) for i := 0; i < p; i++ { - G[i] = Fq(internal.Uint32_mod_uint14(L[i], q) - q12) + G[i] = Fq(internal.Uint32ModUint14(L[i], q) - q12) } } @@ -500,24 +497,20 @@ func topEncode(s []byte,T []int8) { } } -func topDecode(s []byte) (T []int8) { - - T = make([]int8, 2*topBytes+1) +func topDecode(T []int8, s []byte) { for i := 0; i < topBytes; i++ { T[2*i] = int8(s[i] & 15) T[2*i+1] = int8(s[i] >> 4) } - return T } // Streamlined NTRU Prime Core plus encoding -func inputsRandom(seed []byte) (r Inputs) { +func inputsRandom(r *Inputs, seed []byte) { for i := 0; i < I; i++ { r[i] = int8(1 & (seed[i>>3] >> (i & 7))) } - return r } // Generates public key and private key @@ -552,11 +545,12 @@ func zEncrypt(c []byte, r Inputs, pk []byte) { func zDecrypt(r *Inputs, c []byte, sk []byte) { a := make([]small, p) B := make([]Fq, p) + T := make([]int8, I) smallDecode(a, sk) roundedDecode(B, c) - T := topDecode(c[roundedBytes:]) - copy(r[:], decrypt(B, T, a)) + topDecode(T, c[roundedBytes:]) + decrypt(r[:], B, T, a) } // Confirmation hash @@ -631,7 +625,7 @@ func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { seed=make([]byte,EncapsulationSeedSize) cryptoRand.Read(seed) } - + if len(seed) != EncapsulationSeedSize { panic("seed must be of length EncapsulationSeedSize") } @@ -644,9 +638,10 @@ func (pk PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { r_enc := make([]byte, inputsBytes) cache := make([]byte, hashBytes) + var r Inputs hashPrefix(cache, 4, pk.pk[:], publicKeysBytes) - r := inputsRandom(seed) + inputsRandom(&r, seed) hide(c, r_enc, r, pk.pk[:], cache) hashSession(k, 1, r_enc, c) } diff --git a/kem/ntruprime/templates/sntrup.templ.go b/kem/ntruprime/templates/sntrup.templ.go index e205f711d..79bd48a1f 100644 --- a/kem/ntruprime/templates/sntrup.templ.go +++ b/kem/ntruprime/templates/sntrup.templ.go @@ -36,7 +36,7 @@ const ( rqBytes = ntrup.RqBytes w = ntrup.W - + hashBytes = 32 smallBytes = ((p + 3) / 4) @@ -55,13 +55,13 @@ const ( // Size of the established shared key. SharedKeySize = ntrup.SharedKeySize - + // Size of the encapsulated shared key. CiphertextSize = ntrup.CiphertextSize - + // Size of a packed public key. PublicKeySize = ntrup.PublicKeySize - + // Size of a packed private key. PrivateKeySize = ntrup.PrivateKeySize ) @@ -74,7 +74,7 @@ const ( // x must not be close to top int16 func f3Freeze(x int16) small { - return small(internal.Int32_mod_uint14(int32(x)+1, 3)) - 1 + return small(internal.Int32ModUint14(int32(x)+1, 3)) - 1 } // Arithmetic operations over GF(q) @@ -84,7 +84,7 @@ func f3Freeze(x int16) small { /* x must not be close to top int32 */ func fqFreeze(x int32) Fq { - return Fq(internal.Int32_mod_uint14(x+q12, q) - q12) + return Fq(internal.Int32ModUint14(x+q12, q) - q12) } // Calculates reciprocal of Fq @@ -111,22 +111,19 @@ func weightwMask(r []small) int { // returns -1 if non zero // otherwise returns 0 if weight==w - return internal.Int16_nonzero_mask(int16(weight - w)) + return internal.Int16NonzeroMask(int16(weight - w)) } /* R3_fromR(R_fromRq(r)) */ -func r3FromRq(r []Fq) []small { - out := make([]small, p) +func r3FromRq(out []small, r []Fq) { for i := 0; i < p; i++ { out[i] = small(f3Freeze(int16(r[i]))) } - return out } // h = f*g in the ring R3 -func r3Mult(f []small, g []small) (h []small) { - h = make([]small, p) +func r3Mult(h []small, f []small, g []small) { fg := make([]small, p+p-1) var result small var i, j int @@ -155,9 +152,6 @@ func r3Mult(f []small, g []small) (h []small) { for i = 0; i < p; i++ { h[i] = fg[i] } - - return h - } // Calculates the reciprocal of R3 polynomials @@ -192,7 +186,7 @@ func r3Recip(out []small, in []small) int { v[0] = 0 sign = int(-g[0] * f[0]) - var swap int = int(internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0]))) + var swap int = int(internal.Int16NegativeMask(int16(-delta)) & internal.Int16NonzeroMask(int16(g[0]))) delta ^= swap & int(delta^-delta) delta += 1 @@ -226,7 +220,7 @@ func r3Recip(out []small, in []small) int { out[i] = small(sign * int(v[p-1-i])) } - return internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16NonzeroMask(int16(delta)) } @@ -265,18 +259,15 @@ func rqMultSmall(h []Fq, f []Fq, g []small) { } // h = 3f in Rq -func rqMult3(f []Fq) (h []Fq) { - h = make([]Fq, p) +func rqMult3(h []Fq, f []Fq) { for i := 0; i < p; i++ { h[i] = fqFreeze(int32(3 * f[i])) } - return h } // Returns 0 if recip succeeded; else -1 -// out = 1/(3*in) in Rq -func rqRecip3(in []small) ([]Fq, int) { - out := make([]Fq, p) +// out = 1/(3*in) in Rq +func rqRecip3(out []Fq, in []small) int { f := make([]Fq, p+1) g := make([]Fq, p+1) v := make([]Fq, p+1) @@ -303,7 +294,7 @@ func rqRecip3(in []small) ([]Fq, int) { } v[0] = 0 - swap = internal.Int16_negative_mask(int16(-delta)) & internal.Int16_nonzero_mask(int16(g[0])) + swap = internal.Int16NegativeMask(int16(-delta)) & internal.Int16NonzeroMask(int16(g[0])) delta ^= swap & (delta ^ -delta) delta += 1 @@ -337,18 +328,16 @@ func rqRecip3(in []small) ([]Fq, int) { out[i] = fqFreeze(int32(scale) * (int32)(v[p-1-i])) } - return out, internal.Int16_nonzero_mask(int16(delta)) + return internal.Int16NonzeroMask(int16(delta)) } // Rounding all coefficients of a polynomial to the nearest multiple of 3 // Rounded polynomials mod q -func round(a []Fq) []Fq { - out := make([]Fq, p) +func round(out []Fq, a []Fq) { for i := 0; i < p; i++ { out[i] = a[i] - Fq(f3Freeze(int16(a[i]))) } - return out } @@ -473,7 +462,7 @@ func smallRandom(out []small, seed []byte){ // Streamlined NTRU Prime Core -// h,(f,ginv) = keyGen() +// h,(f,ginv) = keyGen() func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { g := make([]small, p) seed := make([]byte, 4*p+4*p) @@ -503,27 +492,31 @@ func keyGen(h []Fq, f []small, ginv []small, gen *nist.DRBG) { } shortRandom(f, seed[4*p:]) - finv, _ := rqRecip3(f) /* always works */ + finv := make([]Fq, p) + + rqRecip3(finv, f) /* always works */ rqMultSmall(h, finv, g) } // c = encrypt(r,h) -func encrypt(r []small, h []Fq) []Fq { +func encrypt(c []Fq, r []small, h []Fq) { hr := make([]Fq, p) + rqMultSmall(hr, h, r) - c := round(hr) - return c + round(c, hr) } // r = decrypt(c,(f,ginv)) -func decrypt(c []Fq, f []small, ginv []small) []small { - r := make([]small, p) +func decrypt(r []small, c []Fq, f []small, ginv []small) { cf := make([]Fq, p) + cf3 := make([]Fq, p) + e := make([]small, p) + ev := make([]small, p) rqMultSmall(cf, c, f) - cf3 := rqMult3(cf) - e := r3FromRq(cf3) - ev := r3Mult(e, ginv) + rqMult3(cf3, cf) + r3FromRq(e, cf3) + r3Mult(ev, e, ginv) mask := weightwMask(ev) /* 0 if weight w, else -1 */ for i := 0; i < w; i++ { @@ -533,8 +526,6 @@ func decrypt(c []Fq, f []small, ginv []small) []small { for i := w; i < p; i++ { r[i] = ev[i] & small(^mask) } - return r - } // Encoding small polynomials (including short polynomials) @@ -563,7 +554,7 @@ func smallEncode(s []byte, f []small) { s[0] = byte(x) } -// Transform bytes into polynomial in R +// Transform bytes into polynomial in R func smallDecode(f []small, s []byte) { var index int = 0 var x byte @@ -669,8 +660,9 @@ func zKeyGen(pk []byte, sk []byte, gen *nist.DRBG) { // C = zEncrypt(r,pk) func zEncrypt(C []byte, r Inputs, pk []byte) { h := make([]Fq, p) + c := make([]Fq, p) rqDecode(h, pk) - c := encrypt(r[:], h) + encrypt(c, r[:], h) roundedEncode(C, c) } @@ -685,7 +677,7 @@ func zDecrypt(r *Inputs, C []byte, sk []byte) { smallDecode(v, sk) roundedDecode(c, C) - copy(r[:], decrypt(c, f, v)) + decrypt(r[:], c, f, v) } // Confirmation hash @@ -709,7 +701,7 @@ func hashSession(k []byte, b int, y []byte, z []byte) { x := make([]byte, hashBytes+ciphertextsBytes+confirmBytes) hashPrefix(x, 3, y, inputsBytes) - + copy(x[hashBytes:],z[:ciphertextsBytes+confirmBytes]) hashPrefix(k, b, x, len(x)) @@ -746,10 +738,10 @@ func hide(c []byte, r_enc []byte, r Inputs, pk []byte, cache []byte) { } -// Takes as input a public key +// Takes as input a public key // Returns ciphertext and shared key -// c,k = encap(pk) -func (pub PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { +// c,k = encap(pk) +func (pub PublicKey) EncapsulateTo(c []byte, k []byte, seed []byte) { if seed == nil { seed = make([]byte, 4*p) cryptoRand.Read(seed) @@ -876,7 +868,7 @@ func (sk *PrivateKey) Public() kem.PublicKey { skey, _ := sk.MarshalBinary() ppk := skey[secretKeysBytes : secretKeysBytes+publicKeysBytes] copy(pk[:], ppk[:]) - return &PublicKey{pk: pk} + return &PublicKey{pk: pk} } func (pk *PublicKey) MarshalBinary() ([]byte, error) { diff --git a/kem/schemes/schemes_test.go b/kem/schemes/schemes_test.go index 4c8e1853d..a466be0f2 100644 --- a/kem/schemes/schemes_test.go +++ b/kem/schemes/schemes_test.go @@ -166,5 +166,4 @@ func Example_schemes() { // ntrulpr761 // ntrulpr857 // ntrulpr953 - } diff --git a/pke/ntruprime/gen.go b/pke/ntruprime/gen.go index cc9c788e2..a9d456dfc 100644 --- a/pke/ntruprime/gen.go +++ b/pke/ntruprime/gen.go @@ -9,8 +9,8 @@ import ( ) type StreamlinedInstance struct { - Name string - P, Q, Rounded_bytes, Rq_bytes, W, SharedKeySize, CiphertextSize, PublicKeySize, PrivateKeySize int + Name string + P, Q, RoundedBytes, RqBytes, W, SharedKeySize, CiphertextSize, PublicKeySize, PrivateKeySize int } func (m StreamlinedInstance) Pkg() string { @@ -18,9 +18,9 @@ func (m StreamlinedInstance) Pkg() string { } type LPRInstance struct { - Name string - P, Q, Rounded_bytes, Rq_bytes, W, SharedKeySize, CiphertextSize, PublicKeySize, PrivateKeySize int - Tau0, Tau1, Tau2, Tau3 int + Name string + P, Q, RoundedBytes, RqBytes, W, SharedKeySize, CiphertextSize, PublicKeySize, PrivateKeySize int + Tau0, Tau1, Tau2, Tau3 int } func (m LPRInstance) Pkg() string { @@ -29,20 +29,20 @@ func (m LPRInstance) Pkg() string { var ( StreamlinedInstances = []StreamlinedInstance{ - {Name: "SNTRUP761", P: 761, Q: 4591, Rounded_bytes: 1007, Rq_bytes: 1158, W: 286, SharedKeySize: 32, CiphertextSize: 1039, PublicKeySize: 1158, PrivateKeySize: 1763}, - {Name: "SNTRUP653", P: 653, Q: 4621, Rounded_bytes: 865, Rq_bytes: 994, W: 288, SharedKeySize: 32, CiphertextSize: 897, PublicKeySize: 994, PrivateKeySize: 1518}, - {Name: "SNTRUP857", P: 857, Q: 5167, Rounded_bytes: 1152, Rq_bytes: 1322, W: 322, SharedKeySize: 32, CiphertextSize: 1184, PublicKeySize: 1322, PrivateKeySize: 1999}, - {Name: "SNTRUP953", P: 953, Q: 6343, Rounded_bytes: 1317, Rq_bytes: 1505, W: 396, SharedKeySize: 32, CiphertextSize: 1349, PublicKeySize: 1505, PrivateKeySize: 2254}, - {Name: "SNTRUP1013", P: 1013, Q: 7177, Rounded_bytes: 1423, Rq_bytes: 1623, W: 448, SharedKeySize: 32, CiphertextSize: 1455, PublicKeySize: 1623, PrivateKeySize: 2417}, - {Name: "SNTRUP1277", P: 1277, Q: 7879, Rounded_bytes: 1815, Rq_bytes: 2067, W: 492, SharedKeySize: 32, CiphertextSize: 1847, PublicKeySize: 2067, PrivateKeySize: 3059}, + {Name: "SNTRUP761", P: 761, Q: 4591, RoundedBytes: 1007, RqBytes: 1158, W: 286, SharedKeySize: 32, CiphertextSize: 1039, PublicKeySize: 1158, PrivateKeySize: 1763}, + {Name: "SNTRUP653", P: 653, Q: 4621, RoundedBytes: 865, RqBytes: 994, W: 288, SharedKeySize: 32, CiphertextSize: 897, PublicKeySize: 994, PrivateKeySize: 1518}, + {Name: "SNTRUP857", P: 857, Q: 5167, RoundedBytes: 1152, RqBytes: 1322, W: 322, SharedKeySize: 32, CiphertextSize: 1184, PublicKeySize: 1322, PrivateKeySize: 1999}, + {Name: "SNTRUP953", P: 953, Q: 6343, RoundedBytes: 1317, RqBytes: 1505, W: 396, SharedKeySize: 32, CiphertextSize: 1349, PublicKeySize: 1505, PrivateKeySize: 2254}, + {Name: "SNTRUP1013", P: 1013, Q: 7177, RoundedBytes: 1423, RqBytes: 1623, W: 448, SharedKeySize: 32, CiphertextSize: 1455, PublicKeySize: 1623, PrivateKeySize: 2417}, + {Name: "SNTRUP1277", P: 1277, Q: 7879, RoundedBytes: 1815, RqBytes: 2067, W: 492, SharedKeySize: 32, CiphertextSize: 1847, PublicKeySize: 2067, PrivateKeySize: 3059}, } LPRInstances = []LPRInstance{ - {Name: "NTRULPR653", P: 653, Q: 4621, Rounded_bytes: 865, W: 252, Tau0: 2175, Tau1: 113, Tau2: 2031, Tau3: 290, SharedKeySize: 32, CiphertextSize: 1025, PublicKeySize: 897, PrivateKeySize: 1125}, - {Name: "NTRULPR761", P: 761, Q: 4591, Rounded_bytes: 1007, W: 250, Tau0: 2156, Tau1: 114, Tau2: 2007, Tau3: 287, SharedKeySize: 32, CiphertextSize: 1167, PublicKeySize: 1039, PrivateKeySize: 1294}, - {Name: "NTRULPR857", P: 857, Q: 5167, Rounded_bytes: 1152, W: 281, Tau0: 2433, Tau1: 101, Tau2: 2265, Tau3: 324, SharedKeySize: 32, CiphertextSize: 1312, PublicKeySize: 1184, PrivateKeySize: 1463}, - {Name: "NTRULPR953", P: 953, Q: 6343, Rounded_bytes: 1317, W: 345, Tau0: 2997, Tau1: 82, Tau2: 2798, Tau3: 400, SharedKeySize: 32, CiphertextSize: 1477, PublicKeySize: 1349, PrivateKeySize: 1652}, - {Name: "NTRULPR1013", P: 1013, Q: 7177, Rounded_bytes: 1423, W: 392, Tau0: 3367, Tau1: 73, Tau2: 3143, Tau3: 449, SharedKeySize: 32, CiphertextSize: 1583, PublicKeySize: 1455, PrivateKeySize: 1773}, - {Name: "NTRULPR1277", P: 1277, Q: 7879, Rounded_bytes: 1815, W: 429, Tau0: 3724, Tau1: 66, Tau2: 3469, Tau3: 496, SharedKeySize: 32, CiphertextSize: 1975, PublicKeySize: 1847, PrivateKeySize: 2231}, + {Name: "NTRULPR653", P: 653, Q: 4621, RoundedBytes: 865, W: 252, Tau0: 2175, Tau1: 113, Tau2: 2031, Tau3: 290, SharedKeySize: 32, CiphertextSize: 1025, PublicKeySize: 897, PrivateKeySize: 1125}, + {Name: "NTRULPR761", P: 761, Q: 4591, RoundedBytes: 1007, W: 250, Tau0: 2156, Tau1: 114, Tau2: 2007, Tau3: 287, SharedKeySize: 32, CiphertextSize: 1167, PublicKeySize: 1039, PrivateKeySize: 1294}, + {Name: "NTRULPR857", P: 857, Q: 5167, RoundedBytes: 1152, W: 281, Tau0: 2433, Tau1: 101, Tau2: 2265, Tau3: 324, SharedKeySize: 32, CiphertextSize: 1312, PublicKeySize: 1184, PrivateKeySize: 1463}, + {Name: "NTRULPR953", P: 953, Q: 6343, RoundedBytes: 1317, W: 345, Tau0: 2997, Tau1: 82, Tau2: 2798, Tau3: 400, SharedKeySize: 32, CiphertextSize: 1477, PublicKeySize: 1349, PrivateKeySize: 1652}, + {Name: "NTRULPR1013", P: 1013, Q: 7177, RoundedBytes: 1423, W: 392, Tau0: 3367, Tau1: 73, Tau2: 3143, Tau3: 449, SharedKeySize: 32, CiphertextSize: 1583, PublicKeySize: 1455, PrivateKeySize: 1773}, + {Name: "NTRULPR1277", P: 1277, Q: 7879, RoundedBytes: 1815, W: 429, Tau0: 3724, Tau1: 66, Tau2: 3469, Tau3: 496, SharedKeySize: 32, CiphertextSize: 1975, PublicKeySize: 1847, PrivateKeySize: 2231}, } TemplateWarning = "// Code generated from" ) @@ -76,7 +76,7 @@ func generatePackageFiles() { if offset == -1 { panic("Missing template warning in pkg.templ.go") } - err = ioutil.WriteFile(mode.Pkg()+"/params.go", []byte(res[offset:]), 0o644) + err = ioutil.WriteFile(mode.Pkg()+"/params.go", []byte(res[offset:]), 0o600) if err != nil { panic(err) } @@ -107,7 +107,7 @@ func generateLPRFiles() { if offset == -1 { panic("Missing template warning in pkg.templ.go") } - err = ioutil.WriteFile(mode.Pkg()+"/params.go", []byte(res[offset:]), 0o644) + err = ioutil.WriteFile(mode.Pkg()+"/params.go", []byte(res[offset:]), 0o600) if err != nil { panic(err) } diff --git a/pke/ntruprime/templates/ntrulpr.params.templ.go b/pke/ntruprime/templates/ntrulpr.params.templ.go index e2a5e44fa..58dc9c1b6 100644 --- a/pke/ntruprime/templates/ntrulpr.params.templ.go +++ b/pke/ntruprime/templates/ntrulpr.params.templ.go @@ -8,7 +8,7 @@ package ntruprime const ( P = {{.P}} Q = {{.Q}} - RoundedBytes = {{.Rounded_bytes}} + RoundedBytes = {{.RoundedBytes}} W = {{.W}} Tau0 = {{.Tau0}} diff --git a/pke/ntruprime/templates/sntrup.params.templ.go b/pke/ntruprime/templates/sntrup.params.templ.go index d1e4a83ab..7e890b5a4 100644 --- a/pke/ntruprime/templates/sntrup.params.templ.go +++ b/pke/ntruprime/templates/sntrup.params.templ.go @@ -7,8 +7,8 @@ package ntruprime const ( P = {{.P}} Q = {{.Q}} - RoundedBytes = {{.Rounded_bytes}} - RqBytes = {{.Rq_bytes}} + RoundedBytes = {{.RoundedBytes}} + RqBytes = {{.RqBytes}} W = {{.W}} ) From 5002933574fce6a3c977774c75e71aba012c4c1b Mon Sep 17 00:00:00 2001 From: Keelan10 Date: Tue, 20 Dec 2022 22:19:39 +0400 Subject: [PATCH 4/5] Optimize Encode function --- kem/ntruprime/internal/Encode.go | 53 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/kem/ntruprime/internal/Encode.go b/kem/ntruprime/internal/Encode.go index 192503bda..effae5e83 100644 --- a/kem/ntruprime/internal/Encode.go +++ b/kem/ntruprime/internal/Encode.go @@ -1,8 +1,34 @@ package internal -// TO DO: Optimize the Encode function /* 0 <= R[i] < M[i] < 16384 */ func Encode(out []uint8, R []uint16, M []uint16, len int) { + if len > 1 { + R2 := make([]uint16, (len+1)/2) + M2 := make([]uint16, (len+1)/2) + var i int + for ; len > 1; len = (len + 1) / 2 { + for i = 0; i < len-1; i += 2 { + m0 := uint32(M[i]) + r := uint32(R[i]) + uint32(R[i+1])*m0 + m := uint32(M[i+1]) * m0 + for m >= 16384 { + out[0] = uint8(r) + out = out[1:] + + r >>= 8 + m = (m + 255) >> 8 + } + R2[i/2] = uint16(r) + M2[i/2] = uint16(m) + } + if i < len { + R2[i/2] = R[i] + M2[i/2] = M[i] + } + copy(R, R2) + copy(M, M2) + } + } if len == 1 { r := R[0] m := M[0] @@ -13,29 +39,4 @@ func Encode(out []uint8, R []uint16, M []uint16, len int) { m = (m + 255) >> 8 } } - if len > 1 { - R2 := make([]uint16, (len+1)/2) - M2 := make([]uint16, (len+1)/2) - var i int - - for i = 0; i < len-1; i += 2 { - m0 := uint32(M[i]) - r := uint32(R[i]) + uint32(R[i+1])*m0 - m := uint32(M[i+1]) * m0 - for m >= 16384 { - out[0] = uint8(r) - out = out[1:] - - r >>= 8 - m = (m + 255) >> 8 - } - R2[i/2] = uint16(r) - M2[i/2] = uint16(m) - } - if i < len { - R2[i/2] = R[i] - M2[i/2] = M[i] - } - Encode(out, R2, M2, (len+1)/2) - } } From e8d7edb133624b7fa80e66fd9f6eb97f3f1cd361 Mon Sep 17 00:00:00 2001 From: Keelan10 Date: Thu, 30 Mar 2023 22:24:14 +0400 Subject: [PATCH 5/5] Restructure code --- kem/ntruprime/sntrup1013/ntruprime.go | 20 ++- kem/ntruprime/sntrup1277/ntruprime.go | 20 ++- kem/ntruprime/sntrup653/ntruprime.go | 20 ++- kem/ntruprime/sntrup761/ntruprime.go | 20 ++- kem/ntruprime/sntrup857/ntruprime.go | 20 ++- kem/ntruprime/sntrup953/ntruprime.go | 20 ++- kem/ntruprime/templates/sntrup.templ.go | 20 ++- kem/schemes/schemes.go | 17 +- kem/schemes/schemes_test.go | 10 +- pke/ntruprime/kem/kem.go | 96 +---------- pke/ntruprime/kem/schemes/sntrup/schemes.go | 14 +- .../kem/schemes/sntrup/schemes_test.go | 152 ------------------ 12 files changed, 162 insertions(+), 267 deletions(-) delete mode 100644 pke/ntruprime/kem/schemes/sntrup/schemes_test.go diff --git a/kem/ntruprime/sntrup1013/ntruprime.go b/kem/ntruprime/sntrup1013/ntruprime.go index 680c754db..c09bb6a0a 100644 --- a/kem/ntruprime/sntrup1013/ntruprime.go +++ b/kem/ntruprime/sntrup1013/ntruprime.go @@ -13,8 +13,9 @@ import ( "crypto/sha512" "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/kem" "github.com/cloudflare/circl/kem/ntruprime/internal" - "github.com/cloudflare/circl/pke/ntruprime/kem" + sntrupKem "github.com/cloudflare/circl/pke/ntruprime/kem" ntrup "github.com/cloudflare/circl/pke/ntruprime/sntrup1013" ) @@ -45,6 +46,12 @@ const ( ) const ( + // Size of seed for NewKeyFromSeed + // Note that during keyGen, a random small is generated until a valid one (whose reciprocal succeeds) is found + // The size of keySeed depends on the number of times the reciprocal fails + // This is why DeriveKeyPairFromGen is used to deterministically derive key pair instead of using seed + KeySeedSize = 4*p + p*4 + inputsBytes + // Size of seed for EncapsulateTo. EncapsulationSeedSize = 4 * p @@ -816,14 +823,18 @@ type PublicKey struct { type scheme struct{} -var sch kem.Scheme = &scheme{} +var sch sntrupKem.Scheme = &scheme{} // Scheme returns a KEM interface. func Scheme() kem.Scheme { return sch } +// SntrupScheme returns a sntrup.KEM interface +func SntrupScheme() sntrupKem.Scheme { return sch } + func (*scheme) Name() string { return "sntrup1013" } func (*scheme) PublicKeySize() int { return PublicKeySize } func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } func (*scheme) SharedKeySize() int { return SharedKeySize } func (*scheme) CiphertextSize() int { return CiphertextSize } func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } @@ -876,6 +887,11 @@ func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { } +// Not used +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + return nil, nil +} + func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { if gen == nil { diff --git a/kem/ntruprime/sntrup1277/ntruprime.go b/kem/ntruprime/sntrup1277/ntruprime.go index ab6e4b74f..a1318569d 100644 --- a/kem/ntruprime/sntrup1277/ntruprime.go +++ b/kem/ntruprime/sntrup1277/ntruprime.go @@ -13,8 +13,9 @@ import ( "crypto/sha512" "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/kem" "github.com/cloudflare/circl/kem/ntruprime/internal" - "github.com/cloudflare/circl/pke/ntruprime/kem" + sntrupKem "github.com/cloudflare/circl/pke/ntruprime/kem" ntrup "github.com/cloudflare/circl/pke/ntruprime/sntrup1277" ) @@ -45,6 +46,12 @@ const ( ) const ( + // Size of seed for NewKeyFromSeed + // Note that during keyGen, a random small is generated until a valid one (whose reciprocal succeeds) is found + // The size of keySeed depends on the number of times the reciprocal fails + // This is why DeriveKeyPairFromGen is used to deterministically derive key pair instead of using seed + KeySeedSize = 4*p + p*4 + inputsBytes + // Size of seed for EncapsulateTo. EncapsulationSeedSize = 4 * p @@ -816,14 +823,18 @@ type PublicKey struct { type scheme struct{} -var sch kem.Scheme = &scheme{} +var sch sntrupKem.Scheme = &scheme{} // Scheme returns a KEM interface. func Scheme() kem.Scheme { return sch } +// SntrupScheme returns a sntrup.KEM interface +func SntrupScheme() sntrupKem.Scheme { return sch } + func (*scheme) Name() string { return "sntrup1277" } func (*scheme) PublicKeySize() int { return PublicKeySize } func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } func (*scheme) SharedKeySize() int { return SharedKeySize } func (*scheme) CiphertextSize() int { return CiphertextSize } func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } @@ -876,6 +887,11 @@ func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { } +// Not used +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + return nil, nil +} + func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { if gen == nil { diff --git a/kem/ntruprime/sntrup653/ntruprime.go b/kem/ntruprime/sntrup653/ntruprime.go index 126367bd4..e5d4aae79 100644 --- a/kem/ntruprime/sntrup653/ntruprime.go +++ b/kem/ntruprime/sntrup653/ntruprime.go @@ -13,8 +13,9 @@ import ( "crypto/sha512" "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/kem" "github.com/cloudflare/circl/kem/ntruprime/internal" - "github.com/cloudflare/circl/pke/ntruprime/kem" + sntrupKem "github.com/cloudflare/circl/pke/ntruprime/kem" ntrup "github.com/cloudflare/circl/pke/ntruprime/sntrup653" ) @@ -45,6 +46,12 @@ const ( ) const ( + // Size of seed for NewKeyFromSeed + // Note that during keyGen, a random small is generated until a valid one (whose reciprocal succeeds) is found + // The size of keySeed depends on the number of times the reciprocal fails + // This is why DeriveKeyPairFromGen is used to deterministically derive key pair instead of using seed + KeySeedSize = 4*p + p*4 + inputsBytes + // Size of seed for EncapsulateTo. EncapsulationSeedSize = 4 * p @@ -816,14 +823,18 @@ type PublicKey struct { type scheme struct{} -var sch kem.Scheme = &scheme{} +var sch sntrupKem.Scheme = &scheme{} // Scheme returns a KEM interface. func Scheme() kem.Scheme { return sch } +// SntrupScheme returns a sntrup.KEM interface +func SntrupScheme() sntrupKem.Scheme { return sch } + func (*scheme) Name() string { return "sntrup653" } func (*scheme) PublicKeySize() int { return PublicKeySize } func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } func (*scheme) SharedKeySize() int { return SharedKeySize } func (*scheme) CiphertextSize() int { return CiphertextSize } func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } @@ -876,6 +887,11 @@ func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { } +// Not used +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + return nil, nil +} + func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { if gen == nil { diff --git a/kem/ntruprime/sntrup761/ntruprime.go b/kem/ntruprime/sntrup761/ntruprime.go index bb7ed200a..1ea6b232d 100644 --- a/kem/ntruprime/sntrup761/ntruprime.go +++ b/kem/ntruprime/sntrup761/ntruprime.go @@ -13,8 +13,9 @@ import ( "crypto/sha512" "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/kem" "github.com/cloudflare/circl/kem/ntruprime/internal" - "github.com/cloudflare/circl/pke/ntruprime/kem" + sntrupKem "github.com/cloudflare/circl/pke/ntruprime/kem" ntrup "github.com/cloudflare/circl/pke/ntruprime/sntrup761" ) @@ -45,6 +46,12 @@ const ( ) const ( + // Size of seed for NewKeyFromSeed + // Note that during keyGen, a random small is generated until a valid one (whose reciprocal succeeds) is found + // The size of keySeed depends on the number of times the reciprocal fails + // This is why DeriveKeyPairFromGen is used to deterministically derive key pair instead of using seed + KeySeedSize = 4*p + p*4 + inputsBytes + // Size of seed for EncapsulateTo. EncapsulationSeedSize = 4 * p @@ -816,14 +823,18 @@ type PublicKey struct { type scheme struct{} -var sch kem.Scheme = &scheme{} +var sch sntrupKem.Scheme = &scheme{} // Scheme returns a KEM interface. func Scheme() kem.Scheme { return sch } +// SntrupScheme returns a sntrup.KEM interface +func SntrupScheme() sntrupKem.Scheme { return sch } + func (*scheme) Name() string { return "sntrup761" } func (*scheme) PublicKeySize() int { return PublicKeySize } func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } func (*scheme) SharedKeySize() int { return SharedKeySize } func (*scheme) CiphertextSize() int { return CiphertextSize } func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } @@ -876,6 +887,11 @@ func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { } +// Not used +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + return nil, nil +} + func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { if gen == nil { diff --git a/kem/ntruprime/sntrup857/ntruprime.go b/kem/ntruprime/sntrup857/ntruprime.go index 8680ddedb..35b2feb19 100644 --- a/kem/ntruprime/sntrup857/ntruprime.go +++ b/kem/ntruprime/sntrup857/ntruprime.go @@ -13,8 +13,9 @@ import ( "crypto/sha512" "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/kem" "github.com/cloudflare/circl/kem/ntruprime/internal" - "github.com/cloudflare/circl/pke/ntruprime/kem" + sntrupKem "github.com/cloudflare/circl/pke/ntruprime/kem" ntrup "github.com/cloudflare/circl/pke/ntruprime/sntrup857" ) @@ -45,6 +46,12 @@ const ( ) const ( + // Size of seed for NewKeyFromSeed + // Note that during keyGen, a random small is generated until a valid one (whose reciprocal succeeds) is found + // The size of keySeed depends on the number of times the reciprocal fails + // This is why DeriveKeyPairFromGen is used to deterministically derive key pair instead of using seed + KeySeedSize = 4*p + p*4 + inputsBytes + // Size of seed for EncapsulateTo. EncapsulationSeedSize = 4 * p @@ -816,14 +823,18 @@ type PublicKey struct { type scheme struct{} -var sch kem.Scheme = &scheme{} +var sch sntrupKem.Scheme = &scheme{} // Scheme returns a KEM interface. func Scheme() kem.Scheme { return sch } +// SntrupScheme returns a sntrup.KEM interface +func SntrupScheme() sntrupKem.Scheme { return sch } + func (*scheme) Name() string { return "sntrup857" } func (*scheme) PublicKeySize() int { return PublicKeySize } func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } func (*scheme) SharedKeySize() int { return SharedKeySize } func (*scheme) CiphertextSize() int { return CiphertextSize } func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } @@ -876,6 +887,11 @@ func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { } +// Not used +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + return nil, nil +} + func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { if gen == nil { diff --git a/kem/ntruprime/sntrup953/ntruprime.go b/kem/ntruprime/sntrup953/ntruprime.go index a7e36df7b..73bfc5ec7 100644 --- a/kem/ntruprime/sntrup953/ntruprime.go +++ b/kem/ntruprime/sntrup953/ntruprime.go @@ -13,8 +13,9 @@ import ( "crypto/sha512" "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/kem" "github.com/cloudflare/circl/kem/ntruprime/internal" - "github.com/cloudflare/circl/pke/ntruprime/kem" + sntrupKem "github.com/cloudflare/circl/pke/ntruprime/kem" ntrup "github.com/cloudflare/circl/pke/ntruprime/sntrup953" ) @@ -45,6 +46,12 @@ const ( ) const ( + // Size of seed for NewKeyFromSeed + // Note that during keyGen, a random small is generated until a valid one (whose reciprocal succeeds) is found + // The size of keySeed depends on the number of times the reciprocal fails + // This is why DeriveKeyPairFromGen is used to deterministically derive key pair instead of using seed + KeySeedSize = 4*p + p*4 + inputsBytes + // Size of seed for EncapsulateTo. EncapsulationSeedSize = 4 * p @@ -816,14 +823,18 @@ type PublicKey struct { type scheme struct{} -var sch kem.Scheme = &scheme{} +var sch sntrupKem.Scheme = &scheme{} // Scheme returns a KEM interface. func Scheme() kem.Scheme { return sch } +// SntrupScheme returns a sntrup.KEM interface +func SntrupScheme() sntrupKem.Scheme { return sch } + func (*scheme) Name() string { return "sntrup953" } func (*scheme) PublicKeySize() int { return PublicKeySize } func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } func (*scheme) SharedKeySize() int { return SharedKeySize } func (*scheme) CiphertextSize() int { return CiphertextSize } func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } @@ -876,6 +887,11 @@ func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { } +// Not used +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + return nil, nil +} + func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { if gen == nil { diff --git a/kem/ntruprime/templates/sntrup.templ.go b/kem/ntruprime/templates/sntrup.templ.go index 79bd48a1f..71cc85a10 100644 --- a/kem/ntruprime/templates/sntrup.templ.go +++ b/kem/ntruprime/templates/sntrup.templ.go @@ -17,8 +17,9 @@ import ( "crypto/sha512" "github.com/cloudflare/circl/internal/nist" - "github.com/cloudflare/circl/pke/ntruprime/kem" + "github.com/cloudflare/circl/kem" "github.com/cloudflare/circl/kem/ntruprime/internal" + sntrupKem "github.com/cloudflare/circl/pke/ntruprime/kem" ntrup "github.com/cloudflare/circl/pke/ntruprime/{{.Pkg}}" ) @@ -50,6 +51,12 @@ const ( ) const ( + // Size of seed for NewKeyFromSeed + // Note that during keyGen, a random small is generated until a valid one (whose reciprocal succeeds) is found + // The size of keySeed depends on the number of times the reciprocal fails + // This is why DeriveKeyPairFromGen is used to deterministically derive key pair instead of using seed + KeySeedSize = 4*p + p*4 + inputsBytes + // Size of seed for EncapsulateTo. EncapsulationSeedSize = 4*p @@ -824,14 +831,18 @@ type PublicKey struct { type scheme struct{} -var sch kem.Scheme = &scheme{} +var sch sntrupKem.Scheme = &scheme{} // Scheme returns a KEM interface. func Scheme() kem.Scheme { return sch } +// SntrupScheme returns a sntrup.KEM interface +func SntrupScheme() sntrupKem.Scheme { return sch } + func (*scheme) Name() string { return "{{.Pkg}}" } func (*scheme) PublicKeySize() int { return PublicKeySize } func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } func (*scheme) SharedKeySize() int { return SharedKeySize } func (*scheme) CiphertextSize() int { return CiphertextSize } func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } @@ -886,6 +897,11 @@ func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { } +// Not used +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + return nil, nil +} + func (*scheme) DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) { if gen == nil { diff --git a/kem/schemes/schemes.go b/kem/schemes/schemes.go index 5f74cbd6c..15a3eb14f 100644 --- a/kem/schemes/schemes.go +++ b/kem/schemes/schemes.go @@ -15,6 +15,7 @@ // FrodoKEM-640-SHAKE // Kyber512, Kyber768, Kyber1024 // Kyber512, Kyber768, Kyber1024 +// NTRULPR653, NTRULPR761, NTRULPR857, NTRULPR953, NTRULPR1013, NTRULPR1277 // SNTRUP653, SNTRUP761, SNTRUP857, SNTRUP953, SNTRUP1013, SNTRUP1277 package schemes @@ -35,6 +36,12 @@ import ( "github.com/cloudflare/circl/kem/ntruprime/ntrulpr761" "github.com/cloudflare/circl/kem/ntruprime/ntrulpr857" "github.com/cloudflare/circl/kem/ntruprime/ntrulpr953" + "github.com/cloudflare/circl/kem/ntruprime/sntrup1013" + "github.com/cloudflare/circl/kem/ntruprime/sntrup1277" + "github.com/cloudflare/circl/kem/ntruprime/sntrup653" + "github.com/cloudflare/circl/kem/ntruprime/sntrup761" + "github.com/cloudflare/circl/kem/ntruprime/sntrup857" + "github.com/cloudflare/circl/kem/ntruprime/sntrup953" ) var allSchemes = [...]kem.Scheme{ @@ -52,12 +59,18 @@ var allSchemes = [...]kem.Scheme{ hybrid.Kyber768X448(), hybrid.Kyber1024X448(), hybrid.P256Kyber768Draft00(), - ntrulpr1013.Scheme(), - ntrulpr1277.Scheme(), ntrulpr653.Scheme(), ntrulpr761.Scheme(), ntrulpr857.Scheme(), ntrulpr953.Scheme(), + ntrulpr1013.Scheme(), + ntrulpr1277.Scheme(), + sntrup653.Scheme(), + sntrup761.Scheme(), + sntrup857.Scheme(), + sntrup953.Scheme(), + sntrup1013.Scheme(), + sntrup1277.Scheme(), } var allSchemeNames map[string]kem.Scheme diff --git a/kem/schemes/schemes_test.go b/kem/schemes/schemes_test.go index a466be0f2..528e85f0f 100644 --- a/kem/schemes/schemes_test.go +++ b/kem/schemes/schemes_test.go @@ -160,10 +160,16 @@ func Example_schemes() { // Kyber768-X448 // Kyber1024-X448 // P256Kyber768Draft00 - // ntrulpr1013 - // ntrulpr1277 // ntrulpr653 // ntrulpr761 // ntrulpr857 // ntrulpr953 + // ntrulpr1013 + // ntrulpr1277 + // sntrup653 + // sntrup761 + // sntrup857 + // sntrup953 + // sntrup1013 + // sntrup1277 } diff --git a/pke/ntruprime/kem/kem.go b/pke/ntruprime/kem/kem.go index ec66b01fa..59bfd92c4 100644 --- a/pke/ntruprime/kem/kem.go +++ b/pke/ntruprime/kem/kem.go @@ -1,104 +1,20 @@ // Package kem provides a unified interface for Streamlined NTRU Prime KEM schemes. // -// # A register of schemes is available in the package +// # A register of Streamlined NTRU Prime schemes is available in the package // // github.com/cloudflare/circl/pke/ntruprime/kem/schemes/sntrup package kem import ( - "encoding" - "errors" - "github.com/cloudflare/circl/internal/nist" + "github.com/cloudflare/circl/kem" ) -// A KEM public key -type PublicKey interface { - // Returns the scheme for this public key - Scheme() Scheme - - encoding.BinaryMarshaler - Equal(PublicKey) bool -} - -// A KEM private key -type PrivateKey interface { - // Returns the scheme for this private key - Scheme() Scheme - - encoding.BinaryMarshaler - Equal(PrivateKey) bool - Public() PublicKey -} - // A Scheme represents a specific instance of a NTRU PRIME KEM. type Scheme interface { - // Name of the scheme - Name() string - - // GenerateKeyPair creates a new key pair. - GenerateKeyPair() (PublicKey, PrivateKey, error) - - // Encapsulate generates a shared key ss for the public key and - // encapsulates it into a ciphertext ct. - Encapsulate(pk PublicKey) (ct, ss []byte, err error) - - // Returns the shared key encapsulated in ciphertext ct for the - // private key sk. - Decapsulate(sk PrivateKey, ct []byte) ([]byte, error) - - // Unmarshals a PublicKey from the provided buffer. - UnmarshalBinaryPublicKey([]byte) (PublicKey, error) - - // Unmarshals a PrivateKey from the provided buffer. - UnmarshalBinaryPrivateKey([]byte) (PrivateKey, error) - - // Size of encapsulated keys. - CiphertextSize() int + kem.Scheme - // Size of established shared keys. - SharedKeySize() int - - // Size of packed private keys. - PrivateKeySize() int - - // Size of packed public keys. - PublicKeySize() int - - // DeriveKeyPair deterministicallly derives a pair of keys from a nist DRBG. - // Panics if the DRBG is nil - DeriveKeyPairFromGen(gen *nist.DRBG) (PublicKey, PrivateKey) - - // EncapsulateDeterministically generates a shared key ss for the public - // key deterministically from the given seed and encapsulates it into - // a ciphertext ct. If unsure, you're better off using Encapsulate(). - EncapsulateDeterministically(pk PublicKey, seed []byte) ( - ct, ss []byte, err error) - - // Size of seed used in EncapsulateDeterministically(). - EncapsulationSeedSize() int + // DeriveKeyPairFromGen deterministicallly derives a pair of keys from a nist DRBG. + // Only used for deterministic testing + DeriveKeyPairFromGen(gen *nist.DRBG) (kem.PublicKey, kem.PrivateKey) } - -var ( - // ErrTypeMismatch is the error used if types of, for instance, private - // and public keys don't match - ErrTypeMismatch = errors.New("types mismatch") - - // ErrPubKeySize is the error used if the provided public key is of - // the wrong size. - ErrPubKeySize = errors.New("wrong size for public key") - - // ErrCiphertextSize is the error used if the provided ciphertext - // is of the wrong size. - ErrCiphertextSize = errors.New("wrong size for ciphertext") - - // ErrPrivKeySize is the error used if the provided private key is of - // the wrong size. - ErrPrivKeySize = errors.New("wrong size for private key") - - // ErrPubKey is the error used if the provided public key is invalid. - ErrPubKey = errors.New("invalid public key") - - // ErrCipherText is the error used if the provided ciphertext is invalid. - ErrCipherText = errors.New("invalid ciphertext") -) diff --git a/pke/ntruprime/kem/schemes/sntrup/schemes.go b/pke/ntruprime/kem/schemes/sntrup/schemes.go index 3bb2edadf..1fc9b6f1e 100644 --- a/pke/ntruprime/kem/schemes/sntrup/schemes.go +++ b/pke/ntruprime/kem/schemes/sntrup/schemes.go @@ -5,7 +5,7 @@ // Post-quantum kems: // // SNTRUP653, SNTRUP761, SNTRUP857, SNTRUP953, SNTRUP1013, SNTRUP1277 -package schemes +package sntrupSchemes import ( "strings" @@ -20,12 +20,12 @@ import ( ) var allSchemes = [...]kem.Scheme{ - sntrup653.Scheme(), - sntrup761.Scheme(), - sntrup857.Scheme(), - sntrup953.Scheme(), - sntrup1013.Scheme(), - sntrup1277.Scheme(), + sntrup653.SntrupScheme(), + sntrup761.SntrupScheme(), + sntrup857.SntrupScheme(), + sntrup953.SntrupScheme(), + sntrup1013.SntrupScheme(), + sntrup1277.SntrupScheme(), } var allSchemeNames map[string]kem.Scheme diff --git a/pke/ntruprime/kem/schemes/sntrup/schemes_test.go b/pke/ntruprime/kem/schemes/sntrup/schemes_test.go deleted file mode 100644 index 4e5f0ccdb..000000000 --- a/pke/ntruprime/kem/schemes/sntrup/schemes_test.go +++ /dev/null @@ -1,152 +0,0 @@ -package schemes_test - -import ( - "bytes" - "fmt" - "testing" - - schemes "github.com/cloudflare/circl/pke/ntruprime/kem/schemes/sntrup" -) - -func TestCaseSensitivity(t *testing.T) { - if schemes.ByName("sntrup1277") != schemes.ByName("SNTRUP1277") { - t.Fatal() - } -} - -func BenchmarkGenerateKeyPair(b *testing.B) { - allSchemes := schemes.All() - for _, scheme := range allSchemes { - scheme := scheme - b.Run(scheme.Name(), func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, _, _ = scheme.GenerateKeyPair() - } - }) - } -} - -func BenchmarkEncapsulate(b *testing.B) { - allSchemes := schemes.All() - for _, scheme := range allSchemes { - scheme := scheme - pk, _, _ := scheme.GenerateKeyPair() - b.Run(scheme.Name(), func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, _, _ = scheme.Encapsulate(pk) - } - }) - } -} - -func BenchmarkDecapsulate(b *testing.B) { - allSchemes := schemes.All() - for _, scheme := range allSchemes { - scheme := scheme - pk, sk, _ := scheme.GenerateKeyPair() - ct, _, _ := scheme.Encapsulate(pk) - b.Run(scheme.Name(), func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, _ = scheme.Decapsulate(sk, ct) - } - }) - } -} - -func TestApi(t *testing.T) { - allSchemes := schemes.All() - for _, scheme := range allSchemes { - scheme := scheme - t.Run(scheme.Name(), func(t *testing.T) { - if scheme == nil { - t.Fatal() - } - - pk, sk, err := scheme.GenerateKeyPair() - if err != nil { - t.Fatal() - } - - packedPk, err := pk.MarshalBinary() - if err != nil { - t.Fatal() - } - - if len(packedPk) != scheme.PublicKeySize() { - t.Fatal() - } - - packedSk, err := sk.MarshalBinary() - if err != nil { - t.Fatal() - } - - if len(packedSk) != scheme.PrivateKeySize() { - t.Fatal() - } - - pk2, err := scheme.UnmarshalBinaryPublicKey(packedPk) - if err != nil { - t.Fatal() - } - - sk2, err := scheme.UnmarshalBinaryPrivateKey(packedSk) - if err != nil { - t.Fatal() - } - - if !sk.Equal(sk2) { - t.Fatal() - } - - if !pk.Equal(pk2) { - t.Fatal() - } - - ct, ss, err := scheme.Encapsulate(pk2) - if err != nil { - t.Fatal(err) - } - if len(ct) != scheme.CiphertextSize() { - t.Fatal() - } - if len(ss) != scheme.SharedKeySize() { - t.Fatal() - } - - ct3, ss3, err := scheme.Encapsulate(pk2) - if err != nil { - t.Fatal(err) - } - if bytes.Equal(ss3, ss) { - t.Fatal() - } - if bytes.Equal(ct3, ct) { - t.Fatal() - } - - ss2, err := scheme.Decapsulate(sk2, ct) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(ss, ss2) { - t.Fatal() - } - }) - } -} - -func Example_schemes() { - // import "github.com/cloudflare/circl/pke/ntruprime/kem/schemes/sntrup" - - for _, sch := range schemes.All() { - fmt.Println(sch.Name()) - } - // Output: - // sntrup653 - // sntrup761 - // sntrup857 - // sntrup953 - // sntrup1013 - // sntrup1277 -}