func main()

in src/util/fipstools/acvp/acvptool/acvp.go [289:655]


func main() {
	flag.Parse()

	var err error
	var middle Middle
	middle, err = subprocess.New(*wrapperPath)
	if err != nil {
		log.Fatalf("failed to initialise middle: %s", err)
	}
	defer middle.Close()

	configBytes, err := middle.Config()
	if err != nil {
		log.Fatalf("failed to get config from middle: %s", err)
	}

	var supportedAlgos []map[string]interface{}
	if err := json.Unmarshal(configBytes, &supportedAlgos); err != nil {
		log.Fatalf("failed to parse configuration from Middle: %s", err)
	}

	if *dumpRegcap {
		nonTestAlgos := make([]map[string]interface{}, 0, len(supportedAlgos))
		for _, algo := range supportedAlgos {
			if value, ok := algo["acvptoolTestOnly"]; ok {
				testOnly, ok := value.(bool)
				if !ok {
					log.Fatalf("modulewrapper config contains acvptoolTestOnly field with non-boolean value %#v", value)
				}
				if testOnly {
					continue
				}
			}
			nonTestAlgos = append(nonTestAlgos, algo)
		}

		regcap := []map[string]interface{}{
			map[string]interface{}{"acvVersion": "1.0"},
			map[string]interface{}{"algorithms": nonTestAlgos},
		}
		regcapBytes, err := json.MarshalIndent(regcap, "", "    ")
		if err != nil {
			log.Fatalf("failed to marshal regcap: %s", err)
		}
		os.Stdout.Write(regcapBytes)
		os.Stdout.WriteString("\n")
		os.Exit(0)
	}

	if len(*jsonInputFile) > 0 {
		if err := processFile(*jsonInputFile, supportedAlgos, middle); err != nil {
			log.Fatalf("failed to process input file: %s", err)
		}
		os.Exit(0)
	}

	var config Config
	if err := jsonFromFile(&config, *configFilename); err != nil {
		log.Fatalf("Failed to load config file: %s", err)
	}

	if len(config.TOTPSecret) == 0 {
		log.Fatal("Config file missing TOTPSecret")
	}
	totpSecret, err := base64.StdEncoding.DecodeString(config.TOTPSecret)
	if err != nil {
		log.Fatalf("Failed to base64-decode TOTP secret from config file: %s. (Note that the secret _itself_ should be in the config, not the name of a file that contains it.)", err)
	}

	if len(config.CertPEMFile) == 0 {
		log.Fatal("Config file missing CertPEMFile")
	}
	certPEM, err := ioutil.ReadFile(config.CertPEMFile)
	if err != nil {
		log.Fatalf("failed to read certificate from %q: %s", config.CertPEMFile, err)
	}
	block, _ := pem.Decode(certPEM)
	certDER := block.Bytes

	if len(config.PrivateKeyDERFile) == 0 && len(config.PrivateKeyFile) == 0 {
		log.Fatal("Config file missing PrivateKeyDERFile and PrivateKeyFile")
	}
	if len(config.PrivateKeyDERFile) != 0 && len(config.PrivateKeyFile) != 0 {
		log.Fatal("Config file has both PrivateKeyDERFile and PrivateKeyFile. Can only have one.")
	}
	privateKeyFile := config.PrivateKeyDERFile
	if len(config.PrivateKeyFile) > 0 {
		privateKeyFile = config.PrivateKeyFile
	}

	keyBytes, err := ioutil.ReadFile(privateKeyFile)
	if err != nil {
		log.Fatalf("failed to read private key from %q: %s", privateKeyFile, err)
	}

	var keyDER []byte
	pemBlock, _ := pem.Decode(keyBytes)
	if pemBlock != nil {
		keyDER = pemBlock.Bytes
	} else {
		keyDER = keyBytes
	}

	var certKey crypto.PrivateKey
	if certKey, err = x509.ParsePKCS1PrivateKey(keyDER); err != nil {
		if certKey, err = x509.ParsePKCS8PrivateKey(keyDER); err != nil {
			log.Fatalf("failed to parse private key from %q: %s", privateKeyFile, err)
		}
	}

	var requestedAlgosFlag string
	if len(*runFlag) > 0 && len(*fetchFlag) > 0 {
		log.Fatalf("cannot specify both -run and -fetch")
	}
	if len(*runFlag) > 0 {
		requestedAlgosFlag = *runFlag
	} else {
		requestedAlgosFlag = *fetchFlag
	}

	runAlgos := make(map[string]bool)
	if len(requestedAlgosFlag) > 0 {
		for _, substr := range strings.Split(requestedAlgosFlag, ",") {
			runAlgos[substr] = false
		}
	}

	var algorithms []map[string]interface{}
	for _, supportedAlgo := range supportedAlgos {
		algoInterface, ok := supportedAlgo["algorithm"]
		if !ok {
			continue
		}

		algo, ok := algoInterface.(string)
		if !ok {
			continue
		}

		if _, ok := runAlgos[algo]; ok {
			algorithms = append(algorithms, supportedAlgo)
			runAlgos[algo] = true
		}
	}

	for algo, recognised := range runAlgos {
		if !recognised {
			log.Fatalf("requested algorithm %q was not recognised", algo)
		}
	}

	if len(config.ACVPServer) == 0 {
		config.ACVPServer = "https://demo.acvts.nist.gov/"
	}
	server := acvp.NewServer(config.ACVPServer, config.LogFile, [][]byte{certDER}, certKey, func() string {
		return TOTP(totpSecret[:])
	})

	var sessionTokensCacheDir string
	if len(config.SessionTokensCache) > 0 {
		sessionTokensCacheDir = config.SessionTokensCache
		if strings.HasPrefix(sessionTokensCacheDir, "~/") {
			home := os.Getenv("HOME")
			if len(home) == 0 {
				log.Fatal("~ used in config file but $HOME not set")
			}
			sessionTokensCacheDir = filepath.Join(home, sessionTokensCacheDir[2:])
		}

		if err := loadCachedSessionTokens(server, sessionTokensCacheDir); err != nil {
			log.Fatal(err)
		}
	}

	if err := server.Login(); err != nil {
		log.Fatalf("failed to login: %s", err)
	}

	if len(requestedAlgosFlag) == 0 {
		if interactiveModeSupported {
			runInteractive(server, config)
		} else {
			log.Fatalf("no arguments given but interactive mode not supported")
		}
		return
	}

	requestBytes, err := json.Marshal(acvp.TestSession{
		IsSample:    true,
		Publishable: false,
		Algorithms:  algorithms,
	})
	if err != nil {
		log.Fatalf("Failed to serialise JSON: %s", err)
	}

	var result acvp.TestSession
	if err := server.Post(&result, "acvp/v1/testSessions", requestBytes); err != nil {
		log.Fatalf("Request to create test session failed: %s", err)
	}

	url := trimLeadingSlash(result.URL)
	log.Printf("Created test session %q", url)
	if token := result.AccessToken; len(token) > 0 {
		server.PrefixTokens[url] = token
		if len(sessionTokensCacheDir) > 0 {
			ioutil.WriteFile(filepath.Join(sessionTokensCacheDir, neturl.PathEscape(url))+".token", []byte(token), 0600)
		}
	}

	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 {
			if firstTime {
				log.Printf("Fetching test vectors %q", setURL)
				firstTime = false
			}

			vectorsBytes, err := server.GetBytes(trimLeadingSlash(setURL))
			if err != nil {
				log.Fatalf("Failed to fetch vector set %q: %s", setURL, err)
			}

			var vectors acvp.Vectors
			if err := json.Unmarshal(vectorsBytes, &vectors); err != nil {
				log.Fatalf("Failed to parse vector set from %q: %s", setURL, err)
			}

			if retry := vectors.Retry; retry > 0 {
				log.Printf("Server requested %d seconds delay", retry)
				if retry > 10 {
					retry = 10
				}
				time.Sleep(time.Duration(retry) * time.Second)
				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)
				log.Printf("Deleting test set")
				server.Delete(url)
				os.Exit(1)
			}

			headerBytes, err := json.Marshal(acvp.Vectors{
				ID:   vectors.ID,
				Algo: vectors.Algo,
			})
			if err != nil {
				log.Printf("Failed to marshal result: %s", err)
				log.Printf("Deleting test set")
				server.Delete(url)
				os.Exit(1)
			}

			var resultBuf bytes.Buffer
			resultBuf.Write(headerBytes[:len(headerBytes)-1])
			resultBuf.WriteString(`,"testGroups":`)
			replyBytes, err := json.Marshal(replyGroups)
			if err != nil {
				log.Printf("Failed to marshal result: %s", err)
				log.Printf("Deleting test set")
				server.Delete(url)
				os.Exit(1)
			}
			resultBuf.Write(replyBytes)
			resultBuf.WriteString("}")

			resultData := resultBuf.Bytes()
			resultSize := uint64(len(resultData)) + 32 /* for framing overhead */
			if server.SizeLimit > 0 && resultSize >= server.SizeLimit {
				// The NIST ACVP server no longer requires the large-upload process,
				// suggesting that it may no longer be needed.
				log.Printf("Result is %d bytes, too much given server limit of %d bytes. Using large-upload process.", resultSize, server.SizeLimit)
				largeRequestBytes, err := json.Marshal(acvp.LargeUploadRequest{
					Size: resultSize,
					URL:  setURL,
				})
				if err != nil {
					log.Printf("Failed to marshal large-upload request: %s", err)
					log.Printf("Deleting test set")
					server.Delete(url)
					os.Exit(1)
				}

				var largeResponse acvp.LargeUploadResponse
				if err := server.Post(&largeResponse, "/large", largeRequestBytes); err != nil {
					log.Fatalf("Failed to request large-upload endpoint: %s", err)
				}

				log.Printf("Directed to large-upload endpoint at %q", largeResponse.URL)
				client := &http.Client{}
				req, err := http.NewRequest("POST", largeResponse.URL, bytes.NewBuffer(resultData))
				if err != nil {
					log.Fatalf("Failed to create POST request: %s", err)
				}
				token := largeResponse.AccessToken
				if len(token) == 0 {
					token = server.AccessToken
				}
				req.Header.Add("Authorization", "Bearer "+token)
				req.Header.Add("Content-Type", "application/json")
				resp, err := client.Do(req)
				if err != nil {
					log.Fatalf("Failed writing large upload: %s", err)
				}
				resp.Body.Close()
				if resp.StatusCode != 200 {
					log.Fatalf("Large upload resulted in status code %d", resp.StatusCode)
				}
			} else {
				log.Printf("Result size %d bytes", resultSize)
				if err := server.Post(nil, trimLeadingSlash(setURL)+"/results", resultData); err != nil {
					log.Fatalf("Failed to upload results: %s\n", err)
				}
			}

			break
		}
	}

	if len(*fetchFlag) > 0 {
		os.Stdout.WriteString("]\n")
		os.Exit(0)
	}

FetchResults:
	for {
		var results acvp.SessionResults
		if err := server.Get(&results, trimLeadingSlash(url)+"/results"); err != nil {
			log.Fatalf("Failed to fetch session results: %s", err)
		}

		if results.Passed {
			log.Print("Test passed")
			break
		}

		for _, result := range results.Results {
			if result.Status == "incomplete" {
				log.Print("Server hasn't finished processing results. Waiting 10 seconds.")
				time.Sleep(10 * time.Second)
				continue FetchResults
			}
		}

		log.Fatalf("Server did not accept results: %#v", results)
	}
}