Skip to content

Commit 25d8d32

Browse files
committed
Implement NTRU Prime
1 parent 946a259 commit 25d8d32

File tree

38 files changed

+13975
-0
lines changed

38 files changed

+13975
-0
lines changed

kem/ntruprime/doc.go

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//go:generate go run gen.go
2+
3+
// Package ntruprime implements the NTRU Prime IND-CCA2 secure
4+
// key encapsulation mechanism (KEM) as submitted to round 3 of the NIST PQC
5+
// competition and described in
6+
//
7+
// https://ntruprime.cr.yp.to/nist/ntruprime-20201007.pdf
8+
package ntruprime

kem/ntruprime/gen.go

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//go:build ignore
2+
// +build ignore
3+
4+
package main
5+
6+
import (
7+
"bytes"
8+
"go/format"
9+
"io/ioutil"
10+
"strings"
11+
"text/template"
12+
)
13+
14+
type Instance struct {
15+
Name string
16+
Hash string
17+
}
18+
19+
func (m Instance) Pkg() string {
20+
return strings.ToLower(m.Name)
21+
}
22+
23+
var (
24+
SInstances = []Instance{
25+
{Name: "SNTRUP761"},
26+
{Name: "SNTRUP653"},
27+
{Name: "SNTRUP857"},
28+
{Name: "SNTRUP953"},
29+
{Name: "SNTRUP1013"},
30+
{Name: "SNTRUP1277"},
31+
}
32+
LPRInstances = []Instance{
33+
{Name: "NTRULPR761"},
34+
{Name: "NTRULPR653"},
35+
{Name: "NTRULPR857"},
36+
{Name: "NTRULPR953"},
37+
{Name: "NTRULPR1013"},
38+
{Name: "NTRULPR1277"},
39+
}
40+
TemplateWarning = "// Code generated from"
41+
)
42+
43+
func main() {
44+
generateStreamlinedPackageFiles()
45+
generateLPRPackageFiles()
46+
}
47+
48+
func generateStreamlinedPackageFiles() {
49+
template, err := template.ParseFiles("templates/sntrup.templ.go")
50+
if err != nil {
51+
panic(err)
52+
}
53+
54+
for _, mode := range SInstances {
55+
buf := new(bytes.Buffer)
56+
err := template.Execute(buf, mode)
57+
if err != nil {
58+
panic(err)
59+
}
60+
61+
// Formating output code
62+
code, err := format.Source(buf.Bytes())
63+
if err != nil {
64+
panic("error formating code")
65+
}
66+
67+
res := string(code)
68+
offset := strings.Index(res, TemplateWarning)
69+
if offset == -1 {
70+
panic("Missing template warning in pkg.templ.go")
71+
}
72+
err = ioutil.WriteFile(mode.Pkg()+"/ntruprime.go", []byte(res[offset:]), 0o644)
73+
if err != nil {
74+
panic(err)
75+
}
76+
}
77+
}
78+
79+
func generateLPRPackageFiles() {
80+
template, err := template.ParseFiles("templates/ntrulpr.templ.go")
81+
if err != nil {
82+
panic(err)
83+
}
84+
85+
for _, mode := range LPRInstances {
86+
buf := new(bytes.Buffer)
87+
err := template.Execute(buf, mode)
88+
if err != nil {
89+
panic(err)
90+
}
91+
92+
// Formating output code
93+
code, err := format.Source(buf.Bytes())
94+
if err != nil {
95+
panic("error formating code")
96+
}
97+
98+
res := string(code)
99+
offset := strings.Index(res, TemplateWarning)
100+
if offset == -1 {
101+
panic("Missing template warning in pkg.templ.go")
102+
}
103+
err = ioutil.WriteFile(mode.Pkg()+"/ntruprime.go", []byte(res[offset:]), 0o644)
104+
if err != nil {
105+
panic(err)
106+
}
107+
}
108+
}
109+
110+
func generateKAT() {
111+
template, err := template.ParseFiles("templates/kat.templ.go")
112+
if err != nil {
113+
panic(err)
114+
}
115+
116+
for _, mode := range SInstances {
117+
buf := new(bytes.Buffer)
118+
err := template.Execute(buf, mode)
119+
if err != nil {
120+
panic(err)
121+
}
122+
123+
// Formating output code
124+
code, err := format.Source(buf.Bytes())
125+
if err != nil {
126+
panic("error formating code")
127+
}
128+
129+
res := string(code)
130+
offset := strings.Index(res, TemplateWarning)
131+
if offset == -1 {
132+
panic("Missing template warning in pkg.templ.go")
133+
}
134+
err = ioutil.WriteFile(mode.Pkg()+"/kat_test.go", []byte(res[offset:]), 0o644)
135+
if err != nil {
136+
panic(err)
137+
}
138+
}
139+
}

kem/ntruprime/internal/Decode.go

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package internal
2+
3+
/* Decode(R,s,M,len) */
4+
/* assumes 0 < M[i] < 16384 */
5+
/* produces 0 <= R[i] < M[i] */
6+
func Decode(out []uint16, S []uint8, M []uint16, len int) {
7+
index := 0
8+
if len == 1 {
9+
if M[0] == 1 {
10+
out[index] = 0
11+
} else if M[0] <= 256 {
12+
out[index] = Uint32_mod_uint14(uint32(S[0]), M[0])
13+
} else {
14+
out[index] = Uint32_mod_uint14(uint32(uint16(S[0])+((uint16(S[1]))<<8)), M[0])
15+
}
16+
}
17+
if len > 1 {
18+
R2 := make([]uint16, (len+1)/2)
19+
M2 := make([]uint16, (len+1)/2)
20+
bottomr := make([]uint16, len/2)
21+
bottomt := make([]uint32, len/2)
22+
i := 0
23+
for i = 0; i < len-1; i += 2 {
24+
m := uint32(M[i]) * uint32(M[i+1])
25+
26+
if m > 256*16383 {
27+
bottomt[i/2] = 256 * 256
28+
bottomr[i/2] = uint16(S[0]) + 256*uint16(S[1])
29+
S = S[2:]
30+
M2[i/2] = uint16((((m + 255) >> 8) + 255) >> 8)
31+
} else if m >= 16384 {
32+
bottomt[i/2] = 256
33+
bottomr[i/2] = uint16(S[0])
34+
S = S[1:]
35+
M2[i/2] = uint16((m + 255) >> 8)
36+
37+
} else {
38+
bottomt[i/2] = 1
39+
bottomr[i/2] = 0
40+
M2[i/2] = uint16(m)
41+
}
42+
}
43+
if i < len {
44+
M2[i/2] = M[i]
45+
}
46+
47+
Decode(R2, S, M2, (len+1)/2)
48+
49+
for i = 0; i < len-1; i += 2 {
50+
var r uint32 = uint32(bottomr[i/2])
51+
var r1 uint32
52+
var r0 uint16
53+
54+
r += bottomt[i/2] * uint32(R2[i/2])
55+
Uint32_divmod_uint14(&r1, &r0, r, M[i])
56+
r1 = uint32(Uint32_mod_uint14(r1, M[i+1])) /* only needed for invalid inputs */
57+
58+
out[index] = r0
59+
index++
60+
out[index] = uint16(r1)
61+
index++
62+
63+
}
64+
if i < len {
65+
out[index] = R2[i/2]
66+
index++
67+
}
68+
69+
}
70+
}

kem/ntruprime/internal/Divmod.go

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package internal
2+
3+
/*
4+
CPU division instruction typically takes time depending on x.
5+
This software is designed to take time independent of x.
6+
Time still varies depending on m; user must ensure that m is constant.
7+
Time also varies on CPUs where multiplication is variable-time.
8+
There could be more CPU issues.
9+
There could also be compiler issues.
10+
*/
11+
// q, r = x/m
12+
// Returns quotient and remainder
13+
func Uint32_divmod_uint14(q *uint32, r *uint16, x uint32, m uint16) {
14+
var v uint32 = 0x80000000
15+
16+
v /= uint32(m)
17+
18+
*q = 0
19+
20+
var qpart uint32 = uint32(uint64(x) * uint64(v) >> 31)
21+
22+
x -= qpart * uint32(m)
23+
*q += qpart
24+
25+
qpart = uint32(uint64(x) * uint64(v) >> 31)
26+
x -= qpart * uint32(m)
27+
*q += qpart
28+
29+
x -= uint32(m)
30+
*q += 1
31+
var mask uint32 = -(x >> 31)
32+
x += mask & uint32(m)
33+
*q += mask
34+
35+
*r = uint16(x)
36+
37+
}
38+
39+
// Returns the quotient of x/m
40+
func Uint32_div_uint14(x uint32, m uint16) uint32 {
41+
var q uint32
42+
var r uint16
43+
Uint32_divmod_uint14(&q, &r, x, m)
44+
return q
45+
}
46+
47+
// Returns the remainder of x/m
48+
func Uint32_mod_uint14(x uint32, m uint16) uint16 {
49+
var q uint32
50+
var r uint16
51+
Uint32_divmod_uint14(&q, &r, x, m)
52+
return r
53+
}
54+
55+
// Calculates quotient and remainder
56+
func Int32_divmod_uint14(q *int32, r *uint16, x int32, m uint16) {
57+
var uq, uq2 uint32
58+
var ur, ur2 uint16
59+
var mask uint32
60+
61+
Uint32_divmod_uint14(&uq, &ur, 0x80000000+uint32(x), m)
62+
Uint32_divmod_uint14(&uq2, &ur2, 0x80000000, m)
63+
64+
ur -= ur2
65+
uq -= uq2
66+
mask = -(uint32)(ur >> 15)
67+
ur += uint16(mask & uint32(m))
68+
uq += mask
69+
*r = ur
70+
*q = int32(uq)
71+
}
72+
73+
// Returns quotient of x/m
74+
func Int32_div_uint14(x int32, m uint16) int32 {
75+
var q int32
76+
var r uint16
77+
Int32_divmod_uint14(&q, &r, x, m)
78+
return q
79+
}
80+
81+
// Returns remainder of x/m
82+
func Int32_mod_uint14(x int32, m uint16) uint16 {
83+
var q int32
84+
var r uint16
85+
Int32_divmod_uint14(&q, &r, x, m)
86+
return r
87+
88+
}
89+
90+
// Returns -1 if x!=0; else return 0
91+
func Int16_nonzero_mask(x int16) int {
92+
var u uint16 = uint16(x) /* 0, else 1...65535 */
93+
var v uint32 = uint32(u) /* 0, else 1...65535 */
94+
v = -v /* 0, else 2^32-65535...2^32-1 */
95+
v >>= 31 /* 0, else 1 */
96+
return -int(v) /* 0, else -1 */
97+
}
98+
99+
// Returns -1 if x<0; otherwise return 0
100+
func Int16_negative_mask(x int16) int {
101+
var u uint16 = uint16(x)
102+
u >>= 15
103+
return -(int)(u)
104+
}

kem/ntruprime/internal/Encode.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package internal
2+
3+
/* 0 <= R[i] < M[i] < 16384 */
4+
func Encode(out []uint8, R []uint16, M []uint16, len int) {
5+
if len == 1 {
6+
r := R[0]
7+
m := M[0]
8+
for m > 1 {
9+
out[0] = uint8(r)
10+
out = out[1:]
11+
r >>= 8
12+
m = (m + 255) >> 8
13+
}
14+
}
15+
if len > 1 {
16+
R2 := make([]uint16, (len+1)/2)
17+
M2 := make([]uint16, (len+1)/2)
18+
var i int = 0
19+
20+
for i = 0; i < len-1; i += 2 {
21+
m0 := uint32(M[i])
22+
r := uint32(R[i]) + uint32(R[i+1])*m0
23+
m := uint32(M[i+1]) * m0
24+
for m >= 16384 {
25+
out[0] = uint8(r)
26+
out = out[1:]
27+
28+
r >>= 8
29+
m = (m + 255) >> 8
30+
}
31+
R2[i/2] = uint16(r)
32+
M2[i/2] = uint16(m)
33+
}
34+
if i < len {
35+
R2[i/2] = R[i]
36+
M2[i/2] = M[i]
37+
}
38+
Encode(out, R2, M2, (len+1)/2)
39+
}
40+
}

0 commit comments

Comments
 (0)