blob: e5c67c0cf2105eed9bd0a35cb3a7273dc1a127f6 [file] [log] [blame]
Robert Sloan8ff03552017-06-14 12:40:58 -07001// run_cavp.go processes CAVP input files and generates suitable response
2// files, optionally comparing the results against the provided FAX files.
3package main
4
5import (
6 "bufio"
7 "flag"
8 "fmt"
9 "os"
10 "os/exec"
11 "path/filepath"
12 "runtime"
13 "strings"
14 "sync"
15 "time"
16)
17
18var (
19 oraclePath = flag.String("oracle-bin", "", "Path to the oracle binary")
20 suiteDir = flag.String("suite-dir", "", "Base directory containing the CAVP test suite")
21 noFAX = flag.Bool("no-fax", false, "Skip comparing against FAX files")
22)
23
24// test describes a single request file.
25type test struct {
26 // inFile is the base of the filename without an extension, i.e.
27 // “ECBMCT128”.
28 inFile string
29 // args are the arguments (not including the input filename) to the
30 // oracle binary.
31 args []string
32 // noFAX, if true, indicates that the output cannot be compared against
33 // the FAX file. (E.g. because the primitive is non-deterministic.)
34 noFAX bool
35}
36
37// testSuite describes a series of tests that are handled by a single oracle
38// binary.
39type testSuite struct {
40 // directory is the name of the directory in the CAVP input, i.e. “AES”.
41 directory string
42 // suite names the test suite to pass as the first command-line argument.
43 suite string
44 // faxScanFunc, if not nil, is the function to use instead of
45 // (*bufio.Scanner).Scan. This can be used to skip lines.
46 faxScanFunc func(*bufio.Scanner) bool
47 tests []test
48}
49
50func (t *testSuite) getDirectory() string {
51 return filepath.Join(*suiteDir, t.directory)
52}
53
54var aesGCMTests = testSuite{
55 "AES_GCM",
56 "aes_gcm",
57 nil,
58 []test{
59 {"gcmDecrypt128", []string{"dec", "aes-128-gcm"}, false},
60 {"gcmDecrypt256", []string{"dec", "aes-256-gcm"}, false},
61 {"gcmEncryptExtIV128", []string{"enc", "aes-128-gcm"}, false},
62 {"gcmEncryptExtIV256", []string{"enc", "aes-256-gcm"}, false},
63 },
64}
65
66var aesTests = testSuite{
67 "AES",
68 "aes",
69 nil,
70 []test{
71 {"CBCGFSbox128", []string{"kat", "aes-128-cbc"}, false},
72 {"CBCGFSbox192", []string{"kat", "aes-192-cbc"}, false},
73 {"CBCGFSbox256", []string{"kat", "aes-256-cbc"}, false},
74 {"CBCKeySbox128", []string{"kat", "aes-128-cbc"}, false},
75 {"CBCKeySbox192", []string{"kat", "aes-192-cbc"}, false},
76 {"CBCKeySbox256", []string{"kat", "aes-256-cbc"}, false},
77 {"CBCMMT128", []string{"kat", "aes-128-cbc"}, false},
78 {"CBCMMT192", []string{"kat", "aes-192-cbc"}, false},
79 {"CBCMMT256", []string{"kat", "aes-256-cbc"}, false},
80 {"CBCVarKey128", []string{"kat", "aes-128-cbc"}, false},
81 {"CBCVarKey192", []string{"kat", "aes-192-cbc"}, false},
82 {"CBCVarKey256", []string{"kat", "aes-256-cbc"}, false},
83 {"CBCVarTxt128", []string{"kat", "aes-128-cbc"}, false},
84 {"CBCVarTxt192", []string{"kat", "aes-192-cbc"}, false},
85 {"CBCVarTxt256", []string{"kat", "aes-256-cbc"}, false},
86 {"ECBGFSbox128", []string{"kat", "aes-128-ecb"}, false},
87 {"ECBGFSbox192", []string{"kat", "aes-192-ecb"}, false},
88 {"ECBGFSbox256", []string{"kat", "aes-256-ecb"}, false},
89 {"ECBKeySbox128", []string{"kat", "aes-128-ecb"}, false},
90 {"ECBKeySbox192", []string{"kat", "aes-192-ecb"}, false},
91 {"ECBKeySbox256", []string{"kat", "aes-256-ecb"}, false},
92 {"ECBMMT128", []string{"kat", "aes-128-ecb"}, false},
93 {"ECBMMT192", []string{"kat", "aes-192-ecb"}, false},
94 {"ECBMMT256", []string{"kat", "aes-256-ecb"}, false},
95 {"ECBVarKey128", []string{"kat", "aes-128-ecb"}, false},
96 {"ECBVarKey192", []string{"kat", "aes-192-ecb"}, false},
97 {"ECBVarKey256", []string{"kat", "aes-256-ecb"}, false},
98 {"ECBVarTxt128", []string{"kat", "aes-128-ecb"}, false},
99 {"ECBVarTxt192", []string{"kat", "aes-192-ecb"}, false},
100 {"ECBVarTxt256", []string{"kat", "aes-256-ecb"}, false},
101 // AES Monte-Carlo tests
102 {"ECBMCT128", []string{"mct", "aes-128-ecb"}, false},
103 {"ECBMCT192", []string{"mct", "aes-192-ecb"}, false},
104 {"ECBMCT256", []string{"mct", "aes-256-ecb"}, false},
105 {"CBCMCT128", []string{"mct", "aes-128-cbc"}, false},
106 {"CBCMCT192", []string{"mct", "aes-192-cbc"}, false},
107 {"CBCMCT256", []string{"mct", "aes-256-cbc"}, false},
108 },
109}
110
111var ecdsa2KeyPairTests = testSuite{
112 "ECDSA2",
113 "ecdsa2_keypair",
114 nil,
115 []test{{"KeyPair", nil, true}},
116}
117
118var ecdsa2PKVTests = testSuite{
119 "ECDSA2",
120 "ecdsa2_pkv",
121 nil,
122 []test{{"PKV", nil, false}},
123}
124
125var ecdsa2SigGenTests = testSuite{
126 "ECDSA2",
127 "ecdsa2_siggen",
128 nil,
129 []test{
130 {"SigGen", []string{"SigGen"}, true},
131 {"SigGenComponent", []string{"SigGenComponent"}, true},
132 },
133}
134
135var ecdsa2SigVerTests = testSuite{
136 "ECDSA2",
137 "ecdsa2_sigver",
138 nil,
139 []test{{"SigVer", nil, false}},
140}
141
142var rsa2KeyGenTests = testSuite{
143 "RSA2",
144 "rsa2_keygen",
145 nil,
146 []test{
147 {"KeyGen_RandomProbablyPrime3_3", nil, true},
148 },
149}
150
151var rsa2SigGenTests = testSuite{
152 "RSA2",
153 "rsa2_siggen",
154 nil,
155 []test{
156 {"SigGen15_186-3", []string{"pkcs15"}, true},
157 {"SigGenPSS_186-3", []string{"pss"}, true},
158 },
159}
160
161var rsa2SigVerTests = testSuite{
162 "RSA2",
163 "rsa2_sigver",
164 func(s *bufio.Scanner) bool {
165 for {
166 if !s.Scan() {
167 return false
168 }
169
170 line := s.Text()
171 if strings.HasPrefix(line, "p = ") || strings.HasPrefix(line, "d = ") || strings.HasPrefix(line, "SaltVal = ") || strings.HasPrefix(line, "EM with ") {
172 continue
173 }
174 if strings.HasPrefix(line, "q = ") {
175 // Skip the "q = " line and an additional blank line.
176 if !s.Scan() {
177 return false
178 }
179 if len(strings.TrimSpace(s.Text())) > 0 {
180 return false
181 }
182 continue
183 }
184 return true
185 }
186 },
187 []test{
188 {"SigVer15_186-3", []string{"pkcs15"}, false},
189 {"SigVerPSS_186-3", []string{"pss"}, false},
190 },
191}
192
193var hmacTests = testSuite{
194 "HMAC",
195 "hmac",
196 nil,
197 []test{{"HMAC", nil, false}},
198}
199
200var shaTests = testSuite{
201 "SHA",
202 "sha",
203 nil,
204 []test{
205 {"SHA1LongMsg", []string{"SHA1"}, false},
206 {"SHA1ShortMsg", []string{"SHA1"}, false},
207 {"SHA224LongMsg", []string{"SHA224"}, false},
208 {"SHA224ShortMsg", []string{"SHA224"}, false},
209 {"SHA256LongMsg", []string{"SHA256"}, false},
210 {"SHA256ShortMsg", []string{"SHA256"}, false},
211 {"SHA384LongMsg", []string{"SHA384"}, false},
212 {"SHA384ShortMsg", []string{"SHA384"}, false},
213 {"SHA512LongMsg", []string{"SHA512"}, false},
214 {"SHA512ShortMsg", []string{"SHA512"}, false},
215 },
216}
217
218var shaMonteTests = testSuite{
219 "SHA",
220 "sha_monte",
221 nil,
222 []test{
223 {"SHA1Monte", []string{"SHA1"}, false},
224 {"SHA224Monte", []string{"SHA224"}, false},
225 {"SHA256Monte", []string{"SHA256"}, false},
226 {"SHA384Monte", []string{"SHA384"}, false},
227 {"SHA512Monte", []string{"SHA512"}, false},
228 },
229}
230
231var ctrDRBGTests = testSuite{
232 "DRBG800-90A",
233 "ctr_drbg",
234 nil,
235 []test{{"CTR_DRBG", nil, false}},
236}
237
238var tdesTests = testSuite{
239 "TDES",
240 "tdes",
241 nil,
242 []test{
243 {"TCBCMMT2", []string{"kat", "des-ede-cbc"}, false},
244 {"TCBCMMT3", []string{"kat", "des-ede3-cbc"}, false},
245 {"TCBCMonte2", []string{"mct", "des-ede3-cbc"}, false},
246 {"TCBCMonte3", []string{"mct", "des-ede3-cbc"}, false},
247 {"TCBCinvperm", []string{"kat", "des-ede3-cbc"}, false},
248 {"TCBCpermop", []string{"kat", "des-ede3-cbc"}, false},
249 {"TCBCsubtab", []string{"kat", "des-ede3-cbc"}, false},
250 {"TCBCvarkey", []string{"kat", "des-ede3-cbc"}, false},
251 {"TCBCvartext", []string{"kat", "des-ede3-cbc"}, false},
252 {"TECBMMT2", []string{"kat", "des-ede"}, false},
253 {"TECBMMT3", []string{"kat", "des-ede3"}, false},
254 {"TECBMonte2", []string{"mct", "des-ede3"}, false},
255 {"TECBMonte3", []string{"mct", "des-ede3"}, false},
256 {"TECBinvperm", []string{"kat", "des-ede3"}, false},
257 {"TECBpermop", []string{"kat", "des-ede3"}, false},
258 {"TECBsubtab", []string{"kat", "des-ede3"}, false},
259 {"TECBvarkey", []string{"kat", "des-ede3"}, false},
260 {"TECBvartext", []string{"kat", "des-ede3"}, false},
261 },
262}
263
264var keyWrapTests = testSuite{
265 "KeyWrap38F",
266 "keywrap",
267 nil,
268 []test{
269 {"KW_AD_128", []string{"dec", "128"}, false},
270 {"KW_AD_256", []string{"dec", "256"}, false},
271 {"KW_AE_128", []string{"enc", "128"}, false},
272 {"KW_AE_256", []string{"enc", "256"}, false},
273 },
274}
275
276var allTestSuites = []*testSuite{
277 &aesGCMTests,
278 &aesTests,
279 &ctrDRBGTests,
280 &ecdsa2KeyPairTests,
281 &ecdsa2PKVTests,
282 &ecdsa2SigGenTests,
283 &ecdsa2SigVerTests,
284 &hmacTests,
285 &keyWrapTests,
286 &rsa2KeyGenTests,
287 &rsa2SigGenTests,
288 &rsa2SigVerTests,
289 &shaTests,
290 &shaMonteTests,
291 &tdesTests,
292}
293
294// testInstance represents a specific test in a testSuite.
295type testInstance struct {
296 suite *testSuite
297 testIndex int
298}
299
300func worker(wg *sync.WaitGroup, work <-chan testInstance) {
301 defer wg.Done()
302
303 for ti := range work {
304 test := ti.suite.tests[ti.testIndex]
305
306 if err := doTest(ti.suite, test); err != nil {
307 fmt.Fprintf(os.Stderr, "%s\n", err)
308 os.Exit(2)
309 }
310
311 if !*noFAX && !test.noFAX {
312 if err := compareFAX(ti.suite, test); err != nil {
313 fmt.Fprintf(os.Stderr, "%s\n", err)
314 os.Exit(3)
315 }
316 }
317 }
318}
319
320func main() {
321 flag.Parse()
322
323 if len(*oraclePath) == 0 {
324 fmt.Fprintf(os.Stderr, "Must give -oracle-bin\n")
325 os.Exit(1)
326 }
327
328 work := make(chan testInstance)
329 var wg sync.WaitGroup
330
331 for i := 0; i < runtime.NumCPU(); i++ {
332 wg.Add(1)
333 go worker(&wg, work)
334 }
335
336 for _, suite := range allTestSuites {
337 for i := range suite.tests {
338 work <- testInstance{suite, i}
339 }
340 }
341
342 close(work)
343 wg.Wait()
344}
345
346func doTest(suite *testSuite, test test) error {
347 args := []string{suite.suite}
348 args = append(args, test.args...)
349 args = append(args, filepath.Join(suite.getDirectory(), "req", test.inFile+".req"))
350
351 respDir := filepath.Join(suite.getDirectory(), "resp")
352 if err := os.Mkdir(respDir, 0755); err != nil && !os.IsExist(err) {
353 return fmt.Errorf("cannot create resp directory: %s", err)
354 }
355 outPath := filepath.Join(respDir, test.inFile+".rsp")
356 outFile, err := os.OpenFile(outPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
357 if err != nil {
358 return fmt.Errorf("cannot open output file for %q %q: %s", suite.getDirectory(), test.inFile, err)
359 }
360 defer outFile.Close()
361
362 cmd := exec.Command(*oraclePath, args...)
363 cmd.Stdout = outFile
364 cmd.Stderr = os.Stderr
365
366 cmdLine := strings.Join(append([]string{*oraclePath}, args...), " ")
367 startTime := time.Now()
368 if err := cmd.Run(); err != nil {
369 return fmt.Errorf("cannot run command for %q %q (%s): %s", suite.getDirectory(), test.inFile, cmdLine, err)
370 }
371
372 fmt.Printf("%s (%ds)\n", cmdLine, int(time.Since(startTime).Seconds()))
373
374 return nil
375}
376
377func canonicalizeLine(in string) string {
378 if strings.HasPrefix(in, "Result = P (") {
379 return "Result = P"
380 }
381 if strings.HasPrefix(in, "Result = F (") {
382 return "Result = F"
383 }
384 return in
385}
386
387func compareFAX(suite *testSuite, test test) error {
388 faxScanFunc := suite.faxScanFunc
389 if faxScanFunc == nil {
390 faxScanFunc = (*bufio.Scanner).Scan
391 }
392
393 respPath := filepath.Join(suite.getDirectory(), "resp", test.inFile+".rsp")
394 respFile, err := os.Open(respPath)
395 if err != nil {
396 return fmt.Errorf("cannot read output of %q %q: %s", suite.getDirectory(), test.inFile, err)
397 }
398 defer respFile.Close()
399
400 faxPath := filepath.Join(suite.getDirectory(), "fax", test.inFile+".fax")
401 faxFile, err := os.Open(faxPath)
402 if err != nil {
403 return fmt.Errorf("cannot open fax file for %q %q: %s", suite.getDirectory(), test.inFile, err)
404 }
405 defer faxFile.Close()
406
407 respScanner := bufio.NewScanner(respFile)
408 faxScanner := bufio.NewScanner(faxFile)
409
410 lineNo := 0
411 inHeader := true
412
413 for respScanner.Scan() {
414 lineNo++
415 respLine := respScanner.Text()
416 var faxLine string
417
418 if inHeader && (len(respLine) == 0 || respLine[0] == '#') {
419 continue
420 }
421
422 for {
423 haveFaxLine := false
424
425 if inHeader {
426 for faxScanFunc(faxScanner) {
427 faxLine = faxScanner.Text()
428 if len(faxLine) != 0 && faxLine[0] != '#' {
429 haveFaxLine = true
430 break
431 }
432 }
433
434 inHeader = false
435 } else {
436 if faxScanFunc(faxScanner) {
437 faxLine = faxScanner.Text()
438 haveFaxLine = true
439 }
440 }
441
442 if !haveFaxLine {
443 // Ignore blank lines at the end of the generated file.
444 if len(respLine) == 0 {
445 break
446 }
447 return fmt.Errorf("resp file is longer than fax for %q %q", suite.getDirectory(), test.inFile)
448 }
449
450 if strings.HasPrefix(faxLine, " (Reason: ") {
451 continue
452 }
453
454 break
455 }
456
457 if canonicalizeLine(faxLine) == canonicalizeLine(respLine) {
458 continue
459 }
460
461 return fmt.Errorf("resp and fax differ at line %d for %q %q: %q vs %q", lineNo, suite.getDirectory(), test.inFile, respLine, faxLine)
462 }
463
464 if faxScanFunc(faxScanner) {
465 return fmt.Errorf("fax file is longer than resp for %q %q", suite.getDirectory(), test.inFile)
466 }
467
468 return nil
469}