Source file
src/crypto/mldsa/mldsa_wycheproof_test.go
1
2
3
4
5
6
7 package mldsa_test
8
9 import (
10 "bytes"
11 "crypto"
12 "crypto/internal/cryptotest/wycheproof"
13 internalmldsa "crypto/internal/fips140/mldsa"
14 "crypto/mldsa"
15 "slices"
16 "testing"
17 )
18
19
20
21 func TestVerifyWycheproof(t *testing.T) {
22 for _, file := range []string{
23 "mldsa_44_verify_test.json",
24 "mldsa_65_verify_test.json",
25 "mldsa_87_verify_test.json",
26 } {
27 var testdata wycheproof.MldsaVerifySchemaJson
28 wycheproof.LoadVectorFile(t, file, &testdata)
29
30 params := paramsForAlg(testdata.Algorithm)
31
32 for _, tg := range testdata.TestGroups {
33 publicKey := wycheproof.MustDecodeHex(tg.PublicKey)
34
35 for _, tv := range tg.Tests {
36 t.Run(wycheproof.TestName(file, tv), func(t *testing.T) {
37 t.Parallel()
38
39 shouldPass := wycheproof.ShouldPass(t, tv.Result, tv.Flags, nil)
40
41 pub, err := mldsa.NewPublicKey(params, publicKey)
42 if err != nil {
43 if shouldPass {
44 t.Fatalf("NewPublicKey: %v", err)
45 }
46 return
47 }
48
49 if !bytes.Equal(pub.Bytes(), publicKey) {
50 t.Errorf("public key roundtrip mismatch")
51 }
52
53 msg := wycheproof.MustDecodeHex(tv.Msg)
54 sig := wycheproof.MustDecodeHex(tv.Sig)
55 opts := new(mldsa.Options)
56 if tv.Ctx != nil {
57 opts.Context = string(wycheproof.MustDecodeHex(*tv.Ctx))
58 }
59
60 err = mldsa.Verify(pub, msg, sig, opts)
61 if shouldPass && err != nil {
62 t.Errorf("Verify: %v", err)
63 }
64 if !shouldPass && err == nil {
65 t.Errorf("Verify should have failed")
66 }
67 })
68 }
69 }
70 }
71 }
72
73
74
75
76
77 func TestSignSeedWycheproof(t *testing.T) {
78
79
80 for _, file := range []string{
81 "mldsa_44_sign_seed_test.json",
82 "mldsa_65_sign_seed_test.json",
83 "mldsa_87_sign_seed_test.json",
84 } {
85 var testdata wycheproof.MldsaSignSeedSchemaJson
86 wycheproof.LoadVectorFile(t, file, &testdata)
87
88 params := paramsForAlg(testdata.Algorithm)
89
90 for _, tg := range testdata.TestGroups {
91 seed := wycheproof.MustDecodeHex(tg.PrivateSeed)
92 var expectedPublicKey []byte
93 if pk, ok := tg.PublicKey.(string); ok {
94 expectedPublicKey = wycheproof.MustDecodeHex(pk)
95 }
96
97 for _, tv := range tg.Tests {
98 t.Run(wycheproof.TestName(file, tv), func(t *testing.T) {
99 t.Parallel()
100
101 shouldPass := wycheproof.ShouldPass(t, tv.Result, tv.Flags, nil)
102
103 priv, err := mldsa.NewPrivateKey(params, seed)
104 if err != nil {
105 if shouldPass {
106 t.Fatalf("NewPrivateKey: %v", err)
107 }
108 return
109 }
110
111
112
113 if expectedPublicKey != nil && !bytes.Equal(priv.PublicKey().Bytes(), expectedPublicKey) {
114 t.Fatalf("public key mismatch")
115 }
116
117 if slices.Contains(tv.Flags, "Randomized") {
118 t.Skipf("randomized signatures not supported with public API")
119 }
120
121 runSignTest(t, priv, tv, shouldPass)
122 })
123 }
124 }
125 }
126 }
127
128 func runSignTest(t *testing.T, priv *mldsa.PrivateKey, tv wycheproof.MlDsaSignTestVector, shouldPass bool) {
129 t.Helper()
130
131 var msg, μ []byte
132 opts := new(mldsa.Options)
133 if tv.Msg != nil {
134 msg = wycheproof.MustDecodeHex(*tv.Msg)
135 if tv.Ctx != nil {
136 opts.Context = string(wycheproof.MustDecodeHex(*tv.Ctx))
137 }
138 }
139 if tv.Mu != nil && *tv.Mu != "" {
140 μ = wycheproof.MustDecodeHex(*tv.Mu)
141 }
142 if msg == nil && μ == nil {
143 t.Fatalf("test vector has neither msg nor mu")
144 }
145
146 var sigMsg, sigMu []byte
147 var errMsg, errMu error
148 if msg != nil {
149 sigMsg, errMsg = priv.SignDeterministic(msg, opts)
150 }
151 if μ != nil {
152 sigMu, errMu = priv.SignDeterministic(μ, crypto.MLDSAMu)
153 }
154
155 for _, e := range []error{errMsg, errMu} {
156 if e != nil {
157 if shouldPass {
158 t.Fatalf("Sign: %v", e)
159 }
160 return
161 }
162 }
163 if !shouldPass {
164 t.Errorf("Sign unexpectedly succeeded")
165 return
166 }
167
168 expectedSig := wycheproof.MustDecodeHex(tv.Sig)
169 sig := sigMsg
170 if sig == nil {
171 sig = sigMu
172 }
173 if sigMsg != nil && sigMu != nil && !bytes.Equal(sigMsg, sigMu) {
174 t.Errorf("Sign(msg, ctx) and SignExternalMu(mu) disagree")
175 }
176 if !bytes.Equal(sig, expectedSig) {
177 t.Errorf("signature mismatch")
178 }
179
180 pub := priv.PublicKey()
181 if msg != nil {
182 if err := mldsa.Verify(pub, msg, sig, opts); err != nil {
183 t.Errorf("Verify of own signature failed: %v", err)
184 }
185 }
186
187
188
189 }
190
191
192
193
194
195 func TestMLDSASignSeedRandomizedWycheproof(t *testing.T) {
196 for _, file := range []string{
197 "mldsa_44_sign_seed_test.json",
198 "mldsa_65_sign_seed_test.json",
199 "mldsa_87_sign_seed_test.json",
200 } {
201 var testdata wycheproof.MldsaSignSeedSchemaJson
202 wycheproof.LoadVectorFile(t, file, &testdata)
203
204 newPriv := newPrivateKeyFromSeedFn(t, testdata.Algorithm)
205
206 for _, tg := range testdata.TestGroups {
207 seed := wycheproof.MustDecodeHex(tg.PrivateSeed)
208 var expectedPublicKey []byte
209 if pk, ok := tg.PublicKey.(string); ok {
210 expectedPublicKey = wycheproof.MustDecodeHex(pk)
211 }
212
213 for _, tv := range tg.Tests {
214 t.Run(wycheproof.TestName(file, tv), func(t *testing.T) {
215 t.Parallel()
216
217 shouldPass := wycheproof.ShouldPass(t, tv.Result, tv.Flags, nil)
218 priv, err := newPriv(seed)
219 if err != nil {
220 if shouldPass {
221 t.Fatalf("NewPrivateKey: %v", err)
222 }
223 return
224 }
225
226
227
228
229 if expectedPublicKey != nil && !bytes.Equal(priv.PublicKey().Bytes(), expectedPublicKey) {
230 t.Fatalf("public key mismatch")
231 }
232
233 runRandomizedSignTest(t, priv, tv, shouldPass)
234 })
235 }
236 }
237 }
238 }
239
240 func runRandomizedSignTest(t *testing.T, priv *internalmldsa.PrivateKey, tv wycheproof.MlDsaSignTestVector, shouldPass bool) {
241 t.Helper()
242
243 var msg, μ []byte
244 var ctx string
245 rnd := make([]byte, 32)
246
247 if tv.Msg != nil {
248 msg = wycheproof.MustDecodeHex(*tv.Msg)
249 if tv.Ctx != nil {
250 ctx = string(wycheproof.MustDecodeHex(*tv.Ctx))
251 }
252 }
253 if tv.Mu != nil && *tv.Mu != "" {
254 μ = wycheproof.MustDecodeHex(*tv.Mu)
255 }
256 if tv.Rnd != nil && *tv.Rnd != "" {
257 rnd = wycheproof.MustDecodeHex(*tv.Rnd)
258 }
259
260 if msg == nil && μ == nil {
261 t.Fatalf("test vector has neither msg nor mu")
262 }
263
264 var sigMsg, sigMu []byte
265 var errMsg, errMu error
266 if msg != nil {
267 sigMsg, errMsg = internalmldsa.TestingOnlySignWithRandom(priv, msg, ctx, rnd)
268 }
269 if μ != nil {
270 sigMu, errMu = internalmldsa.TestingOnlySignExternalMuWithRandom(priv, μ, rnd)
271 }
272
273 for _, e := range []error{errMsg, errMu} {
274 if e != nil {
275 if shouldPass {
276 t.Fatalf("Sign: %v", e)
277 }
278 return
279 }
280 }
281 if !shouldPass {
282 t.Errorf("Sign unexpectedly succeeded")
283 return
284 }
285
286 expectedSig := wycheproof.MustDecodeHex(tv.Sig)
287 sig := sigMsg
288 if sig == nil {
289 sig = sigMu
290 }
291 if sigMsg != nil && sigMu != nil && !bytes.Equal(sigMsg, sigMu) {
292 t.Errorf("Sign(msg, ctx) and SignExternalMu(mu) disagree")
293 }
294 if !bytes.Equal(sig, expectedSig) {
295 t.Errorf("signature mismatch")
296 }
297
298 pub := priv.PublicKey()
299 if msg != nil {
300 if err := internalmldsa.Verify(pub, msg, sig, ctx); err != nil {
301 t.Errorf("Verify of own signature failed: %v", err)
302 }
303 }
304 if μ != nil {
305 if err := internalmldsa.VerifyExternalMu(pub, μ, sig); err != nil {
306 t.Errorf("VerifyExternalMu of own signature failed: %v", err)
307 }
308 }
309 }
310
311
312
313
314
315
316 func TestMLDSANoSeedWycheproof(t *testing.T) {
317 for _, file := range []string{
318 "mldsa_44_sign_noseed_test.json",
319 "mldsa_65_sign_noseed_test.json",
320 "mldsa_87_sign_noseed_test.json",
321 } {
322 var testdata wycheproof.MldsaSignNoseedSchemaJson
323 wycheproof.LoadVectorFile(t, file, &testdata)
324
325 for _, tg := range testdata.TestGroups {
326 privateKey := wycheproof.MustDecodeHex(tg.PrivateKey)
327 var expectedPublicKey []byte
328 if pk, ok := tg.PublicKey.(string); ok {
329 expectedPublicKey = wycheproof.MustDecodeHex(pk)
330 }
331
332 for _, tv := range tg.Tests {
333 t.Run(wycheproof.TestName(file, tv), func(t *testing.T) {
334 t.Parallel()
335
336 shouldPass := wycheproof.ShouldPass(t, tv.Result, tv.Flags, nil)
337 priv, err := internalmldsa.TestingOnlyNewPrivateKeyFromSemiExpanded(privateKey)
338 if err != nil {
339 if shouldPass {
340 t.Fatalf("TestingOnlyNewPrivateKeyFromSemiExpanded: %v", err)
341 }
342 return
343 }
344
345 if expectedPublicKey != nil && !bytes.Equal(priv.PublicKey().Bytes(), expectedPublicKey) {
346 t.Fatalf("public key mismatch")
347 }
348 })
349 }
350 }
351 }
352 }
353
354 func paramsForAlg(algorithm string) mldsa.Parameters {
355 switch algorithm {
356 case "ML-DSA-44":
357 return mldsa.MLDSA44()
358 case "ML-DSA-65":
359 return mldsa.MLDSA65()
360 case "ML-DSA-87":
361 return mldsa.MLDSA87()
362 }
363 panic("unknown algorithm: " + algorithm)
364 }
365
366 func newPrivateKeyFromSeedFn(t *testing.T, algorithm string) func([]byte) (*internalmldsa.PrivateKey, error) {
367 switch algorithm {
368 case "ML-DSA-44":
369 return internalmldsa.NewPrivateKey44
370 case "ML-DSA-65":
371 return internalmldsa.NewPrivateKey65
372 case "ML-DSA-87":
373 return internalmldsa.NewPrivateKey87
374 }
375 t.Fatalf("unknown algorithm: %s", algorithm)
376 return nil
377 }
378
View as plain text