summaryrefslogtreecommitdiff
path: root/src/util/fipstools/cavp/run_cavp.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/fipstools/cavp/run_cavp.go')
-rw-r--r--src/util/fipstools/cavp/run_cavp.go590
1 files changed, 590 insertions, 0 deletions
diff --git a/src/util/fipstools/cavp/run_cavp.go b/src/util/fipstools/cavp/run_cavp.go
new file mode 100644
index 00000000..b37f4532
--- /dev/null
+++ b/src/util/fipstools/cavp/run_cavp.go
@@ -0,0 +1,590 @@
+// 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_192", []string{"dec", "192"}, false},
+ {"KW_AD_256", []string{"dec", "256"}, false},
+ {"KW_AE_128", []string{"enc", "128"}, false},
+ {"KW_AE_192", []string{"enc", "192"}, false},
+ {"KW_AE_256", []string{"enc", "256"}, false},
+ {"KWP_AD_128", []string{"dec-pad", "128"}, false},
+ {"KWP_AD_192", []string{"dec-pad", "192"}, false},
+ {"KWP_AD_256", []string{"dec-pad", "256"}, false},
+ {"KWP_AE_128", []string{"enc-pad", "128"}, false},
+ {"KWP_AE_192", []string{"enc-pad", "192"}, false},
+ {"KWP_AE_256", []string{"enc-pad", "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
+}