Source file src/crypto/ed25519/ed25519vectors_test.go

     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  	"crypto/internal/cryptotest"
    10  	"encoding/hex"
    11  	"encoding/json"
    12  	"os"
    13  	"path/filepath"
    14  	"testing"
    15  )
    16  
    17  // TestEd25519Vectors runs a very large set of test vectors that exercise all
    18  // combinations of low-order points, low-order components, and non-canonical
    19  // encodings. These vectors lock in unspecified and spec-divergent behaviors in
    20  // edge cases that are not security relevant in most contexts, but that can
    21  // cause issues in consensus applications if changed.
    22  //
    23  // Our behavior matches the "classic" unwritten verification rules of the
    24  // "ref10" reference implementation.
    25  //
    26  // Note that although we test for these edge cases, they are not covered by the
    27  // Go 1 Compatibility Promise. Applications that need stable verification rules
    28  // should use github.com/hdevalence/ed25519consensus.
    29  //
    30  // See https://hdevalence.ca/blog/2020-10-04-its-25519am for more details.
    31  func TestEd25519Vectors(t *testing.T) {
    32  	jsonVectors := downloadEd25519Vectors(t)
    33  	var vectors []struct {
    34  		A, R, S, M string
    35  		Flags      []string
    36  	}
    37  	if err := json.Unmarshal(jsonVectors, &vectors); err != nil {
    38  		t.Fatal(err)
    39  	}
    40  	for i, v := range vectors {
    41  		expectedToVerify := true
    42  		for _, f := range v.Flags {
    43  			switch f {
    44  			// We use the simplified verification formula that doesn't multiply
    45  			// by the cofactor, so any low order residue will cause the
    46  			// signature not to verify.
    47  			//
    48  			// This is allowed, but not required, by RFC 8032.
    49  			case "LowOrderResidue":
    50  				expectedToVerify = false
    51  			// Our point decoding allows non-canonical encodings (in violation
    52  			// of RFC 8032) but R is not decoded: instead, R is recomputed and
    53  			// compared bytewise against the canonical encoding.
    54  			case "NonCanonicalR":
    55  				expectedToVerify = false
    56  			}
    57  		}
    58  
    59  		publicKey := decodeHex(t, v.A)
    60  		signature := append(decodeHex(t, v.R), decodeHex(t, v.S)...)
    61  		message := []byte(v.M)
    62  
    63  		didVerify := ed25519.Verify(publicKey, message, signature)
    64  		if didVerify && !expectedToVerify {
    65  			t.Errorf("#%d: vector with flags %s unexpectedly verified", i, v.Flags)
    66  		}
    67  		if !didVerify && expectedToVerify {
    68  			t.Errorf("#%d: vector with flags %s unexpectedly rejected", i, v.Flags)
    69  		}
    70  	}
    71  }
    72  
    73  func downloadEd25519Vectors(t *testing.T) []byte {
    74  	// Download the JSON test file from the GOPROXY with `go mod download`,
    75  	// pinning the version so test and module caching works as expected.
    76  	path := "filippo.io/mostly-harmless/ed25519vectors"
    77  	version := "v0.0.0-20210322192420-30a2d7243a94"
    78  	dir := cryptotest.FetchModule(t, path, version)
    79  
    80  	jsonVectors, err := os.ReadFile(filepath.Join(dir, "ed25519vectors.json"))
    81  	if err != nil {
    82  		t.Fatalf("failed to read ed25519vectors.json: %v", err)
    83  	}
    84  	return jsonVectors
    85  }
    86  
    87  func decodeHex(t *testing.T, s string) []byte {
    88  	t.Helper()
    89  	b, err := hex.DecodeString(s)
    90  	if err != nil {
    91  		t.Errorf("invalid hex: %v", err)
    92  	}
    93  	return b
    94  }
    95  

View as plain text