github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/crypto/ed25519/ed25519vectors_test.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ed25519_test 6 7 import ( 8 "crypto/ed25519" 9 "encoding/hex" 10 "encoding/json" 11 "internal/testenv" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "testing" 16 ) 17 18 // TestEd25519Vectors runs a very large set of test vectors that exercise all 19 // combinations of low-order points, low-order components, and non-canonical 20 // encodings. These vectors lock in unspecified and spec-divergent behaviors in 21 // edge cases that are not security relevant in most contexts, but that can 22 // cause issues in consensus applications if changed. 23 // 24 // Our behavior matches the "classic" unwritten verification rules of the 25 // "ref10" reference implementation. 26 // 27 // Note that although we test for these edge cases, they are not covered by the 28 // Go 1 Compatibility Promise. Applications that need stable verification rules 29 // should use github.com/hdevalence/ed25519consensus. 30 // 31 // See https://hdevalence.ca/blog/2020-10-04-its-25519am for more details. 32 func TestEd25519Vectors(t *testing.T) { 33 jsonVectors := downloadEd25519Vectors(t) 34 var vectors []struct { 35 A, R, S, M string 36 Flags []string 37 } 38 if err := json.Unmarshal(jsonVectors, &vectors); err != nil { 39 t.Fatal(err) 40 } 41 for i, v := range vectors { 42 expectedToVerify := true 43 for _, f := range v.Flags { 44 switch f { 45 // We use the simplified verification formula that doesn't multiply 46 // by the cofactor, so any low order residue will cause the 47 // signature not to verify. 48 // 49 // This is allowed, but not required, by RFC 8032. 50 case "LowOrderResidue": 51 expectedToVerify = false 52 // Our point decoding allows non-canonical encodings (in violation 53 // of RFC 8032) but R is not decoded: instead, R is recomputed and 54 // compared bytewise against the canonical encoding. 55 case "NonCanonicalR": 56 expectedToVerify = false 57 } 58 } 59 60 publicKey := decodeHex(t, v.A) 61 signature := append(decodeHex(t, v.R), decodeHex(t, v.S)...) 62 message := []byte(v.M) 63 64 didVerify := ed25519.Verify(publicKey, message, signature) 65 if didVerify && !expectedToVerify { 66 t.Errorf("#%d: vector with flags %s unexpectedly verified", i, v.Flags) 67 } 68 if !didVerify && expectedToVerify { 69 t.Errorf("#%d: vector with flags %s unexpectedly rejected", i, v.Flags) 70 } 71 } 72 } 73 74 func downloadEd25519Vectors(t *testing.T) []byte { 75 testenv.MustHaveExternalNetwork(t) 76 77 // Download the JSON test file from the GOPROXY with `go mod download`, 78 // pinning the version so test and module caching works as expected. 79 goTool := testenv.GoToolPath(t) 80 path := "filippo.io/mostly-harmless/ed25519vectors@v0.0.0-20210322192420-30a2d7243a94" 81 cmd := exec.Command(goTool, "mod", "download", "-json", path) 82 // TODO: enable the sumdb once the TryBots proxy supports it. 83 cmd.Env = append(os.Environ(), "GONOSUMDB=*") 84 output, err := cmd.Output() 85 if err != nil { 86 t.Fatalf("failed to run `go mod download -json %s`, output: %s", path, output) 87 } 88 var dm struct { 89 Dir string // absolute path to cached source root directory 90 } 91 if err := json.Unmarshal(output, &dm); err != nil { 92 t.Fatal(err) 93 } 94 95 jsonVectors, err := os.ReadFile(filepath.Join(dm.Dir, "ed25519vectors.json")) 96 if err != nil { 97 t.Fatalf("failed to read ed25519vectors.json: %v", err) 98 } 99 return jsonVectors 100 } 101 102 func decodeHex(t *testing.T, s string) []byte { 103 t.Helper() 104 b, err := hex.DecodeString(s) 105 if err != nil { 106 t.Errorf("invalid hex: %v", err) 107 } 108 return b 109 }