openpgp/packet: improve handling of short MPIs for RSA values

MPIs are (supposed to be) stripped of leading zeroes. Avoid passing
such short values into crypto/rsa, even if it currently happens to work.

Change-Id: I5a5f4813b8358e83fcc2deeda1272d2733814542
Reviewed-on: https://go-review.googlesource.com/100844
Reviewed-by: Adam Langley <agl@golang.org>
diff --git a/openpgp/packet/encrypted_key.go b/openpgp/packet/encrypted_key.go
index 5ed3cd2..02b372c 100644
--- a/openpgp/packet/encrypted_key.go
+++ b/openpgp/packet/encrypted_key.go
@@ -78,7 +78,8 @@
 	// padding oracle attacks.
 	switch priv.PubKeyAlgo {
 	case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
-		b, err = rsa.DecryptPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1.bytes)
+		k := priv.PrivateKey.(*rsa.PrivateKey)
+		b, err = rsa.DecryptPKCS1v15(config.Random(), k, padToKeySize(&k.PublicKey, e.encryptedMPI1.bytes))
 	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 fee14cf..f2fcf4d 100644
--- a/openpgp/packet/encrypted_key_test.go
+++ b/openpgp/packet/encrypted_key_test.go
@@ -39,39 +39,44 @@
 }
 
 func TestDecryptingEncryptedKey(t *testing.T) {
-	const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8"
-	const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b"
+	for i, encryptedKeyHex := range []string{
+		"c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8",
+		// MPI can be shorter than the length of the key.
+		"c18b032a67d68660df41c70103f8e520c52ae9807183c669ce26e772e482dc5d8cf60e6f59316e145be14d2e5221ee69550db1d5618a8cb002a719f1f0b9345bde21536d410ec90ba86cac37748dec7933eb7f9873873b2d61d3321d1cd44535014f6df58f7bc0c7afb5edc38e1a974428997d2f747f9a173bea9ca53079b409517d332df62d805564cffc9be6",
+	} {
+		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
-	}
+		p, err := Read(readerFromHex(encryptedKeyHex))
+		if err != nil {
+			t.Errorf("#%d: error from Read: %s", i, err)
+			return
+		}
+		ek, ok := p.(*EncryptedKey)
+		if !ok {
+			t.Errorf("#%d: didn't parse an EncryptedKey, got %#v", i, p)
+			return
+		}
 
-	if ek.KeyId != 0x2a67d68660df41c7 || ek.Algo != PubKeyAlgoRSA {
-		t.Errorf("unexpected EncryptedKey contents: %#v", ek)
-		return
-	}
+		if ek.KeyId != 0x2a67d68660df41c7 || ek.Algo != PubKeyAlgoRSA {
+			t.Errorf("#%d: unexpected EncryptedKey contents: %#v", i, ek)
+			return
+		}
 
-	err = ek.Decrypt(encryptedKeyPriv, nil)
-	if err != nil {
-		t.Errorf("error from Decrypt: %s", err)
-		return
-	}
+		err = ek.Decrypt(encryptedKeyPriv, nil)
+		if err != nil {
+			t.Errorf("#%d: error from Decrypt: %s", i, err)
+			return
+		}
 
-	if ek.CipherFunc != CipherAES256 {
-		t.Errorf("unexpected EncryptedKey contents: %#v", ek)
-		return
-	}
+		if ek.CipherFunc != CipherAES256 {
+			t.Errorf("#%d: unexpected EncryptedKey contents: %#v", i, ek)
+			return
+		}
 
-	keyHex := fmt.Sprintf("%x", ek.Key)
-	if keyHex != expectedKeyHex {
-		t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex)
+		keyHex := fmt.Sprintf("%x", ek.Key)
+		if keyHex != expectedKeyHex {
+			t.Errorf("#%d: bad key, got %s want %s", i, keyHex, expectedKeyHex)
+		}
 	}
 }
 
@@ -121,7 +126,7 @@
 
 	keyHex := fmt.Sprintf("%x", ek.Key)
 	if keyHex != expectedKeyHex {
-		t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex)
+		t.Errorf("bad key, got %s want %s", keyHex, expectedKeyHex)
 	}
 }
 
diff --git a/openpgp/packet/packet.go b/openpgp/packet/packet.go
index 3eded93..625bb5a 100644
--- a/openpgp/packet/packet.go
+++ b/openpgp/packet/packet.go
@@ -11,10 +11,12 @@
 	"crypto/aes"
 	"crypto/cipher"
 	"crypto/des"
-	"golang.org/x/crypto/cast5"
-	"golang.org/x/crypto/openpgp/errors"
+	"crypto/rsa"
 	"io"
 	"math/big"
+
+	"golang.org/x/crypto/cast5"
+	"golang.org/x/crypto/openpgp/errors"
 )
 
 // readFull is the same as io.ReadFull except that reading zero bytes returns
@@ -500,19 +502,17 @@
 	numBytes := (int(bitLength) + 7) / 8
 	mpi = make([]byte, numBytes)
 	_, err = readFull(r, mpi)
-	return
-}
-
-// mpiLength returns the length of the given *big.Int when serialized as an
-// MPI.
-func mpiLength(n *big.Int) (mpiLengthInBytes int) {
-	mpiLengthInBytes = 2 /* MPI length */
-	mpiLengthInBytes += (n.BitLen() + 7) / 8
+	// According to RFC 4880 3.2. we should check that the MPI has no leading
+	// zeroes (at least when not an encrypted MPI?), but this implementation
+	// does generate leading zeroes, so we keep accepting them.
 	return
 }
 
 // writeMPI serializes a big integer to w.
 func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) {
+	// Note that we can produce leading zeroes, in violation of RFC 4880 3.2.
+	// Implementations seem to be tolerant of them, and stripping them would
+	// make it complex to guarantee matching re-serialization.
 	_, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)})
 	if err == nil {
 		_, err = w.Write(mpiBytes)
@@ -525,6 +525,18 @@
 	return writeMPI(w, uint16(i.BitLen()), i.Bytes())
 }
 
+// padToKeySize left-pads a MPI with zeroes to match the length of the
+// specified RSA public.
+func padToKeySize(pub *rsa.PublicKey, b []byte) []byte {
+	k := (pub.N.BitLen() + 7) / 8
+	if len(b) >= k {
+		return b
+	}
+	bb := make([]byte, k)
+	copy(bb[len(bb)-len(b):], b)
+	return bb
+}
+
 // CompressionAlgo Represents the different compression algorithms
 // supported by OpenPGP (except for BZIP2, which is not currently
 // supported). See Section 9.3 of RFC 4880.
diff --git a/openpgp/packet/public_key.go b/openpgp/packet/public_key.go
index fbd60f6..fcd5f52 100644
--- a/openpgp/packet/public_key.go
+++ b/openpgp/packet/public_key.go
@@ -520,7 +520,7 @@
 	switch pk.PubKeyAlgo {
 	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
 		rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey)
-		err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes)
+		err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.bytes))
 		if err != nil {
 			return errors.SignatureError("RSA verification failure")
 		}
@@ -571,7 +571,7 @@
 	switch pk.PubKeyAlgo {
 	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
 		rsaPublicKey := pk.PublicKey.(*rsa.PublicKey)
-		if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes); err != nil {
+		if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, padToKeySize(rsaPublicKey, sig.RSASignature.bytes)); err != nil {
 			return errors.SignatureError("RSA verification failure")
 		}
 		return