|
- // run_cavp.go processes CAVP input files and generates suitable response
- // files, optionally comparing the results against the provided FAX files.
- package main
-
- import (
- "bufio"
- "errors"
- "flag"
- "fmt"
- "os"
- "os/exec"
- "path"
- "path/filepath"
- "runtime"
- "strings"
- "sync"
- "time"
- )
-
- var (
- oraclePath = flag.String("oracle-bin", "", "Path to the oracle binary")
- suiteDir = flag.String("suite-dir", "", "Base directory containing the CAVP test suite")
- noFAX = flag.Bool("no-fax", false, "Skip comparing against FAX files")
- android = flag.Bool("android", false, "Run tests via ADB")
- )
-
- const (
- androidTmpPath = "/data/local/tmp/"
- androidCAVPPath = androidTmpPath + "cavp"
- androidLibCryptoPath = androidTmpPath + "libcrypto.so"
- )
-
- // test describes a single request file.
- type test struct {
- // inFile is the base of the filename without an extension, i.e.
- // “ECBMCT128”.
- inFile string
- // args are the arguments (not including the input filename) to the
- // oracle binary.
- args []string
- // noFAX, if true, indicates that the output cannot be compared against
- // the FAX file. (E.g. because the primitive is non-deterministic.)
- noFAX bool
- }
-
- // nextLineState can be used by FAX next-line function to store state.
- type nextLineState struct {
- // State used by the KAS test.
- nextIsIUTHash bool
- }
-
- // testSuite describes a series of tests that are handled by a single oracle
- // binary.
- type testSuite struct {
- // directory is the name of the directory in the CAVP input, i.e. “AES”.
- directory string
- // suite names the test suite to pass as the first command-line argument.
- suite string
- // nextLineFunc, if not nil, is the function used to read the next line
- // from the FAX file. This can be used to skip lines and/or mutate them
- // as needed. The second argument can be used by the scanner to store
- // state, if needed. If isWildcard is true on return then line is not
- // meaningful and any line from the response file should be accepted.
- nextLineFunc func(*bufio.Scanner, *nextLineState) (line string, isWildcard, ok bool)
- tests []test
- }
-
- func (t *testSuite) getDirectory() string {
- return filepath.Join(*suiteDir, t.directory)
- }
-
- var aesGCMTests = testSuite{
- "AES_GCM",
- "aes_gcm",
- nil,
- []test{
- {"gcmDecrypt128", []string{"dec", "aes-128-gcm"}, false},
- {"gcmDecrypt256", []string{"dec", "aes-256-gcm"}, false},
- {"gcmEncryptExtIV128", []string{"enc", "aes-128-gcm"}, false},
- {"gcmEncryptExtIV256", []string{"enc", "aes-256-gcm"}, false},
- },
- }
-
- var aesTests = testSuite{
- "AES",
- "aes",
- nil,
- []test{
- {"CBCGFSbox128", []string{"kat", "aes-128-cbc"}, false},
- {"CBCGFSbox192", []string{"kat", "aes-192-cbc"}, false},
- {"CBCGFSbox256", []string{"kat", "aes-256-cbc"}, false},
- {"CBCKeySbox128", []string{"kat", "aes-128-cbc"}, false},
- {"CBCKeySbox192", []string{"kat", "aes-192-cbc"}, false},
- {"CBCKeySbox256", []string{"kat", "aes-256-cbc"}, false},
- {"CBCMMT128", []string{"kat", "aes-128-cbc"}, false},
- {"CBCMMT192", []string{"kat", "aes-192-cbc"}, false},
- {"CBCMMT256", []string{"kat", "aes-256-cbc"}, false},
- {"CBCVarKey128", []string{"kat", "aes-128-cbc"}, false},
- {"CBCVarKey192", []string{"kat", "aes-192-cbc"}, false},
- {"CBCVarKey256", []string{"kat", "aes-256-cbc"}, false},
- {"CBCVarTxt128", []string{"kat", "aes-128-cbc"}, false},
- {"CBCVarTxt192", []string{"kat", "aes-192-cbc"}, false},
- {"CBCVarTxt256", []string{"kat", "aes-256-cbc"}, false},
- {"ECBGFSbox128", []string{"kat", "aes-128-ecb"}, false},
- {"ECBGFSbox192", []string{"kat", "aes-192-ecb"}, false},
- {"ECBGFSbox256", []string{"kat", "aes-256-ecb"}, false},
- {"ECBKeySbox128", []string{"kat", "aes-128-ecb"}, false},
- {"ECBKeySbox192", []string{"kat", "aes-192-ecb"}, false},
- {"ECBKeySbox256", []string{"kat", "aes-256-ecb"}, false},
- {"ECBMMT128", []string{"kat", "aes-128-ecb"}, false},
- {"ECBMMT192", []string{"kat", "aes-192-ecb"}, false},
- {"ECBMMT256", []string{"kat", "aes-256-ecb"}, false},
- {"ECBVarKey128", []string{"kat", "aes-128-ecb"}, false},
- {"ECBVarKey192", []string{"kat", "aes-192-ecb"}, false},
- {"ECBVarKey256", []string{"kat", "aes-256-ecb"}, false},
- {"ECBVarTxt128", []string{"kat", "aes-128-ecb"}, false},
- {"ECBVarTxt192", []string{"kat", "aes-192-ecb"}, false},
- {"ECBVarTxt256", []string{"kat", "aes-256-ecb"}, false},
- // AES Monte-Carlo tests
- {"ECBMCT128", []string{"mct", "aes-128-ecb"}, false},
- {"ECBMCT192", []string{"mct", "aes-192-ecb"}, false},
- {"ECBMCT256", []string{"mct", "aes-256-ecb"}, false},
- {"CBCMCT128", []string{"mct", "aes-128-cbc"}, false},
- {"CBCMCT192", []string{"mct", "aes-192-cbc"}, false},
- {"CBCMCT256", []string{"mct", "aes-256-cbc"}, false},
- },
- }
-
- var ecdsa2KeyPairTests = testSuite{
- "ECDSA2",
- "ecdsa2_keypair",
- nil,
- []test{{"KeyPair", nil, true}},
- }
-
- var ecdsa2PKVTests = testSuite{
- "ECDSA2",
- "ecdsa2_pkv",
- nil,
- []test{{"PKV", nil, false}},
- }
-
- var ecdsa2SigGenTests = testSuite{
- "ECDSA2",
- "ecdsa2_siggen",
- nil,
- []test{
- {"SigGen", []string{"SigGen"}, true},
- {"SigGenComponent", []string{"SigGenComponent"}, true},
- },
- }
-
- var ecdsa2SigVerTests = testSuite{
- "ECDSA2",
- "ecdsa2_sigver",
- nil,
- []test{{"SigVer", nil, false}},
- }
-
- var rsa2KeyGenTests = testSuite{
- "RSA2",
- "rsa2_keygen",
- nil,
- []test{
- {"KeyGen_RandomProbablyPrime3_3", nil, true},
- },
- }
-
- var rsa2SigGenTests = testSuite{
- "RSA2",
- "rsa2_siggen",
- nil,
- []test{
- {"SigGen15_186-3", []string{"pkcs15"}, true},
- {"SigGenPSS_186-3", []string{"pss"}, true},
- },
- }
-
- var rsa2SigVerTests = testSuite{
- "RSA2",
- "rsa2_sigver",
- func(s *bufio.Scanner, state *nextLineState) (string, bool, bool) {
- for {
- if !s.Scan() {
- return "", false, false
- }
-
- line := s.Text()
- if strings.HasPrefix(line, "p = ") || strings.HasPrefix(line, "d = ") || strings.HasPrefix(line, "SaltVal = ") || strings.HasPrefix(line, "EM with ") {
- continue
- }
- if strings.HasPrefix(line, "q = ") {
- // Skip the "q = " line and an additional blank line.
- if !s.Scan() ||
- len(strings.TrimSpace(s.Text())) > 0 {
- return "", false, false
- }
- continue
- }
- return line, false, true
- }
- },
- []test{
- {"SigVer15_186-3", []string{"pkcs15"}, false},
- {"SigVerPSS_186-3", []string{"pss"}, false},
- },
- }
-
- var hmacTests = testSuite{
- "HMAC",
- "hmac",
- nil,
- []test{{"HMAC", nil, false}},
- }
-
- var shaTests = testSuite{
- "SHA",
- "sha",
- nil,
- []test{
- {"SHA1LongMsg", []string{"SHA1"}, false},
- {"SHA1ShortMsg", []string{"SHA1"}, false},
- {"SHA224LongMsg", []string{"SHA224"}, false},
- {"SHA224ShortMsg", []string{"SHA224"}, false},
- {"SHA256LongMsg", []string{"SHA256"}, false},
- {"SHA256ShortMsg", []string{"SHA256"}, false},
- {"SHA384LongMsg", []string{"SHA384"}, false},
- {"SHA384ShortMsg", []string{"SHA384"}, false},
- {"SHA512LongMsg", []string{"SHA512"}, false},
- {"SHA512ShortMsg", []string{"SHA512"}, false},
- },
- }
-
- var shaMonteTests = testSuite{
- "SHA",
- "sha_monte",
- nil,
- []test{
- {"SHA1Monte", []string{"SHA1"}, false},
- {"SHA224Monte", []string{"SHA224"}, false},
- {"SHA256Monte", []string{"SHA256"}, false},
- {"SHA384Monte", []string{"SHA384"}, false},
- {"SHA512Monte", []string{"SHA512"}, false},
- },
- }
-
- var ctrDRBGTests = testSuite{
- "DRBG800-90A",
- "ctr_drbg",
- nil,
- []test{{"CTR_DRBG", nil, false}},
- }
-
- var tdesTests = testSuite{
- "TDES",
- "tdes",
- nil,
- []test{
- {"TCBCMMT2", []string{"kat", "des-ede-cbc"}, false},
- {"TCBCMMT3", []string{"kat", "des-ede3-cbc"}, false},
- {"TCBCMonte2", []string{"mct", "des-ede3-cbc"}, false},
- {"TCBCMonte3", []string{"mct", "des-ede3-cbc"}, false},
- {"TCBCinvperm", []string{"kat", "des-ede3-cbc"}, false},
- {"TCBCpermop", []string{"kat", "des-ede3-cbc"}, false},
- {"TCBCsubtab", []string{"kat", "des-ede3-cbc"}, false},
- {"TCBCvarkey", []string{"kat", "des-ede3-cbc"}, false},
- {"TCBCvartext", []string{"kat", "des-ede3-cbc"}, false},
- {"TECBMMT2", []string{"kat", "des-ede"}, false},
- {"TECBMMT3", []string{"kat", "des-ede3"}, false},
- {"TECBMonte2", []string{"mct", "des-ede3"}, false},
- {"TECBMonte3", []string{"mct", "des-ede3"}, false},
- {"TECBinvperm", []string{"kat", "des-ede3"}, false},
- {"TECBpermop", []string{"kat", "des-ede3"}, false},
- {"TECBsubtab", []string{"kat", "des-ede3"}, false},
- {"TECBvarkey", []string{"kat", "des-ede3"}, false},
- {"TECBvartext", []string{"kat", "des-ede3"}, false},
- },
- }
-
- var keyWrapTests = testSuite{
- "KeyWrap38F",
- "keywrap",
- nil,
- []test{
- {"KW_AD_128", []string{"dec", "128"}, false},
- {"KW_AD_256", []string{"dec", "256"}, false},
- {"KW_AE_128", []string{"enc", "128"}, false},
- {"KW_AE_256", []string{"enc", "256"}, false},
- },
- }
-
- var kasTests = testSuite{
- "KAS",
- "kas",
- func(s *bufio.Scanner, state *nextLineState) (line string, isWildcard, ok bool) {
- for {
- // If the response file will include the IUT hash next,
- // return a wildcard signal because this cannot be
- // matched against the FAX file.
- if state.nextIsIUTHash {
- state.nextIsIUTHash = false
- return "", true, true
- }
-
- if !s.Scan() {
- return "", false, false
- }
-
- line := s.Text()
- if strings.HasPrefix(line, "deCAVS = ") || strings.HasPrefix(line, "Z = ") {
- continue
- }
- if strings.HasPrefix(line, "CAVSHashZZ = ") {
- state.nextIsIUTHash = true
- }
- return line, false, true
- }
- },
- []test{
- {"KASFunctionTest_ECCEphemeralUnified_NOKC_ZZOnly_init", []string{"function"}, true},
- {"KASFunctionTest_ECCEphemeralUnified_NOKC_ZZOnly_resp", []string{"function"}, true},
- {"KASValidityTest_ECCEphemeralUnified_NOKC_ZZOnly_init", []string{"validity"}, false},
- {"KASValidityTest_ECCEphemeralUnified_NOKC_ZZOnly_resp", []string{"validity"}, false},
- },
- }
-
- var tlsKDFTests = testSuite{
- "KDF135",
- "tlskdf",
- nil,
- []test{
- {"tls", nil, false},
- },
- }
-
- var testSuites = []*testSuite{
- &aesGCMTests,
- &aesTests,
- &ctrDRBGTests,
- &ecdsa2KeyPairTests,
- &ecdsa2PKVTests,
- &ecdsa2SigGenTests,
- &ecdsa2SigVerTests,
- &hmacTests,
- &keyWrapTests,
- &rsa2KeyGenTests,
- &rsa2SigGenTests,
- &rsa2SigVerTests,
- &shaTests,
- &shaMonteTests,
- &tdesTests,
- &kasTests,
- &tlsKDFTests,
- }
-
- // testInstance represents a specific test in a testSuite.
- type testInstance struct {
- suite *testSuite
- testIndex int
- }
-
- func worker(wg *sync.WaitGroup, work <-chan testInstance) {
- defer wg.Done()
-
- for ti := range work {
- test := ti.suite.tests[ti.testIndex]
-
- if err := doTest(ti.suite, test); err != nil {
- fmt.Fprintf(os.Stderr, "%s\n", err)
- os.Exit(2)
- }
-
- if !*noFAX && !test.noFAX {
- if err := compareFAX(ti.suite, test); err != nil {
- fmt.Fprintf(os.Stderr, "%s\n", err)
- os.Exit(3)
- }
- }
- }
- }
-
- func checkAndroidPrereqs() error {
- // The cavp binary, and a matching libcrypto.so, are required to be placed
- // in /data/local/tmp before running this script.
- if err := exec.Command("adb", "shell", "ls", androidCAVPPath).Run(); err != nil {
- return errors.New("failed to list cavp binary; ensure that adb works and cavp binary is in place: " + err.Error())
- }
- if err := exec.Command("adb", "shell", "ls", androidLibCryptoPath).Run(); err != nil {
- return errors.New("failed to list libcrypto.so; ensure that library is in place: " + err.Error())
- }
- return nil
- }
-
- func main() {
- flag.Parse()
-
- if *android {
- if err := checkAndroidPrereqs(); err != nil {
- fmt.Fprintf(os.Stderr, "%s\n", err)
- os.Exit(1)
- }
- } else if len(*oraclePath) == 0 {
- fmt.Fprintf(os.Stderr, "Must give -oracle-bin\n")
- os.Exit(1)
- }
-
- work := make(chan testInstance)
- var wg sync.WaitGroup
-
- numWorkers := runtime.NumCPU()
- if *android {
- numWorkers = 1
- }
-
- for i := 0; i < numWorkers; i++ {
- wg.Add(1)
- go worker(&wg, work)
- }
-
- for _, suite := range testSuites {
- for i := range suite.tests {
- work <- testInstance{suite, i}
- }
- }
-
- close(work)
- wg.Wait()
- }
-
- func doTest(suite *testSuite, test test) error {
- bin := *oraclePath
- var args []string
-
- if *android {
- bin = "adb"
- args = []string{"shell", "LD_LIBRARY_PATH=" + androidTmpPath, androidCAVPPath}
- }
-
- args = append(args, suite.suite)
- args = append(args, test.args...)
- reqPath := filepath.Join(suite.getDirectory(), "req", test.inFile+".req")
- var reqPathOnDevice string
-
- if *android {
- reqPathOnDevice = path.Join(androidTmpPath, test.inFile+".req")
- if err := exec.Command("adb", "push", reqPath, reqPathOnDevice).Run(); err != nil {
- return errors.New("failed to push request file: " + err.Error())
- }
- args = append(args, reqPathOnDevice)
- } else {
- args = append(args, reqPath)
- }
-
- respDir := filepath.Join(suite.getDirectory(), "resp")
- if err := os.Mkdir(respDir, 0755); err != nil && !os.IsExist(err) {
- return fmt.Errorf("cannot create resp directory: %s", err)
- }
- outPath := filepath.Join(respDir, test.inFile+".rsp")
- outFile, err := os.OpenFile(outPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
- if err != nil {
- return fmt.Errorf("cannot open output file for %q %q: %s", suite.getDirectory(), test.inFile, err)
- }
- defer outFile.Close()
-
- cmd := exec.Command(bin, args...)
- cmd.Stdout = outFile
- cmd.Stderr = os.Stderr
-
- cmdLine := strings.Join(append([]string{bin}, args...), " ")
- startTime := time.Now()
- if err := cmd.Run(); err != nil {
- return fmt.Errorf("cannot run command for %q %q (%s): %s", suite.getDirectory(), test.inFile, cmdLine, err)
- }
-
- fmt.Printf("%s (%ds)\n", cmdLine, int(time.Since(startTime).Seconds()))
-
- if *android {
- exec.Command("adb", "shell", "rm", reqPathOnDevice).Run()
- }
-
- return nil
- }
-
- func canonicalizeLine(in string) string {
- if strings.HasPrefix(in, "Result = P (") {
- return "Result = P"
- }
- if strings.HasPrefix(in, "Result = F (") {
- return "Result = F"
- }
- return in
- }
-
- func compareFAX(suite *testSuite, test test) error {
- nextLineFunc := suite.nextLineFunc
- if nextLineFunc == nil {
- nextLineFunc = func(s *bufio.Scanner, state *nextLineState) (string, bool, bool) {
- if !s.Scan() {
- return "", false, false
- }
- return s.Text(), false, true
- }
- }
-
- respPath := filepath.Join(suite.getDirectory(), "resp", test.inFile+".rsp")
- respFile, err := os.Open(respPath)
- if err != nil {
- return fmt.Errorf("cannot read output of %q %q: %s", suite.getDirectory(), test.inFile, err)
- }
- defer respFile.Close()
-
- faxPath := filepath.Join(suite.getDirectory(), "fax", test.inFile+".fax")
- faxFile, err := os.Open(faxPath)
- if err != nil {
- return fmt.Errorf("cannot open fax file for %q %q: %s", suite.getDirectory(), test.inFile, err)
- }
- defer faxFile.Close()
-
- respScanner := bufio.NewScanner(respFile)
- faxScanner := bufio.NewScanner(faxFile)
- var nextLineState nextLineState
-
- lineNo := 0
- inHeader := true
-
- for respScanner.Scan() {
- lineNo++
- respLine := respScanner.Text()
- var faxLine string
- var isWildcard, ok bool
-
- if inHeader && (len(respLine) == 0 || respLine[0] == '#') {
- continue
- }
-
- for {
- haveFaxLine := false
-
- if inHeader {
- for {
- if faxLine, isWildcard, ok = nextLineFunc(faxScanner, &nextLineState); !ok {
- break
- }
- if len(faxLine) != 0 && faxLine[0] != '#' {
- haveFaxLine = true
- break
- }
- }
-
- inHeader = false
- } else {
- faxLine, isWildcard, haveFaxLine = nextLineFunc(faxScanner, &nextLineState)
- }
-
- if !haveFaxLine {
- // Ignore blank lines at the end of the generated file.
- if len(respLine) == 0 {
- break
- }
- return fmt.Errorf("resp file is longer than fax for %q %q", suite.getDirectory(), test.inFile)
- }
-
- if strings.HasPrefix(faxLine, " (Reason: ") {
- continue
- }
-
- break
- }
-
- if isWildcard || canonicalizeLine(faxLine) == canonicalizeLine(respLine) {
- continue
- }
-
- return fmt.Errorf("resp and fax differ at line %d for %q %q: %q vs %q", lineNo, suite.getDirectory(), test.inFile, respLine, faxLine)
- }
-
- if _, _, ok := nextLineFunc(faxScanner, &nextLineState); ok {
- return fmt.Errorf("fax file is longer than resp for %q %q", suite.getDirectory(), test.inFile)
- }
-
- return nil
- }
|