github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/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 // Create a temp dir and modcache subdir. 78 d := t.TempDir() 79 // Create a spot for the modcache. 80 modcache := filepath.Join(d, "modcache") 81 if err := os.Mkdir(modcache, 0777); err != nil { 82 t.Fatal(err) 83 } 84 85 t.Setenv("GO111MODULE", "on") 86 t.Setenv("GOMODCACHE", modcache) 87 88 // Download the JSON test file from the GOPROXY with `go mod download`, 89 // pinning the version so test and module caching works as expected. 90 goTool := testenv.GoToolPath(t) 91 path := "filippo.io/mostly-harmless/ed25519vectors@v0.0.0-20210322192420-30a2d7243a94" 92 cmd := exec.Command(goTool, "mod", "download", "-modcacherw", "-json", path) 93 // TODO: enable the sumdb once the TryBots proxy supports it. 94 cmd.Env = append(os.Environ(), "GONOSUMDB=*") 95 output, err := cmd.Output() 96 if err != nil { 97 t.Fatalf("failed to run `go mod download -json %s`, output: %s", path, output) 98 } 99 var dm struct { 100 Dir string // absolute path to cached source root directory 101 } 102 if err := json.Unmarshal(output, &dm); err != nil { 103 t.Fatal(err) 104 } 105 106 jsonVectors, err := os.ReadFile(filepath.Join(dm.Dir, "ed25519vectors.json")) 107 if err != nil { 108 t.Fatalf("failed to read ed25519vectors.json: %v", err) 109 } 110 return jsonVectors 111 } 112 113 func decodeHex(t *testing.T, s string) []byte { 114 t.Helper() 115 b, err := hex.DecodeString(s) 116 if err != nil { 117 t.Errorf("invalid hex: %v", err) 118 } 119 return b 120 }