• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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}
470