mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
crypto/ed25519: add comprehensive edge-case test vectors
This will allow us to make changes to the internals confidently, without risking causing issues in consensus applications. It will also prevent architecture-specific divergence, like #40475. Fixes #40478 Change-Id: I8c2b31406ca88add6941f14d8df8cecb96379cde Reviewed-on: https://go-review.googlesource.com/c/go/+/304349 Run-TryBot: Filippo Valsorda <filippo@golang.org> Reviewed-by: Roland Shoemaker <roland@golang.org> Reviewed-by: Katie Hockman <katie@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Trust: Filippo Valsorda <filippo@golang.org> Trust: Katie Hockman <katie@golang.org>
This commit is contained in:
parent
27015152ec
commit
79b2e14b1a
1 changed files with 109 additions and 0 deletions
109
src/crypto/ed25519/ed25519vectors_test.go
Normal file
109
src/crypto/ed25519/ed25519vectors_test.go
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
// Copyright 2021 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 ed25519_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"internal/testenv"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestEd25519Vectors runs a very large set of test vectors that exercise all
|
||||||
|
// combinations of low-order points, low-order components, and non-canonical
|
||||||
|
// encodings. These vectors lock in unspecified and spec-divergent behaviors in
|
||||||
|
// edge cases that are not security relevant in most contexts, but that can
|
||||||
|
// cause issues in consensus applications if changed.
|
||||||
|
//
|
||||||
|
// Our behavior matches the "classic" unwritten verification rules of the
|
||||||
|
// "ref10" reference implementation.
|
||||||
|
//
|
||||||
|
// Note that although we test for these edge cases, they are not covered by the
|
||||||
|
// Go 1 Compatibility Promise. Applications that need stable verification rules
|
||||||
|
// should use github.com/hdevalence/ed25519consensus.
|
||||||
|
//
|
||||||
|
// See https://hdevalence.ca/blog/2020-10-04-its-25519am for more details.
|
||||||
|
func TestEd25519Vectors(t *testing.T) {
|
||||||
|
jsonVectors := downloadEd25519Vectors(t)
|
||||||
|
var vectors []struct {
|
||||||
|
A, R, S, M string
|
||||||
|
Flags []string
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(jsonVectors, &vectors); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for i, v := range vectors {
|
||||||
|
expectedToVerify := true
|
||||||
|
for _, f := range v.Flags {
|
||||||
|
switch f {
|
||||||
|
// We use the simplified verification formula that doesn't multiply
|
||||||
|
// by the cofactor, so any low order residue will cause the
|
||||||
|
// signature not to verify.
|
||||||
|
//
|
||||||
|
// This is allowed, but not required, by RFC 8032.
|
||||||
|
case "LowOrderResidue":
|
||||||
|
expectedToVerify = false
|
||||||
|
// Our point decoding allows non-canonical encodings (in violation
|
||||||
|
// of RFC 8032) but R is not decoded: instead, R is recomputed and
|
||||||
|
// compared bytewise against the canonical encoding.
|
||||||
|
case "NonCanonicalR":
|
||||||
|
expectedToVerify = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKey := decodeHex(t, v.A)
|
||||||
|
signature := append(decodeHex(t, v.R), decodeHex(t, v.S)...)
|
||||||
|
message := []byte(v.M)
|
||||||
|
|
||||||
|
didVerify := ed25519.Verify(publicKey, message, signature)
|
||||||
|
if didVerify && !expectedToVerify {
|
||||||
|
t.Errorf("#%d: vector with flags %s unexpectedly verified", i, v.Flags)
|
||||||
|
}
|
||||||
|
if !didVerify && expectedToVerify {
|
||||||
|
t.Errorf("#%d: vector with flags %s unexpectedly rejected", i, v.Flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadEd25519Vectors(t *testing.T) []byte {
|
||||||
|
testenv.MustHaveExternalNetwork(t)
|
||||||
|
|
||||||
|
// Download the JSON test file from the GOPROXY with `go mod download`,
|
||||||
|
// pinning the version so test and module caching works as expected.
|
||||||
|
goTool := testenv.GoToolPath(t)
|
||||||
|
path := "filippo.io/mostly-harmless/ed25519vectors@v0.0.0-20210322192420-30a2d7243a94"
|
||||||
|
cmd := exec.Command(goTool, "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 {
|
||||||
|
t.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 {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonVectors, err := os.ReadFile(filepath.Join(dm.Dir, "ed25519vectors.json"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read ed25519vectors.json: %v", err)
|
||||||
|
}
|
||||||
|
return jsonVectors
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeHex(t *testing.T, s string) []byte {
|
||||||
|
t.Helper()
|
||||||
|
b, err := hex.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("invalid hex: %v", err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue