crypto/rsa,crypto/ecdsa,crypto/ed25519: implement PrivateKey.Equal

Fixes #38190

Change-Id: I10766068ee18974e81b3bd78ee0b4d83cc9d1a8c
Reviewed-on: https://go-review.googlesource.com/c/go/+/231417
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Katie Hockman <katie@golang.org>
This commit is contained in:
Filippo Valsorda 2020-04-30 23:52:48 -04:00
parent a6c6e59655
commit a8e83d51a0
6 changed files with 85 additions and 11 deletions

View File

@ -62,6 +62,9 @@ type PublicKey struct {
X, Y *big.Int
}
// Any methods implemented on PublicKey might need to also be implemented on
// PrivateKey, as the latter embeds the former and will expose its methods.
// Equal reports whether pub and x have the same value.
//
// Two keys are only considered to have the same value if they have the same Curve value.
@ -91,6 +94,17 @@ func (priv *PrivateKey) Public() crypto.PublicKey {
return &priv.PublicKey
}
// Equal reports whether priv and x have the same value.
//
// See PublicKey.Equal for details on how Curve is compared.
func (priv *PrivateKey) Equal(x crypto.PrivateKey) bool {
xx, ok := x.(*PrivateKey)
if !ok {
return false
}
return priv.PublicKey.Equal(&xx.PublicKey) && priv.D.Cmp(xx.D) == 0
}
// Sign signs digest with priv, reading randomness from rand. The opts argument
// is not currently used but, in keeping with the crypto.Signer interface,
// should be the hash function used to digest the message.

View File

@ -23,23 +23,32 @@ func testEqual(t *testing.T, c elliptic.Curve) {
if !public.Equal(crypto.Signer(private).Public().(*ecdsa.PublicKey)) {
t.Errorf("private.Public() is not Equal to public: %q", public)
}
if !private.Equal(private) {
t.Errorf("private key is not equal to itself: %v", private)
}
enc, err := x509.MarshalPKIXPublicKey(public)
enc, err := x509.MarshalPKCS8PrivateKey(private)
if err != nil {
t.Fatal(err)
}
decoded, err := x509.ParsePKIXPublicKey(enc)
decoded, err := x509.ParsePKCS8PrivateKey(enc)
if err != nil {
t.Fatal(err)
}
if !public.Equal(decoded) {
if !public.Equal(decoded.(crypto.Signer).Public()) {
t.Errorf("public key is not equal to itself after decoding: %v", public)
}
if !private.Equal(decoded) {
t.Errorf("private key is not equal to itself after decoding: %v", private)
}
other, _ := ecdsa.GenerateKey(c, rand.Reader)
if public.Equal(other) {
if public.Equal(other.Public()) {
t.Errorf("different public keys are Equal")
}
if private.Equal(other) {
t.Errorf("different private keys are Equal")
}
// Ensure that keys with the same coordinates but on different curves
// aren't considered Equal.

View File

@ -40,6 +40,9 @@ const (
// PublicKey is the type of Ed25519 public keys.
type PublicKey []byte
// Any methods implemented on PublicKey might need to also be implemented on
// PrivateKey, as the latter embeds the former and will expose its methods.
// Equal reports whether pub and x have the same value.
func (pub PublicKey) Equal(x crypto.PublicKey) bool {
xx, ok := x.(PublicKey)
@ -59,6 +62,15 @@ func (priv PrivateKey) Public() crypto.PublicKey {
return PublicKey(publicKey)
}
// Equal reports whether priv and x have the same value.
func (priv PrivateKey) Equal(x crypto.PrivateKey) bool {
xx, ok := x.(PrivateKey)
if !ok {
return false
}
return bytes.Equal(priv, xx)
}
// Seed returns the private key seed corresponding to priv. It is provided for
// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
// in this package.

View File

@ -113,14 +113,20 @@ func TestEqual(t *testing.T) {
if !public.Equal(public) {
t.Errorf("public key is not equal to itself: %q", public)
}
if !public.Equal(crypto.Signer(private).Public().(PublicKey)) {
if !public.Equal(crypto.Signer(private).Public()) {
t.Errorf("private.Public() is not Equal to public: %q", public)
}
if !private.Equal(private) {
t.Errorf("private key is not equal to itself: %q", private)
}
other, _, _ := GenerateKey(rand.Reader)
if public.Equal(other) {
otherPub, otherPriv, _ := GenerateKey(rand.Reader)
if public.Equal(otherPub) {
t.Errorf("different public keys are Equal")
}
if private.Equal(otherPriv) {
t.Errorf("different private keys are Equal")
}
}
func TestGolden(t *testing.T) {

View File

@ -22,21 +22,30 @@ func TestEqual(t *testing.T) {
if !public.Equal(crypto.Signer(private).Public().(*rsa.PublicKey)) {
t.Errorf("private.Public() is not Equal to public: %q", public)
}
if !private.Equal(private) {
t.Errorf("private key is not equal to itself: %v", private)
}
enc, err := x509.MarshalPKIXPublicKey(public)
enc, err := x509.MarshalPKCS8PrivateKey(private)
if err != nil {
t.Fatal(err)
}
decoded, err := x509.ParsePKIXPublicKey(enc)
decoded, err := x509.ParsePKCS8PrivateKey(enc)
if err != nil {
t.Fatal(err)
}
if !public.Equal(decoded) {
if !public.Equal(decoded.(crypto.Signer).Public()) {
t.Errorf("public key is not equal to itself after decoding: %v", public)
}
if !private.Equal(decoded) {
t.Errorf("private key is not equal to itself after decoding: %v", private)
}
other, _ := rsa.GenerateKey(rand.Reader, 512)
if public.Equal(other) {
if public.Equal(other.Public()) {
t.Errorf("different public keys are Equal")
}
if private.Equal(other) {
t.Errorf("different private keys are Equal")
}
}

View File

@ -44,6 +44,9 @@ type PublicKey struct {
E int // public exponent
}
// Any methods implemented on PublicKey might need to also be implemented on
// PrivateKey, as the latter embeds the former and will expose its methods.
// Size returns the modulus size in bytes. Raw signatures and ciphertexts
// for or by this public key will have the same size.
func (pub *PublicKey) Size() int {
@ -109,6 +112,27 @@ func (priv *PrivateKey) Public() crypto.PublicKey {
return &priv.PublicKey
}
// Equal reports whether priv and x have equivalent values. It ignores
// Precomputed values.
func (priv *PrivateKey) Equal(x crypto.PrivateKey) bool {
xx, ok := x.(*PrivateKey)
if !ok {
return false
}
if !priv.PublicKey.Equal(&xx.PublicKey) || priv.D.Cmp(xx.D) != 0 {
return false
}
if len(priv.Primes) != len(xx.Primes) {
return false
}
for i := range priv.Primes {
if priv.Primes[i].Cmp(xx.Primes[i]) != 0 {
return false
}
}
return true
}
// Sign signs digest with priv, reading randomness from rand. If opts is a
// *PSSOptions then the PSS algorithm will be used, otherwise PKCS#1 v1.5 will
// be used. digest must be the result of hashing the input message using