From c86fa9a7ed909e2f2a8ab8298254fca727aba16a Mon Sep 17 00:00:00 2001 From: Roland Shoemaker Date: Tue, 16 Aug 2022 10:25:35 -0700 Subject: internal/wycheproof: add crypto/ecdh tests Alongside the existing ECDH tests, add tests that use the new crypto/ecdh package. The test vectors include a number of private that use non-standard sizes, which we reject, but aren't flagged, so we need to skip them. Change-Id: Iaaef225b0149a86833095f51748d230385d43bfe Reviewed-on: https://go-review.googlesource.com/c/crypto/+/424274 Reviewed-by: Russ Cox Run-TryBot: Roland Shoemaker Reviewed-by: Filippo Valsorda TryBot-Result: Gopher Robot Auto-Submit: Roland Shoemaker --- internal/wycheproof/ecdh_stdlib_test.go | 142 ++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 internal/wycheproof/ecdh_stdlib_test.go diff --git a/internal/wycheproof/ecdh_stdlib_test.go b/internal/wycheproof/ecdh_stdlib_test.go new file mode 100644 index 0000000..44315fc --- /dev/null +++ b/internal/wycheproof/ecdh_stdlib_test.go @@ -0,0 +1,142 @@ +// Copyright 2022 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. + +//go:build go1.20 + +package wycheproof + +import ( + "bytes" + "crypto/ecdh" + "fmt" + "testing" +) + +func TestECDHStdLib(t *testing.T) { + type ECDHTestVector struct { + // A brief description of the test case + Comment string `json:"comment,omitempty"` + // A list of flags + Flags []string `json:"flags,omitempty"` + // the private key + Private string `json:"private,omitempty"` + // Encoded public key + Public string `json:"public,omitempty"` + // Test result + Result string `json:"result,omitempty"` + // The shared secret key + Shared string `json:"shared,omitempty"` + // Identifier of the test case + TcID int `json:"tcId,omitempty"` + } + + type ECDHTestGroup struct { + Curve string `json:"curve,omitempty"` + Tests []*ECDHTestVector `json:"tests,omitempty"` + } + + type Root struct { + TestGroups []*ECDHTestGroup `json:"testGroups,omitempty"` + } + + flagsShouldPass := map[string]bool{ + // We don't support compressed points. + "CompressedPoint": false, + // We don't support decoding custom curves. + "UnnamedCurve": false, + // WrongOrder and UnusedParam are only found with UnnamedCurve. + "WrongOrder": false, + "UnusedParam": false, + + // X25519 specific flags + "Twist": true, + "SmallPublicKey": false, + "LowOrderPublic": false, + "ZeroSharedSecret": false, + "NonCanonicalPublic": true, + } + + // curveToCurve is a map of all elliptic curves supported + // by crypto/elliptic, which can subsequently be parsed and tested. + curveToCurve := map[string]ecdh.Curve{ + "secp256r1": ecdh.P256(), + "secp384r1": ecdh.P384(), + "secp521r1": ecdh.P521(), + "curve25519": ecdh.X25519(), + } + + curveToKeySize := map[string]int{ + "secp256r1": 32, + "secp384r1": 48, + "secp521r1": 66, + "curve25519": 32, + } + + for _, f := range []string{ + "ecdh_secp256r1_ecpoint_test.json", + "ecdh_secp384r1_ecpoint_test.json", + "ecdh_secp521r1_ecpoint_test.json", + "x25519_test.json", + } { + var root Root + readTestVector(t, f, &root) + for _, tg := range root.TestGroups { + if _, ok := curveToCurve[tg.Curve]; !ok { + continue + } + for _, tt := range tg.Tests { + tg, tt := tg, tt + t.Run(fmt.Sprintf("%s/%d", tg.Curve, tt.TcID), func(t *testing.T) { + t.Logf("Type: %v", tt.Result) + t.Logf("Flags: %q", tt.Flags) + t.Log(tt.Comment) + + shouldPass := shouldPass(tt.Result, tt.Flags, flagsShouldPass) + + curve := curveToCurve[tg.Curve] + p := decodeHex(tt.Public) + pub, err := curve.NewPublicKey(p) + if err != nil { + if shouldPass { + t.Errorf("NewPublicKey: %v", err) + } + return + } + + privBytes := decodeHex(tt.Private) + if len(privBytes) != curveToKeySize[tg.Curve] { + t.Skipf("non-standard key size %d", len(privBytes)) + } + + priv, err := curve.NewPrivateKey(privBytes) + if err != nil { + if shouldPass { + t.Errorf("NewPrivateKey: %v", err) + } + return + } + + shared := decodeHex(tt.Shared) + x, err := curve.ECDH(priv, pub) + if err != nil { + if tg.Curve == "curve25519" && !shouldPass { + // ECDH is expected to only return an error when using X25519, + // in all other cases an error is unexpected. + return + } + t.Fatalf("ECDH: %v", err) + } + + if bytes.Equal(shared, x) != shouldPass { + if shouldPass { + t.Errorf("ECDH = %x, want %x", shared, x) + } else { + t.Errorf("ECDH = %x, want anything else", shared) + } + } + }) + } + } + } +} -- cgit v1.3