| // Copyright 2019 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Package wycheproof runs a set of the Wycheproof tests |
| // provided by https://github.com/google/wycheproof. |
| package wycheproof |
| |
| import ( |
| "crypto" |
| "crypto/x509" |
| "encoding/hex" |
| "encoding/json" |
| "fmt" |
| "io/ioutil" |
| "log" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "testing" |
| |
| _ "crypto/sha1" |
| _ "crypto/sha256" |
| _ "crypto/sha512" |
| ) |
| |
| const wycheproofModVer = "v0.0.0-20191219022705-2196000605e4" |
| |
| var wycheproofTestVectorsDir string |
| |
| func TestMain(m *testing.M) { |
| if _, err := exec.LookPath("go"); err != nil { |
| log.Printf("skipping test because 'go' command is unavailable: %v", err) |
| os.Exit(0) |
| } |
| |
| // Download the JSON test files from github.com/google/wycheproof |
| // using `go mod download -json` so the cached source of the testdata |
| // can be used in the following tests. |
| path := "github.com/google/wycheproof@" + wycheproofModVer |
| cmd := exec.Command("go", "mod", "download", "-json", path) |
| // TODO: enable the sumdb once the Trybots proxy supports it. |
| cmd.Env = append(os.Environ(), "GONOSUMDB=*") |
| output, err := cmd.Output() |
| if err != nil { |
| log.Fatalf("failed to run `go mod download -json %s`, output: %s", path, output) |
| } |
| var dm struct { |
| Dir string // absolute path to cached source root directory |
| } |
| if err := json.Unmarshal(output, &dm); err != nil { |
| log.Fatal(err) |
| } |
| // Now that the module has been downloaded, use the absolute path of the |
| // cached source as the root directory for all tests going forward. |
| wycheproofTestVectorsDir = filepath.Join(dm.Dir, "testvectors") |
| os.Exit(m.Run()) |
| } |
| |
| func readTestVector(t *testing.T, f string, dest interface{}) { |
| b, err := ioutil.ReadFile(filepath.Join(wycheproofTestVectorsDir, f)) |
| if err != nil { |
| t.Fatalf("failed to read json file: %v", err) |
| } |
| if err := json.Unmarshal(b, &dest); err != nil { |
| t.Fatalf("failed to unmarshal json file: %v", err) |
| } |
| } |
| |
| func decodeHex(s string) []byte { |
| b, err := hex.DecodeString(s) |
| if err != nil { |
| panic(err) |
| } |
| return b |
| } |
| |
| func decodePublicKey(der string) interface{} { |
| d := decodeHex(der) |
| pub, err := x509.ParsePKIXPublicKey(d) |
| if err != nil { |
| panic(fmt.Sprintf("failed to parse DER encoded public key: %v", err)) |
| } |
| return pub |
| } |
| |
| func parseHash(h string) crypto.Hash { |
| switch h { |
| case "SHA-1": |
| return crypto.SHA1 |
| case "SHA-256": |
| return crypto.SHA256 |
| case "SHA-224": |
| return crypto.SHA224 |
| case "SHA-384": |
| return crypto.SHA384 |
| case "SHA-512": |
| return crypto.SHA512 |
| case "SHA-512/224": |
| return crypto.SHA512_224 |
| case "SHA-512/256": |
| return crypto.SHA512_256 |
| default: |
| panic(fmt.Sprintf("could not identify SHA hash algorithm: %q", h)) |
| } |
| } |
| |
| // shouldPass returns whether or not the test should pass. |
| // flagsShouldPass is a map associated with whether or not |
| // a flag for an "acceptable" result should pass. |
| // Every possible flag value that's associated with an |
| // "acceptable" result should be explicitly specified, |
| // otherwise the test will panic. |
| func shouldPass(result string, flags []string, flagsShouldPass map[string]bool) bool { |
| switch result { |
| case "valid": |
| return true |
| case "invalid": |
| return false |
| case "acceptable": |
| for _, flag := range flags { |
| pass, ok := flagsShouldPass[flag] |
| if !ok { |
| panic(fmt.Sprintf("unspecified flag: %q", flag)) |
| } |
| if !pass { |
| return false |
| } |
| } |
| return true // There are no flags, or all are meant to pass. |
| default: |
| panic(fmt.Sprintf("unexpected result: %v", result)) |
| } |
| } |