From a8e83d51a0cc709c836fe8836b10155342aa2ac4 Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Thu, 30 Apr 2020 23:52:48 -0400 Subject: [PATCH] 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 TryBot-Result: Gobot Gobot Reviewed-by: Katie Hockman --- src/crypto/ecdsa/ecdsa.go | 14 ++++++++++++++ src/crypto/ecdsa/equal_test.go | 17 +++++++++++++---- src/crypto/ed25519/ed25519.go | 12 ++++++++++++ src/crypto/ed25519/ed25519_test.go | 12 +++++++++--- src/crypto/rsa/equal_test.go | 17 +++++++++++++---- src/crypto/rsa/rsa.go | 24 ++++++++++++++++++++++++ 6 files changed, 85 insertions(+), 11 deletions(-) diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go index 786b8a98848..ccce8738594 100644 --- a/src/crypto/ecdsa/ecdsa.go +++ b/src/crypto/ecdsa/ecdsa.go @@ -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. diff --git a/src/crypto/ecdsa/equal_test.go b/src/crypto/ecdsa/equal_test.go index 9b507dd4c23..53ac8504c2f 100644 --- a/src/crypto/ecdsa/equal_test.go +++ b/src/crypto/ecdsa/equal_test.go @@ -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. diff --git a/src/crypto/ed25519/ed25519.go b/src/crypto/ed25519/ed25519.go index 748c039dce9..5766970f827 100644 --- a/src/crypto/ed25519/ed25519.go +++ b/src/crypto/ed25519/ed25519.go @@ -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. diff --git a/src/crypto/ed25519/ed25519_test.go b/src/crypto/ed25519/ed25519_test.go index 6b5cb9d2018..f77d463721c 100644 --- a/src/crypto/ed25519/ed25519_test.go +++ b/src/crypto/ed25519/ed25519_test.go @@ -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) { diff --git a/src/crypto/rsa/equal_test.go b/src/crypto/rsa/equal_test.go index b00d0ea8a9b..90f4bf94753 100644 --- a/src/crypto/rsa/equal_test.go +++ b/src/crypto/rsa/equal_test.go @@ -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") + } } diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go index 28eb5926c1a..b414b44148a 100644 --- a/src/crypto/rsa/rsa.go +++ b/src/crypto/rsa/rsa.go @@ -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