Home | History | Annotate | Download | only in fipstools
      1 // run_cavp.go processes CAVP input files and generates suitable response
      2 // files, optionally comparing the results against the provided FAX files.
      3 package main
      4 
      5 import (
      6 	"bufio"
      7 	"flag"
      8 	"fmt"
      9 	"os"
     10 	"os/exec"
     11 	"path/filepath"
     12 	"runtime"
     13 	"strings"
     14 	"sync"
     15 	"time"
     16 )
     17 
     18 var (
     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.
     25 type 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.
     39 type 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 
     50 func (t *testSuite) getDirectory() string {
     51 	return filepath.Join(*suiteDir, t.directory)
     52 }
     53 
     54 var 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 
     66 var 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 
    111 var ecdsa2KeyPairTests = testSuite{
    112 	"ECDSA2",
    113 	"ecdsa2_keypair",
    114 	nil,
    115 	[]test{{"KeyPair", nil, true}},
    116 }
    117 
    118 var ecdsa2PKVTests = testSuite{
    119 	"ECDSA2",
    120 	"ecdsa2_pkv",
    121 	nil,
    122 	[]test{{"PKV", nil, false}},
    123 }
    124 
    125 var ecdsa2SigGenTests = testSuite{
    126 	"ECDSA2",
    127 	"ecdsa2_siggen",
    128 	nil,
    129 	[]test{
    130 		{"SigGen", []string{"SigGen"}, true},
    131 		{"SigGenComponent", []string{"SigGenComponent"}, true},
    132 	},
    133 }
    134 
    135 var ecdsa2SigVerTests = testSuite{
    136 	"ECDSA2",
    137 	"ecdsa2_sigver",
    138 	nil,
    139 	[]test{{"SigVer", nil, false}},
    140 }
    141 
    142 var rsa2KeyGenTests = testSuite{
    143 	"RSA2",
    144 	"rsa2_keygen",
    145 	nil,
    146 	[]test{
    147 		{"KeyGen_RandomProbablyPrime3_3", nil, true},
    148 	},
    149 }
    150 
    151 var 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 
    161 var 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 
    193 var hmacTests = testSuite{
    194 	"HMAC",
    195 	"hmac",
    196 	nil,
    197 	[]test{{"HMAC", nil, false}},
    198 }
    199 
    200 var 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 
    218 var 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 
    231 var ctrDRBGTests = testSuite{
    232 	"DRBG800-90A",
    233 	"ctr_drbg",
    234 	nil,
    235 	[]test{{"CTR_DRBG", nil, false}},
    236 }
    237 
    238 var 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 
    264 var 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 
    276 var 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.
    295 type testInstance struct {
    296 	suite     *testSuite
    297 	testIndex int
    298 }
    299 
    300 func 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 
    320 func 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 
    346 func 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 
    377 func 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 
    387 func 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