Add tool to run CAVP tests.
Change-Id: If503b65de2879186b23ad148363b8ec8be4c611c
Reviewed-on: https://boringssl-review.googlesource.com/15644
Reviewed-by: Martin Kreichgauer <martinkr@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/fipsoracle/run_cavp.go b/crypto/fipsoracle/run_cavp.go
new file mode 100644
index 0000000..88ef35d
--- /dev/null
+++ b/crypto/fipsoracle/run_cavp.go
@@ -0,0 +1,226 @@
+// 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"
+ "flag"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+)
+
+var (
+ binaryDir = flag.String("bin-dir", "", "Directory containing fipsoracle binaries")
+)
+
+// 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
+}
+
+// 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
+ // binary is the name of the binary that can process these tests.
+ binary string
+ tests []test
+}
+
+var aesGCMTests = testSuite{
+ "AES_GCM",
+ "cavp_aes_gcm_test",
+ []test{
+ {"gcmDecrypt128", []string{"dec", "aes-128-gcm"}, false},
+ {"gcmDecrypt256", []string{"dec", "aes-256-gcm"}, false},
+ {"gcmEncryptIntIV128", []string{"enc", "aes-128-gcm"}, true},
+ {"gcmEncryptIntIV256", []string{"enc", "aes-256-gcm"}, true},
+ },
+}
+
+var aesTests = testSuite{
+ "AES",
+ "cavp_aes_test",
+ []test{
+ {"CBCGFSbox128", []string{"aes-128-cbc"}, false},
+ {"CBCGFSbox192", []string{"aes-192-cbc"}, false},
+ {"CBCGFSbox256", []string{"aes-256-cbc"}, false},
+ {"CBCKeySbox128", []string{"aes-128-cbc"}, false},
+ {"CBCKeySbox192", []string{"aes-192-cbc"}, false},
+ {"CBCKeySbox256", []string{"aes-256-cbc"}, false},
+ {"CBCMMT128", []string{"aes-128-cbc"}, false},
+ {"CBCMMT192", []string{"aes-192-cbc"}, false},
+ {"CBCMMT256", []string{"aes-256-cbc"}, false},
+ {"CBCVarKey128", []string{"aes-128-cbc"}, false},
+ {"CBCVarKey192", []string{"aes-192-cbc"}, false},
+ {"CBCVarKey256", []string{"aes-256-cbc"}, false},
+ {"CBCVarTxt128", []string{"aes-128-cbc"}, false},
+ {"CBCVarTxt192", []string{"aes-192-cbc"}, false},
+ {"CBCVarTxt256", []string{"aes-256-cbc"}, false},
+ {"ECBGFSbox128", []string{"aes-128-ecb"}, false},
+ {"ECBGFSbox192", []string{"aes-192-ecb"}, false},
+ {"ECBGFSbox256", []string{"aes-256-ecb"}, false},
+ {"ECBKeySbox128", []string{"aes-128-ecb"}, false},
+ {"ECBKeySbox192", []string{"aes-192-ecb"}, false},
+ {"ECBKeySbox256", []string{"aes-256-ecb"}, false},
+ {"ECBMMT128", []string{"aes-128-ecb"}, false},
+ {"ECBMMT192", []string{"aes-192-ecb"}, false},
+ {"ECBMMT256", []string{"aes-256-ecb"}, false},
+ {"ECBVarKey128", []string{"aes-128-ecb"}, false},
+ {"ECBVarKey192", []string{"aes-192-ecb"}, false},
+ {"ECBVarKey256", []string{"aes-256-ecb"}, false},
+ {"ECBVarTxt128", []string{"aes-128-ecb"}, false},
+ {"ECBVarTxt192", []string{"aes-192-ecb"}, false},
+ {"ECBVarTxt256", []string{"aes-256-ecb"}, false},
+ },
+}
+
+// AES Monte-Carlo tests need a different binary.
+//{"ECBMCT128", []string{"aes-128-ecb"}, false},
+//{"ECBMCT192", []string{"aes-192-ecb"}, false},
+//{"ECBMCT256", []string{"aes-256-ecb"}, false},
+//{"CBCMCT128", []string{"aes-128-cbc"}, false},
+//{"CBCMCT192", []string{"aes-192-cbc"}, false},
+//{"CBCMCT256", []string{"aes-256-cbc"}, false},
+
+var allTestSuites = []*testSuite{
+ &aesGCMTests,
+ &aesTests,
+}
+
+func main() {
+ flag.Parse()
+
+ if len(*binaryDir) == 0 {
+ fmt.Fprintf(os.Stderr, "Must give -bin-dir\n")
+ os.Exit(1)
+ }
+
+ for _, suite := range allTestSuites {
+ for _, test := range suite.tests {
+ if err := doTest(suite, test); err != nil {
+ fmt.Fprintf(os.Stderr, "%s\n", err)
+ os.Exit(2)
+ }
+
+ if !test.noFAX {
+ if err := compareFAX(suite, test); err != nil {
+ fmt.Fprintf(os.Stderr, "%s\n", err)
+ os.Exit(3)
+ }
+ }
+ }
+ }
+}
+
+func doTest(suite *testSuite, test test) error {
+ binary := filepath.Join(*binaryDir, suite.binary)
+
+ var args []string
+ args = append(args, test.args...)
+ args = append(args, filepath.Join(suite.directory, "req", test.inFile+".req"))
+
+ outPath := filepath.Join(suite.directory, "resp", test.inFile+".resp")
+ 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.directory, test.inFile, err)
+ }
+ defer outFile.Close()
+
+ cmd := exec.Command(binary, args...)
+ cmd.Stdout = outFile
+ cmd.Stderr = os.Stderr
+
+ if err := cmd.Run(); err != nil {
+ return fmt.Errorf("cannot run command for %q %q: %s", suite.directory, test.inFile, err)
+ }
+
+ return nil
+}
+
+func compareFAX(suite *testSuite, test test) error {
+ respPath := filepath.Join(suite.directory, "resp", test.inFile+".resp")
+ respFile, err := os.Open(respPath)
+ if err != nil {
+ return fmt.Errorf("cannot read output of %q %q: %s", suite.directory, test.inFile, err)
+ }
+ defer respFile.Close()
+
+ faxPath := filepath.Join(suite.directory, "fax", test.inFile+".fax")
+ faxFile, err := os.Open(faxPath)
+ if err != nil {
+ return fmt.Errorf("cannot open fax file for %q %q: %s", suite.directory, test.inFile, err)
+ }
+ defer faxFile.Close()
+
+ respScanner := bufio.NewScanner(respFile)
+ faxScanner := bufio.NewScanner(faxFile)
+
+ lineNo := 0
+ inHeader := true
+
+ for respScanner.Scan() {
+ lineNo++
+ respLine := respScanner.Text()
+ var faxLine string
+
+ if inHeader && (len(respLine) == 0 || respLine[0] == '#') {
+ continue
+ }
+
+ for {
+ haveFaxLine := false
+
+ if inHeader {
+ for faxScanner.Scan() {
+ faxLine = faxScanner.Text()
+ if len(faxLine) != 0 && faxLine[0] != '#' {
+ haveFaxLine = true
+ break
+ }
+ }
+
+ inHeader = false
+ } else {
+ if faxScanner.Scan() {
+ faxLine = faxScanner.Text()
+ haveFaxLine = true
+ }
+ }
+
+ if !haveFaxLine {
+ return fmt.Errorf("resp file is longer than fax for %q %q", suite.directory, test.inFile)
+ }
+
+ if strings.HasPrefix(faxLine, " (Reason: ") {
+ continue
+ }
+
+ break
+ }
+
+ if faxLine == respLine {
+ continue
+ }
+
+ return fmt.Errorf("resp and fax differ at line %d for %q %q: %q vs %q", lineNo, suite.directory, test.inFile, respLine, faxLine)
+ }
+
+ if faxScanner.Scan() {
+ return fmt.Errorf("fax file is longer than resp for %q %q", suite.directory, test.inFile)
+ }
+
+ return nil
+}