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