acvp: support saving vectors.

Since we have support for reading vectors from files, this change adds
support for saving them. There's no support for uploading the saved
vectors, rather it's just for quicker debugging since the NIST server is
taking over a minute to produce vectors at the moment and that's a
little frustrating to iterate with.

Change-Id: I5da8a084eb06b81aefa838b4e7ad8d529d1d31a6
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/43144
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/util/fipstools/acvp/acvptool/acvp.go b/util/fipstools/acvp/acvptool/acvp.go
index 4dec04f..feebed5 100644
--- a/util/fipstools/acvp/acvptool/acvp.go
+++ b/util/fipstools/acvp/acvptool/acvp.go
@@ -45,6 +45,7 @@
 	configFilename = flag.String("config", "config.json", "Location of the configuration JSON file")
 	jsonInputFile  = flag.String("json", "", "Location of a vector-set input file")
 	runFlag        = flag.String("run", "", "Name of primitive to run tests for")
+	fetchFlag      = flag.String("fetch", "", "Name of primitive to fetch vectors for")
 	wrapperPath    = flag.String("wrapper", "../../../../build/util/fipstools/acvp/modulewrapper/modulewrapper", "Path to the wrapper binary")
 )
 
@@ -331,9 +332,19 @@
 		os.Exit(0)
 	}
 
-	runAlgos := make(map[string]bool)
+	var requestedAlgosFlag string
+	if len(*runFlag) > 0 && len(*fetchFlag) > 0 {
+		log.Fatalf("cannot specify both -run and -fetch")
+	}
 	if len(*runFlag) > 0 {
-		for _, substr := range strings.Split(*runFlag, ",") {
+		requestedAlgosFlag = *runFlag
+	} else {
+		requestedAlgosFlag = *fetchFlag
+	}
+
+	runAlgos := make(map[string]bool)
+	if len(requestedAlgosFlag) > 0 {
+		for _, substr := range strings.Split(requestedAlgosFlag, ",") {
 			runAlgos[substr] = false
 		}
 	}
@@ -389,7 +400,7 @@
 		log.Fatalf("failed to login: %s", err)
 	}
 
-	if len(*runFlag) == 0 {
+	if len(requestedAlgosFlag) == 0 {
 		if interactiveModeSupported {
 			runInteractive(server, config)
 		} else {
@@ -423,6 +434,15 @@
 
 	log.Printf("Have vector sets %v", result.VectorSetURLs)
 
+	if len(*fetchFlag) > 0 {
+		os.Stdout.WriteString("[\n")
+		json.NewEncoder(os.Stdout).Encode(map[string]interface{}{
+			"url":           url,
+			"vectorSetUrls": result.VectorSetURLs,
+			"time":          time.Now().Format(time.RFC3339),
+		})
+	}
+
 	for _, setURL := range result.VectorSetURLs {
 		firstTime := true
 		for {
@@ -450,6 +470,12 @@
 				continue
 			}
 
+			if len(*fetchFlag) > 0 {
+				os.Stdout.WriteString(",\n")
+				os.Stdout.Write(vectorsBytes)
+				break
+			}
+
 			replyGroups, err := middle.Process(vectors.Algo, vectorsBytes)
 			if err != nil {
 				log.Printf("Failed: %s", err)
@@ -535,6 +561,11 @@
 		}
 	}
 
+	if len(*fetchFlag) > 0 {
+		os.Stdout.WriteString("]\n")
+		os.Exit(0)
+	}
+
 FetchResults:
 	for {
 		var results acvp.SessionResults