]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/rsa,crypto/ecdsa,crypto/ed25519: implement PrivateKey.Equal
authorFilippo Valsorda <filippo@golang.org>
Fri, 1 May 2020 03:52:48 +0000 (23:52 -0400)
committerFilippo Valsorda <filippo@golang.org>
Tue, 5 May 2020 18:05:10 +0000 (18:05 +0000)
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>
src/crypto/ecdsa/ecdsa.go
src/crypto/ecdsa/equal_test.go
src/crypto/ed25519/ed25519.go
src/crypto/ed25519/ed25519_test.go
src/crypto/rsa/equal_test.go
src/crypto/rsa/rsa.go

index 786b8a988487f0560e90c86e09af994a4cb805fb..ccce87385947f06dd30d813ee0998a35e3a8cd34 100644 (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.
index 9b507dd4c23b67d75a2669502cf0d7665600d1d0..53ac8504c2f692b887da053acfe890e3c33b12b1 100644 (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.
index 748c039dce9876e91f5ad2224c4e760623fa64c8..5766970f82749ddd1202c258b81061862c253c9c 100644 (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.
index 6b5cb9d20188e7ab472981064515e266ab565adc..f77d463721c475688c11d45a94f448f5c1761feb 100644 (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) {
index b00d0ea8a9b2ca9249caedb253fb71595e4b9ad1..90f4bf947538442a1fd8bdb6f185468907641964 100644 (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")
+       }
 }
index 28eb5926c1a54773fcb4dcf15805b24be1c44db7..b414b44148a46c476a8c5df4679a0bc1ba817847 100644 (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