openpgp/packet: support crypto.Decrypter in EncryptedKey.Decrypt

Fixes golang/go#33301

Change-Id: I74a389367d34d4718d70349794027ed9f1eca370
GitHub-Last-Rev: 6d95cdf63248a54b134027ed49641c204187f188
GitHub-Pull-Request: golang/crypto#94
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/189497
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/openpgp/packet/encrypted_key.go b/openpgp/packet/encrypted_key.go
index 02b372c..6d76397 100644
--- a/openpgp/packet/encrypted_key.go
+++ b/openpgp/packet/encrypted_key.go
@@ -5,6 +5,7 @@
 package packet
 
 import (
+	"crypto"
 	"crypto/rsa"
 	"encoding/binary"
 	"io"
@@ -78,8 +79,9 @@
 	// padding oracle attacks.
 	switch priv.PubKeyAlgo {
 	case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
-		k := priv.PrivateKey.(*rsa.PrivateKey)
-		b, err = rsa.DecryptPKCS1v15(config.Random(), k, padToKeySize(&k.PublicKey, e.encryptedMPI1.bytes))
+		// Supports both *rsa.PrivateKey and crypto.Decrypter
+		k := priv.PrivateKey.(crypto.Decrypter)
+		b, err = k.Decrypt(config.Random(), padToKeySize(k.Public().(*rsa.PublicKey), e.encryptedMPI1.bytes), nil)
 	case PubKeyAlgoElGamal:
 		c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes)
 		c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes)
diff --git a/openpgp/packet/encrypted_key_test.go b/openpgp/packet/encrypted_key_test.go
index f2fcf4d..ecb22bc 100644
--- a/openpgp/packet/encrypted_key_test.go
+++ b/openpgp/packet/encrypted_key_test.go
@@ -6,9 +6,11 @@
 
 import (
 	"bytes"
+	"crypto"
 	"crypto/rsa"
 	"encoding/hex"
 	"fmt"
+	"io"
 	"math/big"
 	"testing"
 )
@@ -80,6 +82,73 @@
 	}
 }
 
+type rsaDecrypter struct {
+	rsaPrivateKey *rsa.PrivateKey
+	decryptCount  int
+}
+
+func (r *rsaDecrypter) Public() crypto.PublicKey {
+	return &r.rsaPrivateKey.PublicKey
+}
+
+func (r *rsaDecrypter) Decrypt(rand io.Reader, msg []byte, opts crypto.DecrypterOpts) (plaintext []byte, err error) {
+	r.decryptCount++
+	return r.rsaPrivateKey.Decrypt(rand, msg, opts)
+}
+
+func TestRSADecrypter(t *testing.T) {
+	const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8"
+
+	const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b"
+
+	p, err := Read(readerFromHex(encryptedKeyHex))
+	if err != nil {
+		t.Errorf("error from Read: %s", err)
+		return
+	}
+	ek, ok := p.(*EncryptedKey)
+	if !ok {
+		t.Errorf("didn't parse an EncryptedKey, got %#v", p)
+		return
+	}
+
+	if ek.KeyId != 0x2a67d68660df41c7 || ek.Algo != PubKeyAlgoRSA {
+		t.Errorf("unexpected EncryptedKey contents: %#v", ek)
+		return
+	}
+
+	customDecrypter := &rsaDecrypter{
+		rsaPrivateKey: encryptedKeyRSAPriv,
+	}
+
+	customKeyPriv := &PrivateKey{
+		PublicKey: PublicKey{
+			PubKeyAlgo: PubKeyAlgoRSA,
+		},
+		PrivateKey: customDecrypter,
+	}
+
+	err = ek.Decrypt(customKeyPriv, nil)
+	if err != nil {
+		t.Errorf("error from Decrypt: %s", err)
+		return
+	}
+
+	if ek.CipherFunc != CipherAES256 {
+		t.Errorf("unexpected EncryptedKey contents: %#v", ek)
+		return
+	}
+
+	keyHex := fmt.Sprintf("%x", ek.Key)
+	if keyHex != expectedKeyHex {
+		t.Errorf("bad key, got %s want %s", keyHex, expectedKeyHex)
+	}
+
+	if customDecrypter.decryptCount != 1 {
+		t.Errorf("Expected customDecrypter.Decrypt() to be called 1 time, but was called %d times", customDecrypter.decryptCount)
+	}
+}
+
 func TestEncryptingEncryptedKey(t *testing.T) {
 	key := []byte{1, 2, 3, 4}
 	const expectedKeyHex = "01020304"
diff --git a/openpgp/packet/private_key.go b/openpgp/packet/private_key.go
index 6f8ec09..81abb7c 100644
--- a/openpgp/packet/private_key.go
+++ b/openpgp/packet/private_key.go
@@ -31,7 +31,7 @@
 	encryptedData []byte
 	cipher        CipherFunction
 	s2k           func(out, in []byte)
-	PrivateKey    interface{} // An *{rsa|dsa|ecdsa}.PrivateKey or a crypto.Signer.
+	PrivateKey    interface{} // An *{rsa|dsa|ecdsa}.PrivateKey or crypto.Signer/crypto.Decrypter (Decryptor RSA only).
 	sha1Checksum  bool
 	iv            []byte
 }