Nick Sullivan uploaded a change:
https://go-review.googlesource.com/3900
crypto/tls: implement crypto.Signer and crypto.Decrypter
Decrypter is a new 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.
This change also implements crypto.Signer and crypto.Decrypter in tls so
opaque RSA and ECDSA keys can be used in both client and server
handshakes.
Change-Id: I433f649f84ed3c2148337d735cafd75f1d94a904
---
M src/crypto/crypto.go
M src/crypto/rsa/pkcs1v15.go
M src/crypto/rsa/rsa.go
M src/crypto/tls/common.go
M src/crypto/tls/handshake_server.go
M src/crypto/tls/key_agreement.go
6 files changed, 80 insertions(+), 19 deletions(-)
diff --git a/src/crypto/crypto.go b/src/crypto/crypto.go
index 59b23e9..d96d985 100644
--- a/src/crypto/crypto.go
+++ b/src/crypto/crypto.go
@@ -124,3 +124,19 @@
// 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 a ciphertext into an existing slice. Behavior
+ // depends on the DecrypterOpts.
+ Decrypt(rand io.Reader, ciphertext, msg []byte, opts DecrypterOpts) (err
error)
+}
+
+// DecrypterOpts contains options for a Decrypter.
+type DecrypterOpts interface{}
diff --git a/src/crypto/rsa/pkcs1v15.go b/src/crypto/rsa/pkcs1v15.go
index 59e8bb5..e461473 100644
--- a/src/crypto/rsa/pkcs1v15.go
+++ b/src/crypto/rsa/pkcs1v15.go
@@ -14,6 +14,12 @@
// This file implements encryption and decryption using PKCS#1 v1.5
padding.
+// PKCS1v15Options implements crypto.DecrypterOpts and provides an option
to
+// perform TLS session key decryption in Decrypt.
+type PKCS1v15Options struct {
+ SessionKey bool
+}
+
// 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
diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go
index 2702311..56f2fb4 100644
--- a/src/crypto/rsa/rsa.go
+++ b/src/crypto/rsa/rsa.go
@@ -24,6 +24,14 @@
E int // public exponent
}
+// OAEPOptions provides the hash and the label to be used in OAEP
+// decryption through the crypto.Decrypter interface as a
+// crypto.DecrypterOpts
+type OAEPOptions struct {
+ hash hash.Hash
+ label []byte
+}
+
var (
errPublicModulus = errors.New("crypto/rsa: missing public modulus")
errPublicExponentSmall = errors.New("crypto/rsa: public exponent too
small")
@@ -77,6 +85,29 @@
return SignPKCS1v15(rand, priv, opts.HashFunc(), msg)
}
+// Decrypt decrypts with an RSA private key. If opts is of type
*OAEPOptions
+// then OAEP padding is expected, otherwise PKCS#1 v1.5 padding is
expected.
+func (priv *PrivateKey) Decrypt(rand io.Reader, ciphertext []byte, msg
[]byte, opts crypto.DecrypterOpts) (err error) {
+ switch o := opts.(type) {
+ case *OAEPOptions:
+ out, err := DecryptOAEP(o.hash, rand, priv, ciphertext, o.label)
+ if err != nil {
+ copy(msg, out)
+ }
+ return err
+ case *PKCS1v15Options:
+ if o.SessionKey {
+ return DecryptPKCS1v15SessionKey(rand, priv, ciphertext, msg)
+ } else {
+ out, err := DecryptPKCS1v15(rand, priv, ciphertext)
+ if err != nil {
+ copy(msg, out)
+ }
+ }
+ }
+ return
+}
+
type PrecomputedValues struct {
Dp, Dq *big.Int // D mod (P-1) (or mod Q-1)
Qinv *big.Int // Q^-1 mod P
diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go
index e3c6004..14858ae 100644
--- a/src/crypto/tls/common.go
+++ b/src/crypto/tls/common.go
@@ -488,10 +488,10 @@
type Certificate struct {
Certificate [][]byte
// PrivateKey contains the private key corresponding to the public key
- // in Leaf. For a server, this must be a *rsa.PrivateKey or
- // *ecdsa.PrivateKey. For a client doing client authentication, this
- // can be any type that implements crypto.Signer (which includes RSA
- // and ECDSA private keys).
+ // in Leaf. For a server, this must implement either crypto.Decrypter
+ // (implemented by RSA private keys) or crypto.Signer (which includes
+ // RSA and ECDSA private keys). For a client doing client authentication,
+ // this can be any type that implements crypto.Signer.
PrivateKey crypto.PrivateKey
// OCSPStaple contains an optional OCSP response which will be served
// to clients that request it.
diff --git a/src/crypto/tls/handshake_server.go
b/src/crypto/tls/handshake_server.go
index a461334..20bcf6d 100644
--- a/src/crypto/tls/handshake_server.go
+++ b/src/crypto/tls/handshake_server.go
@@ -197,7 +197,11 @@
}
}
- _, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey)
+ priv, ok := hs.cert.PrivateKey.(crypto.Signer)
+ if !ok {
+ err = errors.New("tls: certificate private key does not implement
crypto.Signer")
+ }
+ _, hs.ecdsaOk = priv.Public().(*ecdsa.PublicKey)
if hs.checkForResumption() {
return true, nil
diff --git a/src/crypto/tls/key_agreement.go
b/src/crypto/tls/key_agreement.go
index 0974fc6..92d61ff 100644
--- a/src/crypto/tls/key_agreement.go
+++ b/src/crypto/tls/key_agreement.go
@@ -50,7 +50,11 @@
ciphertext = ckx.ciphertext[2:]
}
- err = rsa.DecryptPKCS1v15SessionKey(config.rand(),
cert.PrivateKey.(*rsa.PrivateKey), ciphertext, preMasterSecret)
+ priv, ok := cert.PrivateKey.(crypto.Decrypter)
+ if !ok {
+ return nil, errors.New("tls: certificate private key does not implement
crypto.Decrypter")
+ }
+ err = priv.Decrypt(config.rand(), ciphertext, preMasterSecret,
rsa.PKCS1v15Options{SessionKey: true})
if err != nil {
return nil, err
}
@@ -239,30 +243,30 @@
if err != nil {
return nil, err
}
+
+ priv, ok := cert.PrivateKey.(crypto.Signer)
+ if !ok {
+ err = errors.New("tls: certificate private key does not implement
crypto.Signer")
+ }
var sig []byte
switch ka.sigType {
case signatureECDSA:
- privKey, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
+ _, ok := priv.Public().(*ecdsa.PublicKey)
if !ok {
- return nil, errors.New("ECDHE ECDSA requires an ECDSA server private
key")
+ return nil, errors.New("ECDHE ECDSA requires an ECDSA server key")
}
- r, s, err := ecdsa.Sign(config.rand(), privKey, digest)
- if err != nil {
- return nil, errors.New("failed to sign ECDHE parameters: " +
err.Error())
- }
- sig, err = asn1.Marshal(ecdsaSignature{r, s})
case signatureRSA:
- privKey, ok := cert.PrivateKey.(*rsa.PrivateKey)
+ _, ok := priv.Public().(*rsa.PublicKey)
if !ok {
- return nil, errors.New("ECDHE RSA requires a RSA server private key")
- }
- sig, err = rsa.SignPKCS1v15(config.rand(), privKey, hashFunc, digest)
- if err != nil {
- return nil, errors.New("failed to sign ECDHE parameters: " +
err.Error())
+ return nil, errors.New("ECDHE RSA requires a RSA server key")
}
default:
return nil, errors.New("unknown ECDHE signature algorithm")
}
+ sig, err = priv.Sign(config.rand(), digest, hashFunc)
+ if err != nil {
+ return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
+ }
skx := new(serverKeyExchangeMsg)
sigAndHashLen := 0
--
https://go-review.googlesource.com/3900