]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/rsa: implement crypto.Decrypter
authorNick Sullivan <nicholas.sullivan@gmail.com>
Wed, 25 Feb 2015 01:55:25 +0000 (17:55 -0800)
committerAdam Langley <agl@golang.org>
Mon, 16 Mar 2015 23:15:08 +0000 (23:15 +0000)
Decrypter is an interface to support opaque private keys that perform
decryption operations. This interface is analogous to the crypto.Signer
interface.

This change introduces the crypto.Decrypter interface and implements
the crypto.Decrypter interface for rsa.PrivateKey with both OAEP and
PKCS#1 v1.5 padding modes.

Change-Id: I433f649f84ed3c2148337d735cafd75f1d94a904
Reviewed-on: https://go-review.googlesource.com/3900
Reviewed-by: Adam Langley <agl@golang.org>
src/crypto/crypto.go
src/crypto/rsa/pkcs1v15.go
src/crypto/rsa/pkcs1v15_test.go
src/crypto/rsa/rsa.go

index 59b23e93f5cd9ff637f8cd0d9dd8aa8256b89cae..183a4bd313afb048681d1e0d0c9dfb32aee702ad 100644 (file)
@@ -124,3 +124,19 @@ type SignerOpts interface {
        // hashing was done.
        HashFunc() Hash
 }
+
+// Decrypter is an interface for an opaque private key that can be used for
+// asymmetric decryption operations. For example, an RSA key kept in a hardware
+// module.
+type Decrypter interface {
+       // Public returns the public key corresponding to the opaque,
+       // private key.
+       Public() PublicKey
+
+       // Decrypt decrypts msg. The opts argument should be appropriate for
+       // the primitive used. See the documentation in each implementation for
+       // details.
+       Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error)
+}
+
+type DecrypterOpts interface{}
index 59e8bb5b7ba1a6cb06d2e8c1367f0f3ca7811b51..34037b0d674f72a8d3ec89bba1aa1a3d6c2594e7 100644 (file)
@@ -14,6 +14,16 @@ import (
 
 // This file implements encryption and decryption using PKCS#1 v1.5 padding.
 
+// PKCS1v15DecrypterOpts is for passing options to PKCS#1 v1.5 decryption using
+// the crypto.Decrypter interface.
+type PKCS1v15DecryptOptions struct {
+       // SessionKeyLen is the length of the session key that is being
+       // decrypted. If not zero, then a padding error during decryption will
+       // cause a random plaintext of this length to be returned rather than
+       // an error. These alternatives happen in constant time.
+       SessionKeyLen int
+}
+
 // EncryptPKCS1v15 encrypts the given message with RSA and the padding scheme from PKCS#1 v1.5.
 // The message must be no longer than the length of the public modulus minus 11 bytes.
 // WARNING: use of this function to encrypt plaintexts other than session keys
index 2dc5dbc2c85c5a97c5817f95b19e2157b9d1aa68..89253751ec2696784ed4bf42d654a090a05d360f 100644 (file)
@@ -51,14 +51,25 @@ var decryptPKCS1v15Tests = []DecryptPKCS1v15Test{
 }
 
 func TestDecryptPKCS1v15(t *testing.T) {
-       for i, test := range decryptPKCS1v15Tests {
-               out, err := DecryptPKCS1v15(nil, rsaPrivateKey, decodeBase64(test.in))
-               if err != nil {
-                       t.Errorf("#%d error decrypting", i)
-               }
-               want := []byte(test.out)
-               if !bytes.Equal(out, want) {
-                       t.Errorf("#%d got:%#v want:%#v", i, out, want)
+       decryptionFuncs := []func([]byte) ([]byte, error){
+               func(ciphertext []byte) (plaintext []byte, err error) {
+                       return DecryptPKCS1v15(nil, rsaPrivateKey, ciphertext)
+               },
+               func(ciphertext []byte) (plaintext []byte, err error) {
+                       return rsaPrivateKey.Decrypt(nil, ciphertext, nil)
+               },
+       }
+
+       for _, decryptFunc := range decryptionFuncs {
+               for i, test := range decryptPKCS1v15Tests {
+                       out, err := decryptFunc(decodeBase64(test.in))
+                       if err != nil {
+                               t.Errorf("#%d error decrypting", i)
+                       }
+                       want := []byte(test.out)
+                       if !bytes.Equal(out, want) {
+                               t.Errorf("#%d got:%#v want:%#v", i, out, want)
+                       }
                }
        }
 }
@@ -138,6 +149,22 @@ func TestEncryptPKCS1v15SessionKey(t *testing.T) {
        }
 }
 
+func TestEncryptPKCS1v15DecrypterSessionKey(t *testing.T) {
+       for i, test := range decryptPKCS1v15SessionKeyTests {
+               plaintext, err := rsaPrivateKey.Decrypt(rand.Reader, decodeBase64(test.in), &PKCS1v15DecryptOptions{SessionKeyLen: 4})
+               if err != nil {
+                       t.Fatalf("#%d: error decrypting: %s", i, err)
+               }
+               if len(plaintext) != 4 {
+                       t.Fatalf("#%d: incorrect length plaintext: got %d, want 4", i, len(plaintext))
+               }
+
+               if test.out != "FAIL" && !bytes.Equal(plaintext, []byte(test.out)) {
+                       t.Errorf("#%d: incorrect plaintext: got %x, want %x", plaintext, test.out)
+               }
+       }
+}
+
 func TestNonZeroRandomBytes(t *testing.T) {
        random := rand.Reader
 
index 21704469d28984d1319d81b86b7e93d38a0a22b8..f9f6d25a893f456a719cd26aa5d5827c5a8492fa 100644 (file)
@@ -24,6 +24,16 @@ type PublicKey struct {
        E int      // public exponent
 }
 
+// OAEPOptions is an interface for passing options to OAEP decryption using the
+// crypto.Decrypter interface.
+type OAEPOptions struct {
+       // Hash is the hash function that will be used when generating the mask.
+       Hash crypto.Hash
+       // Label is an arbitrary byte string that must be equal to the value
+       // used when encrypting.
+       Label []byte
+}
+
 var (
        errPublicModulus       = errors.New("crypto/rsa: missing public modulus")
        errPublicExponentSmall = errors.New("crypto/rsa: public exponent too small")
@@ -77,6 +87,37 @@ func (priv *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts)
        return SignPKCS1v15(rand, priv, opts.HashFunc(), msg)
 }
 
+// Decrypt decrypts ciphertext with priv. If opts is nil or of type
+// *PKCS1v15DecryptOptions then PKCS#1 v1.5 decryption is performed. Otherwise
+// opts must have type *OAEPOptions and OAEP decryption is done.
+func (priv *PrivateKey) Decrypt(rand io.Reader, ciphertext []byte, opts crypto.DecrypterOpts) (plaintext []byte, err error) {
+       if opts == nil {
+               return DecryptPKCS1v15(rand, priv, ciphertext)
+       }
+
+       switch opts := opts.(type) {
+       case *OAEPOptions:
+               return DecryptOAEP(opts.Hash.New(), rand, priv, ciphertext, opts.Label)
+
+       case *PKCS1v15DecryptOptions:
+               if l := opts.SessionKeyLen; l > 0 {
+                       plaintext = make([]byte, l)
+                       if _, err := rand.Read(plaintext); err != nil {
+                               return nil, err
+                       }
+                       if err := DecryptPKCS1v15SessionKey(rand, priv, ciphertext, plaintext); err != nil {
+                               return nil, err
+                       }
+                       return plaintext, nil
+               } else {
+                       return DecryptPKCS1v15(rand, priv, ciphertext)
+               }
+
+       default:
+               return nil, errors.New("crypto/rsa: invalid options for Decrypt")
+       }
+}
+
 type PrecomputedValues struct {
        Dp, Dq *big.Int // D mod (P-1) (or mod Q-1)
        Qinv   *big.Int // Q^-1 mod P