Fixes golang/go#38936 Change-Id: I231d30fcc683abd9efb36b6fd9cc05f599078ade Reviewed-on: https://go-review.googlesource.com/c/crypto/+/396174 Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Filippo Valsorda <valsorda@google.com> Auto-Submit: Filippo Valsorda <filippo@golang.org> Reviewed-by: Roland Shoemaker <roland@golang.org>
164 lines
4.4 KiB
Go
164 lines
4.4 KiB
Go
// Copyright 2019 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 wycheproof
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/x509"
|
|
"encoding/asn1"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"golang.org/x/crypto/cryptobyte"
|
|
casn1 "golang.org/x/crypto/cryptobyte/asn1"
|
|
)
|
|
|
|
func TestECDH(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{
|
|
// ParsePKIXPublicKey doesn't support compressed points, but we test
|
|
// them against UnmarshalCompressed anyway.
|
|
"CompressedPoint": true,
|
|
// We don't support decoding custom curves.
|
|
"UnnamedCurve": false,
|
|
// WrongOrder and UnusedParam are only found with UnnamedCurve.
|
|
"WrongOrder": false,
|
|
"UnusedParam": false,
|
|
}
|
|
|
|
// supportedCurves is a map of all elliptic curves supported
|
|
// by crypto/elliptic, which can subsequently be parsed and tested.
|
|
supportedCurves := map[string]bool{
|
|
"secp224r1": true,
|
|
"secp256r1": true,
|
|
"secp384r1": true,
|
|
"secp521r1": true,
|
|
}
|
|
|
|
var root Root
|
|
readTestVector(t, "ecdh_test.json", &root)
|
|
for _, tg := range root.TestGroups {
|
|
if !supportedCurves[tg.Curve] {
|
|
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)
|
|
|
|
p := decodeHex(tt.Public)
|
|
pp, err := x509.ParsePKIXPublicKey(p)
|
|
if err != nil {
|
|
pp, err = decodeCompressedPKIX(p)
|
|
}
|
|
if err != nil {
|
|
if shouldPass {
|
|
t.Errorf("unexpected parsing error: %s", err)
|
|
}
|
|
return
|
|
}
|
|
pub := pp.(*ecdsa.PublicKey)
|
|
|
|
priv := decodeHex(tt.Private)
|
|
shared := decodeHex(tt.Shared)
|
|
|
|
x, _ := pub.Curve.ScalarMult(pub.X, pub.Y, priv)
|
|
xBytes := make([]byte, (pub.Curve.Params().BitSize+7)/8)
|
|
got := bytes.Equal(shared, x.FillBytes(xBytes))
|
|
|
|
if want := shouldPass; got != want {
|
|
t.Errorf("wanted success %v, got %v", want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func decodeCompressedPKIX(der []byte) (interface{}, error) {
|
|
s := cryptobyte.String(der)
|
|
var s1, s2 cryptobyte.String
|
|
var algoOID, namedCurveOID asn1.ObjectIdentifier
|
|
var pointDER []byte
|
|
if !s.ReadASN1(&s1, casn1.SEQUENCE) || !s.Empty() ||
|
|
!s1.ReadASN1(&s2, casn1.SEQUENCE) ||
|
|
!s2.ReadASN1ObjectIdentifier(&algoOID) ||
|
|
!s2.ReadASN1ObjectIdentifier(&namedCurveOID) || !s2.Empty() ||
|
|
!s1.ReadASN1BitStringAsBytes(&pointDER) || !s1.Empty() {
|
|
return nil, errors.New("failed to parse PKIX structure")
|
|
}
|
|
|
|
if !algoOID.Equal(oidPublicKeyECDSA) {
|
|
return nil, errors.New("wrong algorithm OID")
|
|
}
|
|
namedCurve := namedCurveFromOID(namedCurveOID)
|
|
if namedCurve == nil {
|
|
return nil, errors.New("unsupported elliptic curve")
|
|
}
|
|
x, y := elliptic.UnmarshalCompressed(namedCurve, pointDER)
|
|
if x == nil {
|
|
return nil, errors.New("failed to unmarshal elliptic curve point")
|
|
}
|
|
pub := &ecdsa.PublicKey{
|
|
Curve: namedCurve,
|
|
X: x,
|
|
Y: y,
|
|
}
|
|
return pub, nil
|
|
}
|
|
|
|
var (
|
|
oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
|
|
oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33}
|
|
oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
|
|
oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
|
|
oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
|
|
)
|
|
|
|
func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {
|
|
switch {
|
|
case oid.Equal(oidNamedCurveP224):
|
|
return elliptic.P224()
|
|
case oid.Equal(oidNamedCurveP256):
|
|
return elliptic.P256()
|
|
case oid.Equal(oidNamedCurveP384):
|
|
return elliptic.P384()
|
|
case oid.Equal(oidNamedCurveP521):
|
|
return elliptic.P521()
|
|
}
|
|
return nil
|
|
}
|