acvptool: add support for uploading results.

See the update to ACVP.md for documentation but this now allows running
a test to be broken down into separate commands for each step: fetching,
processing, and uploading.

Change-Id: Id86d1cd0b07fcc9bdc6c665072b511da0832bdde
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/55608
Reviewed-by: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
fips-20230428
Adam Langley 2 years ago committed by Boringssl LUCI CQ
parent d77fdbff01
commit 1740ff90a7
  1. 8
      util/fipstools/acvp/ACVP.md
  2. 99
      util/fipstools/acvp/acvptool/acvp.go

@ -251,4 +251,10 @@ The current list of objects is:
In online mode, a given algorithm can be run by using the `-run` option. For example, `-run SHA2-256`. This will fetch a vector set, have the module-under-test answer it, and upload the answer. If you want to just fetch the vector set for later use with the `-json` option (documented above) then you can use `-fetch` instead of `-run`. The `-fetch` option also supports passing `-expected-out <filename>` to fetch and write the expected results, if the server supports that.
The tool doesn't currently support the sorts of operations that a lab would need, like uploading results from a file.
After results have been produced with `-json`, they can be uploaded with `-upload`. So `-run` is effectively these three steps combined:
```
./acvptool -fetch SHA2-256 > request
./acvptool -json request > result
./acvptool -upload result
```

@ -45,6 +45,7 @@ var (
dumpRegcap = flag.Bool("regcap", false, "Print module capabilities JSON to stdout")
configFilename = flag.String("config", "config.json", "Location of the configuration JSON file")
jsonInputFile = flag.String("json", "", "Location of a vector-set input file")
uploadInputFile = flag.String("upload", "", "Location of a JSON results file to upload")
runFlag = flag.String("run", "", "Name of primitive to run tests for")
fetchFlag = flag.String("fetch", "", "Name of primitive to fetch vectors for")
expectedOutFlag = flag.String("expected-out", "", "Name of a file to write the expected results to")
@ -179,12 +180,12 @@ func trimLeadingSlash(s string) string {
return s
}
// looksLikeHeaderElement returns true iff element looks like it's a header, not
// a test. Some ACVP files contain a header as the first element that should be
// duplicated into the response, and some don't. If the element contains
// a "url" field, or if it's missing an "algorithm" field, then we guess that
// it's a header.
func looksLikeHeaderElement(element json.RawMessage) bool {
// looksLikeVectorSetHeader returns true iff element looks like it's a
// vectorSetHeader, not a test. Some ACVP files contain a header as the first
// element that should be duplicated into the response, and some don't. If the
// element contains a "url" field, or if it's missing an "algorithm" field,
// then we guess that it's a header.
func looksLikeVectorSetHeader(element json.RawMessage) bool {
var headerFields struct {
URL string `json:"url"`
Algorithm string `json:"algorithm"`
@ -214,7 +215,7 @@ func processFile(filename string, supportedAlgos []map[string]interface{}, middl
}
var header json.RawMessage
if looksLikeHeaderElement(elements[0]) {
if looksLikeVectorSetHeader(elements[0]) {
header, elements = elements[0], elements[1:]
if len(elements) == 0 {
return errors.New("JSON input is empty")
@ -269,6 +270,7 @@ func processFile(filename string, supportedAlgos []map[string]interface{}, middl
group := map[string]interface{}{
"vsId": commonFields.ID,
"testGroups": replyGroups,
"algorithm": algo,
}
replyBytes, err := json.MarshalIndent(group, "", " ")
if err != nil {
@ -454,6 +456,76 @@ FetchResults:
}
}
// vectorSetHeader is the first element in the array of JSON elements that makes
// up the on-disk format for a vector set.
type vectorSetHeader struct {
URL string `json:"url,omitempty"`
VectorSetURLs []string `json:"vectorSetUrls,omitempty"`
Time string `json:"time,omitempty"`
}
func uploadFromFile(file string, config *Config, sessionTokensCacheDir string) {
if len(*jsonInputFile) > 0 {
log.Fatalf("-upload cannot be used with -json")
}
if len(*runFlag) > 0 {
log.Fatalf("-upload cannot be used with -run")
}
if len(*fetchFlag) > 0 {
log.Fatalf("-upload cannot be used with -fetch")
}
if len(*expectedOutFlag) > 0 {
log.Fatalf("-upload cannot be used with -expected-out")
}
if *dumpRegcap {
log.Fatalf("-upload cannot be used with -regcap")
}
in, err := os.Open(file)
if err != nil {
log.Fatalf("Cannot open input: %s", err)
}
defer in.Close()
decoder := json.NewDecoder(in)
var input []json.RawMessage
if err := decoder.Decode(&input); err != nil {
log.Fatalf("Failed to parse input: %s", err)
}
if len(input) < 2 {
log.Fatalf("Input JSON has fewer than two elements")
}
var header vectorSetHeader
if err := json.Unmarshal(input[0], &header); err != nil {
log.Fatalf("Failed to parse input header: %s", err)
}
if numGroups := len(input) - 1; numGroups != len(header.VectorSetURLs) {
log.Fatalf("have %d URLs from header, but only %d result groups", len(header.VectorSetURLs), numGroups)
}
server, err := connect(config, sessionTokensCacheDir)
if err != nil {
log.Fatal(err)
}
for i, url := range header.VectorSetURLs {
log.Printf("Uploading result for %q", url)
if err := uploadResult(server, url, input[i+1]); err != nil {
log.Fatalf("Failed to upload: %s", err)
}
}
if ok, err := getResultsWithRetry(server, header.URL); err != nil {
log.Fatal(err)
} else if !ok {
os.Exit(1)
}
}
func main() {
flag.Parse()
@ -474,6 +546,11 @@ func main() {
}
}
if len(*uploadInputFile) > 0 {
uploadFromFile(*uploadInputFile, &config, sessionTokensCacheDir)
return
}
middle, err := subprocess.New(*wrapperPath)
if err != nil {
log.Fatalf("failed to initialise middle: %s", err)
@ -629,10 +706,10 @@ func main() {
if len(*fetchFlag) > 0 {
io.WriteString(fetchOutputTee, "[\n")
json.NewEncoder(fetchOutputTee).Encode(map[string]interface{}{
"url": url,
"vectorSetUrls": result.VectorSetURLs,
"time": time.Now().Format(time.RFC3339),
json.NewEncoder(fetchOutputTee).Encode(vectorSetHeader{
URL: url,
VectorSetURLs: result.VectorSetURLs,
Time: time.Now().Format(time.RFC3339),
})
}

Loading…
Cancel
Save