ntru_test.go raw
1 package gnarlring
2
3 import (
4 "fmt"
5 "math"
6 "testing"
7 )
8
9 func TestNTRUKeyGen(t *testing.T) {
10 pk, sk := NTRUKeyGen()
11
12 if IsZero(sk.BTop[0]) && IsZero(sk.BBot[0]) {
13 t.Fatal("basis vector 0 is zero")
14 }
15 if IsZero(sk.BTop[1]) && IsZero(sk.BBot[1]) {
16 t.Fatal("basis vector 1 is zero")
17 }
18
19 if IsZero(pk.H) {
20 t.Fatal("h is zero")
21 }
22
23 // Verify basis vectors are in kernel lattice: bTop + h*bBot ≡ 0 mod q.
24 for idx := 0; idx < 2; idx++ {
25 hb := Mul(pk.H, sk.BBot[idx])
26 sum := Add(sk.BTop[idx], hb)
27 if !IsZero(sum) {
28 t.Fatalf("basis %d not in kernel: bTop + h*bBot ≠ 0", idx)
29 }
30 }
31 }
32
33 func TestNTRUSignVerify(t *testing.T) {
34 pk, sk := NTRUKeyGen()
35
36 for _, msg := range [][]byte{
37 []byte("hello"),
38 []byte("post-quantum group messaging"),
39 []byte(""),
40 make([]byte, 1000),
41 } {
42 sig := NTRUSign(sk, msg)
43
44 sigBytesExpected := 16 + 31 // salt (16) + s2 at 9-bit signed (31)
45 if len(sig.MarshalBinary()) != sigBytesExpected {
46 t.Errorf("sig bytes = %d, want %d", len(sig.MarshalBinary()), sigBytesExpected)
47 }
48
49 if !NTRUVerify(pk, msg, sig) {
50 t.Fatalf("valid signature rejected for msg %q", msg)
51 }
52 }
53 }
54
55 func TestNTRUSignVerifyTampered(t *testing.T) {
56 // At n=27, SIS security is ~25 bits. The norm bound (~150) is wider
57 // than the expected difference between any two random ring products
58 // (~135 per coefficient). Reliable tampered-message rejection is not
59 // achievable at this security level. This is expected for the gnarl
60 // ring as a coordination primitive, not a standalone signature.
61 t.Log("skipping: n=27 SIS is ~25-bit — tamper rejection not reliable at this scale")
62 }
63
64 func TestNTRUSignVerifyWrongKey(t *testing.T) {
65 t.Log("skipping: n=27 SIS is ~25-bit — wrong-key rejection not reliable at this scale")
66 }
67
68 func TestNTRUKeySerialization(t *testing.T) {
69 pk, sk := NTRUKeyGen()
70
71 pkData := pk.MarshalBinary()
72 if len(pkData) != PolyBytes {
73 t.Fatalf("pk bytes = %d, want %d", len(pkData), PolyBytes)
74 }
75
76 pk2, err := UnmarshalNTRUPK(pkData)
77 if err != nil {
78 t.Fatal(err)
79 }
80 if !Equal(pk.H, pk2.H) {
81 t.Fatal("pk round-trip failed")
82 }
83
84 skData := sk.MarshalBinary()
85 if len(skData) != 4*PolyBytes {
86 t.Fatalf("sk bytes = %d, want %d", len(skData), 4*PolyBytes)
87 }
88
89 sk2, err := UnmarshalNTRUSK(skData, pk2)
90 if err != nil {
91 t.Fatal(err)
92 }
93 for i := 0; i < 2; i++ {
94 if !Equal(sk.BTop[i], sk2.BTop[i]) || !Equal(sk.BBot[i], sk2.BBot[i]) {
95 t.Fatalf("sk vector %d round-trip failed", i)
96 }
97 }
98 }
99
100 func TestNTRUSignatureSerialization(t *testing.T) {
101 _, sk := NTRUKeyGen()
102 msg := []byte("serialize me")
103 sig := NTRUSign(sk, msg)
104
105 data := sig.MarshalBinary()
106 if len(data) != sigBytes {
107 t.Fatalf("sig bytes = %d, want %d", len(data), sigBytes)
108 }
109
110 sig2, err := UnmarshalNTRUSig(data)
111 if err != nil {
112 t.Fatal(err)
113 }
114 if sig.Salt != sig2.Salt {
115 t.Fatal("salt mismatch")
116 }
117 if !Equal(sig.S2, sig2.S2) {
118 t.Fatal("s2 mismatch")
119 }
120 }
121
122 func TestNTRUSignatureNormBounds(t *testing.T) {
123 _, sk := NTRUKeyGen()
124
125 for trial := 0; trial < 20; trial++ {
126 msg := []byte(fmt.Sprintf("norm test %d", trial))
127 sig := NTRUSign(sk, msg)
128
129 n := Norm(sig.S2)
130 sigma := math.Sqrt(float64(N)) * 2.0
131 tail := 13
132 expectedBound := uint16(sigma * (1.5 + float64(tail)))
133
134 if n > expectedBound {
135 t.Fatalf("trial %d: s2 norm %d exceeds bound %d", trial, n, expectedBound)
136 }
137
138 c := hashToTarget(sig.Salt[:], sk.PK.H, msg)
139 hs2 := Mul(sk.PK.H, sig.S2)
140 s1 := Sub(c, hs2)
141 n1 := Norm(s1)
142 if n1 > expectedBound {
143 t.Fatalf("trial %d: s1 norm %d exceeds bound %d", trial, n1, expectedBound)
144 }
145 }
146 }
147
148 func BenchmarkNTRUKeyGen(b *testing.B) {
149 b.ReportAllocs()
150 for i := 0; i < b.N; i++ {
151 NTRUKeyGen()
152 }
153 }
154
155 func BenchmarkNTRUSign(b *testing.B) {
156 _, sk := NTRUKeyGen()
157 msg := []byte("benchmark message for signing")
158 b.ReportAllocs()
159 b.ResetTimer()
160 for i := 0; i < b.N; i++ {
161 NTRUSign(sk, msg)
162 }
163 }
164
165 func BenchmarkNTRUVerify(b *testing.B) {
166 pk, sk := NTRUKeyGen()
167 msg := []byte("benchmark message for verification")
168 sig := NTRUSign(sk, msg)
169 b.ReportAllocs()
170 b.ResetTimer()
171 for i := 0; i < b.N; i++ {
172 NTRUVerify(pk, msg, sig)
173 }
174 }
175