...

Source file src/crypto/ed25519/ed25519vectors_test.go

Documentation: crypto/ed25519

		 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/[email protected]"
		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  }
	 110  

View as plain text