Skip to content

Commit e9b2fee

Browse files
committed
internal/wycheproof: add Wycheproof tests for verifying signatures
https://github.com/google/wycheproof provides test vectors exposing vulnerabilities in crypto packages. This change creates a new package called internal/wycheproof that runs these Wycheproof tests against a number of pacakages in the standard library (and in the future, x/crypto). Directory structure: - interal/wycheproof/internal/ecdsa: internal version of ecdsa package which includes a new function that verifies ASN encoded signatures directly - interal/wycheproof/internal/dsa: internal version of dsa package which includes a new function that verifies ASN encoded signatures directly - internal/wycheproof: all tests internal/wycheproof/wycheproof_test.go provides utility functions that are common to many tests in the package, and contains the TestMain which fetches github.com/google/wycheproof from the source. This change includes tests for signature verification with dsa, ecdsa, eddsa, and rsa (both PKCS#1 v1.5 and PSS signatures). Note that these tests download testdata from github.com/google/wycheproof by running `go mod download` in the TestMain. This means that internet access will be necessary in order to run these tests if the testdata is not already in your module cache. More tests will be added incrementally. Change-Id: I0378d4be24b5679fdc186e9fc94c1cc0068e81f7 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/209221 Run-TryBot: Katie Hockman <katie@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Filippo Valsorda <filippo@golang.org>
1 parent e7c4368 commit e9b2fee

File tree

9 files changed

+892
-0
lines changed

9 files changed

+892
-0
lines changed

internal/wycheproof/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
This package runs a set of the Wycheproof tests provided by
2+
https://github.com/google/wycheproof.
3+
4+
The JSON test files live in
5+
https://github.com/google/wycheproof/tree/master/testvectors
6+
and are being fetched and cached at a pinned version every time
7+
these tests are run. To change the version of the wycheproof
8+
repository that is being used for testing, update wycheproofModVer.
9+
10+
The structs for these tests are generated from the
11+
schemas provided in https://github.com/google/wycheproof/tree/master/schemas
12+
using https://github.com/a-h/generate.

internal/wycheproof/dsa_test.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Copyright 2019 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 wycheproof
6+
7+
import (
8+
"crypto/dsa"
9+
"testing"
10+
11+
wdsa "golang.org/x/crypto/internal/wycheproof/internal/dsa"
12+
)
13+
14+
func TestDsa(t *testing.T) {
15+
// AsnSignatureTestVector
16+
type AsnSignatureTestVector struct {
17+
18+
// A brief description of the test case
19+
Comment string `json:"comment,omitempty"`
20+
21+
// A list of flags
22+
Flags []string `json:"flags,omitempty"`
23+
24+
// The message to sign
25+
Msg string `json:"msg,omitempty"`
26+
27+
// Test result
28+
Result string `json:"result,omitempty"`
29+
30+
// An ASN encoded signature for msg
31+
Sig string `json:"sig,omitempty"`
32+
33+
// Identifier of the test case
34+
TcId int `json:"tcId,omitempty"`
35+
}
36+
37+
// DsaPublicKey
38+
type DsaPublicKey struct {
39+
40+
// the generator of the multiplicative subgroup
41+
G string `json:"g,omitempty"`
42+
43+
// the key size in bits
44+
KeySize int `json:"keySize,omitempty"`
45+
46+
// the modulus p
47+
P string `json:"p,omitempty"`
48+
49+
// the order of the generator g
50+
Q string `json:"q,omitempty"`
51+
52+
// the key type
53+
Type string `json:"type,omitempty"`
54+
55+
// the public key value
56+
Y string `json:"y,omitempty"`
57+
}
58+
59+
// DsaTestGroup
60+
type DsaTestGroup struct {
61+
62+
// unenocded DSA public key
63+
Key *DsaPublicKey `json:"key,omitempty"`
64+
65+
// DER encoded public key
66+
KeyDer string `json:"keyDer,omitempty"`
67+
68+
// Pem encoded public key
69+
KeyPem string `json:"keyPem,omitempty"`
70+
71+
// the hash function used for DSA
72+
Sha string `json:"sha,omitempty"`
73+
Tests []*AsnSignatureTestVector `json:"tests,omitempty"`
74+
Type interface{} `json:"type,omitempty"`
75+
}
76+
77+
// Notes a description of the labels used in the test vectors
78+
type Notes struct {
79+
}
80+
81+
// Root
82+
type Root struct {
83+
84+
// the primitive tested in the test file
85+
Algorithm string `json:"algorithm,omitempty"`
86+
87+
// the version of the test vectors.
88+
GeneratorVersion string `json:"generatorVersion,omitempty"`
89+
90+
// additional documentation
91+
Header []string `json:"header,omitempty"`
92+
93+
// a description of the labels used in the test vectors
94+
Notes *Notes `json:"notes,omitempty"`
95+
96+
// the number of test vectors in this test
97+
NumberOfTests int `json:"numberOfTests,omitempty"`
98+
Schema interface{} `json:"schema,omitempty"`
99+
TestGroups []*DsaTestGroup `json:"testGroups,omitempty"`
100+
}
101+
102+
flagsShouldPass := map[string]bool{
103+
// An encoded ASN.1 integer missing a leading zero is invalid, but accepted by some implementations.
104+
"NoLeadingZero": false,
105+
}
106+
107+
var root Root
108+
readTestVector(t, "dsa_test.json", &root)
109+
for _, tg := range root.TestGroups {
110+
pub := decodePublicKey(tg.KeyDer).(*dsa.PublicKey)
111+
h := parseHash(tg.Sha).New()
112+
for _, sig := range tg.Tests {
113+
h.Reset()
114+
h.Write(decodeHex(sig.Msg))
115+
hashed := h.Sum(nil)
116+
hashed = hashed[:pub.Q.BitLen()/8] // Truncate to the byte-length of the subgroup (Q)
117+
got := wdsa.VerifyASN1(pub, hashed, decodeHex(sig.Sig))
118+
if want := shouldPass(sig.Result, sig.Flags, flagsShouldPass); got != want {
119+
t.Errorf("tcid: %d, type: %s, comment: %q, wanted success: %t", sig.TcId, sig.Result, sig.Comment, want)
120+
}
121+
}
122+
}
123+
}

internal/wycheproof/ecdsa_test.go

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// Copyright 2019 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 wycheproof
6+
7+
import (
8+
"crypto/ecdsa"
9+
"testing"
10+
11+
wecdsa "golang.org/x/crypto/internal/wycheproof/internal/ecdsa"
12+
)
13+
14+
func TestEcdsa(t *testing.T) {
15+
// AsnSignatureTestVector
16+
type AsnSignatureTestVector struct {
17+
18+
// A brief description of the test case
19+
Comment string `json:"comment,omitempty"`
20+
21+
// A list of flags
22+
Flags []string `json:"flags,omitempty"`
23+
24+
// The message to sign
25+
Msg string `json:"msg,omitempty"`
26+
27+
// Test result
28+
Result string `json:"result,omitempty"`
29+
30+
// An ASN encoded signature for msg
31+
Sig string `json:"sig,omitempty"`
32+
33+
// Identifier of the test case
34+
TcId int `json:"tcId,omitempty"`
35+
}
36+
37+
// EcPublicKey
38+
type EcPublicKey struct {
39+
40+
// the EC group used by this public key
41+
Curve interface{} `json:"curve,omitempty"`
42+
43+
// the key size in bits
44+
KeySize int `json:"keySize,omitempty"`
45+
46+
// the key type
47+
Type string `json:"type,omitempty"`
48+
49+
// encoded public key point
50+
Uncompressed string `json:"uncompressed,omitempty"`
51+
52+
// the x-coordinate of the public key point
53+
Wx string `json:"wx,omitempty"`
54+
55+
// the y-coordinate of the public key point
56+
Wy string `json:"wy,omitempty"`
57+
}
58+
59+
// EcUnnamedGroup
60+
type EcUnnamedGroup struct {
61+
62+
// coefficient a of the elliptic curve equation
63+
A string `json:"a,omitempty"`
64+
65+
// coefficient b of the elliptic curve equation
66+
B string `json:"b,omitempty"`
67+
68+
// the x-coordinate of the generator
69+
Gx string `json:"gx,omitempty"`
70+
71+
// the y-coordinate of the generator
72+
Gy string `json:"gy,omitempty"`
73+
74+
// the cofactor
75+
H int `json:"h,omitempty"`
76+
77+
// the order of the generator
78+
N string `json:"n,omitempty"`
79+
80+
// the order of the underlying field
81+
P string `json:"p,omitempty"`
82+
83+
// an unnamed EC group over a prime field in Weierstrass form
84+
Type string `json:"type,omitempty"`
85+
}
86+
87+
// EcdsaTestGroup
88+
type EcdsaTestGroup struct {
89+
90+
// unenocded EC public key
91+
Key *EcPublicKey `json:"key,omitempty"`
92+
93+
// DER encoded public key
94+
KeyDer string `json:"keyDer,omitempty"`
95+
96+
// Pem encoded public key
97+
KeyPem string `json:"keyPem,omitempty"`
98+
99+
// the hash function used for ECDSA
100+
Sha string `json:"sha,omitempty"`
101+
Tests []*AsnSignatureTestVector `json:"tests,omitempty"`
102+
Type interface{} `json:"type,omitempty"`
103+
}
104+
105+
// Notes a description of the labels used in the test vectors
106+
type Notes struct {
107+
}
108+
109+
// Root
110+
type Root struct {
111+
112+
// the primitive tested in the test file
113+
Algorithm string `json:"algorithm,omitempty"`
114+
115+
// the version of the test vectors.
116+
GeneratorVersion string `json:"generatorVersion,omitempty"`
117+
118+
// additional documentation
119+
Header []string `json:"header,omitempty"`
120+
121+
// a description of the labels used in the test vectors
122+
Notes *Notes `json:"notes,omitempty"`
123+
124+
// the number of test vectors in this test
125+
NumberOfTests int `json:"numberOfTests,omitempty"`
126+
Schema interface{} `json:"schema,omitempty"`
127+
TestGroups []*EcdsaTestGroup `json:"testGroups,omitempty"`
128+
}
129+
130+
flagsShouldPass := map[string]bool{
131+
// An encoded ASN.1 integer missing a leading zero is invalid, but accepted by some implementations.
132+
"MissingZero": false,
133+
// A signature using a weaker hash than the EC params is not a security risk, as long as the hash is secure.
134+
// https://www.imperialviolet.org/2014/05/25/strengthmatching.html
135+
"WeakHash": true,
136+
}
137+
138+
// supportedCurves is a map of all elliptic curves supported
139+
// by crypto/elliptic, which can subsequently be parsed and tested.
140+
supportedCurves := map[string]bool{
141+
"secp224r1": true,
142+
"secp256r1": true,
143+
"secp384r1": true,
144+
"secp521r1": true,
145+
}
146+
147+
var root Root
148+
readTestVector(t, "ecdsa_test.json", &root)
149+
for _, tg := range root.TestGroups {
150+
curve := tg.Key.Curve.(string)
151+
if !supportedCurves[curve] {
152+
continue
153+
}
154+
pub := decodePublicKey(tg.KeyDer).(*ecdsa.PublicKey)
155+
h := parseHash(tg.Sha).New()
156+
for _, sig := range tg.Tests {
157+
h.Reset()
158+
h.Write(decodeHex(sig.Msg))
159+
hashed := h.Sum(nil)
160+
got := wecdsa.VerifyASN1(pub, hashed, decodeHex(sig.Sig))
161+
if want := shouldPass(sig.Result, sig.Flags, flagsShouldPass); got != want {
162+
t.Errorf("tcid: %d, type: %s, comment: %q, wanted success: %t", sig.TcId, sig.Result, sig.Comment, want)
163+
}
164+
}
165+
}
166+
}

0 commit comments

Comments
 (0)