diff options
-rw-r--r-- | src/pkg/crypto/crypto.go | 33 | ||||
-rw-r--r-- | src/pkg/crypto/ecdsa/ecdsa.go | 24 | ||||
-rw-r--r-- | src/pkg/crypto/rsa/pss.go | 15 | ||||
-rw-r--r-- | src/pkg/crypto/rsa/rsa.go | 19 | ||||
-rw-r--r-- | src/pkg/crypto/tls/common.go | 7 | ||||
-rw-r--r-- | src/pkg/crypto/tls/handshake_client.go | 30 | ||||
-rw-r--r-- | src/pkg/go/build/deps_test.go | 2 |
7 files changed, 114 insertions, 16 deletions
diff --git a/src/pkg/crypto/crypto.go b/src/pkg/crypto/crypto.go index 4b03628e6..c3a2364fe 100644 --- a/src/pkg/crypto/crypto.go +++ b/src/pkg/crypto/crypto.go @@ -7,6 +7,7 @@ package crypto import ( "hash" + "io" "strconv" ) @@ -14,6 +15,11 @@ import ( // package. type Hash uint +// HashFunc simply returns the value of h so that Hash implements SignerOpts. +func (h Hash) HashFunc() Hash { + return h +} + const ( MD4 Hash = 1 + iota // import code.google.com/p/go.crypto/md4 MD5 // import crypto/md5 @@ -83,3 +89,30 @@ type PublicKey interface{} // PrivateKey represents a private key using an unspecified algorithm. type PrivateKey interface{} + +// Signer is an interface for an opaque private key that can be used for +// signing operations. For example, an RSA key kept in a hardware module. +type Signer interface { + // Public returns the public key corresponding to the opaque, + // private key. + Public() PublicKey + + // Sign signs msg with the private key, possibly using entropy from + // rand. For an RSA key, the resulting signature should be either a + // PKCS#1 v1.5 or PSS signature (as indicated by opts). For an (EC)DSA + // key, it should be a DER-serialised, ASN.1 signature structure. + // + // Hash implements the SignerOpts interface and, in most cases, one can + // simply pass in the hash function used as opts. Sign may also attempt + // to type assert opts to other types in order to obtain algorithm + // specific values. See the documentation in each package for details. + Sign(rand io.Reader, msg []byte, opts SignerOpts) (signature []byte, err error) +} + +// SignerOpts contains options for signing with a Signer. +type SignerOpts interface { + // HashFunc returns an identifier for the hash function used to produce + // the message passed to Signer.Sign, or else zero to indicate that no + // hashing was done. + HashFunc() Hash +} diff --git a/src/pkg/crypto/ecdsa/ecdsa.go b/src/pkg/crypto/ecdsa/ecdsa.go index 1bec7437a..d6135531b 100644 --- a/src/pkg/crypto/ecdsa/ecdsa.go +++ b/src/pkg/crypto/ecdsa/ecdsa.go @@ -13,7 +13,9 @@ package ecdsa // http://www.secg.org/download/aid-780/sec1-v2.pdf import ( + "crypto" "crypto/elliptic" + "encoding/asn1" "io" "math/big" ) @@ -30,6 +32,28 @@ type PrivateKey struct { D *big.Int } +type ecdsaSignature struct { + R, S *big.Int +} + +// Public returns the public key corresponding to priv. +func (priv *PrivateKey) Public() crypto.PublicKey { + return &priv.PublicKey +} + +// Sign signs msg with priv, reading randomness from rand. This method is +// intended to support keys where the private part is kept in, for example, a +// hardware module. Common uses should use the Sign function in this package +// directly. +func (priv *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) { + r, s, err := Sign(rand, priv, msg) + if err != nil { + return nil, err + } + + return asn1.Marshal(ecdsaSignature{r, s}) +} + var one = new(big.Int).SetInt64(1) // randFieldElement returns a random element of the field underlying the given diff --git a/src/pkg/crypto/rsa/pss.go b/src/pkg/crypto/rsa/pss.go index 18eafbc05..e9f290825 100644 --- a/src/pkg/crypto/rsa/pss.go +++ b/src/pkg/crypto/rsa/pss.go @@ -222,6 +222,17 @@ type PSSOptions struct { // signature. It can either be a number of bytes, or one of the special // PSSSaltLength constants. SaltLength int + + // Hash, if not zero, overrides the hash function passed to SignPSS. + // This is the only way to specify the hash function when using the + // crypto.Signer interface. + Hash crypto.Hash +} + +// HashFunc returns pssOpts.Hash so that PSSOptions implements +// crypto.SignerOpts. +func (pssOpts *PSSOptions) HashFunc() crypto.Hash { + return pssOpts.Hash } func (opts *PSSOptions) saltLength() int { @@ -244,6 +255,10 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte, saltLength = hash.Size() } + if opts.Hash != 0 { + hash = opts.Hash + } + salt := make([]byte, saltLength) if _, err = io.ReadFull(rand, salt); err != nil { return diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go index bce6ba4eb..270231128 100644 --- a/src/pkg/crypto/rsa/rsa.go +++ b/src/pkg/crypto/rsa/rsa.go @@ -6,6 +6,7 @@ package rsa import ( + "crypto" "crypto/rand" "crypto/subtle" "errors" @@ -58,6 +59,24 @@ type PrivateKey struct { Precomputed PrecomputedValues } +// Public returns the public key corresponding to priv. +func (priv *PrivateKey) Public() crypto.PublicKey { + return &priv.PublicKey +} + +// Sign signs msg 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. This method is intended to support keys where the private part is +// kept in, for example, a hardware module. Common uses should use the Sign* +// functions in this package. +func (priv *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) { + if pssOpts, ok := opts.(*PSSOptions); ok { + return SignPSS(rand, priv, pssOpts.Hash, msg, pssOpts) + } + + return SignPKCS1v15(rand, priv, opts.HashFunc(), msg) +} + 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/pkg/crypto/tls/common.go b/src/pkg/crypto/tls/common.go index f926d5772..776b70c93 100644 --- a/src/pkg/crypto/tls/common.go +++ b/src/pkg/crypto/tls/common.go @@ -487,7 +487,12 @@ func (c *Config) BuildNameToCertificate() { // A Certificate is a chain of one or more certificates, leaf first. type Certificate struct { Certificate [][]byte - PrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey, *ecdsa.PrivateKey + // 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). + PrivateKey crypto.PrivateKey // OCSPStaple contains an optional OCSP response which will be served // to clients that request it. OCSPStaple []byte diff --git a/src/pkg/crypto/tls/handshake_client.go b/src/pkg/crypto/tls/handshake_client.go index 3d9ef9b14..7f662e9c9 100644 --- a/src/pkg/crypto/tls/handshake_client.go +++ b/src/pkg/crypto/tls/handshake_client.go @@ -6,11 +6,11 @@ package tls import ( "bytes" + "crypto" "crypto/ecdsa" "crypto/rsa" "crypto/subtle" "crypto/x509" - "encoding/asn1" "errors" "fmt" "io" @@ -345,8 +345,8 @@ func (hs *clientHandshakeState) doFullHandshake() error { } // We need to search our list of client certs for one - // where SignatureAlgorithm is RSA and the Issuer is in - // certReq.certificateAuthorities + // where SignatureAlgorithm is acceptable to the server and the + // Issuer is in certReq.certificateAuthorities findCert: for i, chain := range c.config.Certificates { if !rsaAvail && !ecdsaAvail { @@ -373,7 +373,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { if len(certReq.certificateAuthorities) == 0 { // they gave us an empty list, so just take the - // first RSA cert from c.config.Certificates + // first cert from c.config.Certificates chainToSend = &chain break findCert } @@ -428,22 +428,24 @@ func (hs *clientHandshakeState) doFullHandshake() error { hasSignatureAndHash: c.vers >= VersionTLS12, } - switch key := c.config.Certificates[0].PrivateKey.(type) { - case *ecdsa.PrivateKey: - digest, _, hashId := hs.finishedHash.hashForClientCertificate(signatureECDSA) - r, s, err := ecdsa.Sign(c.config.rand(), key, digest) - if err == nil { - signed, err = asn1.Marshal(ecdsaSignature{r, s}) - } + key, ok := chainToSend.PrivateKey.(crypto.Signer) + if !ok { + c.sendAlert(alertInternalError) + return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey) + } + switch key.Public().(type) { + case *ecdsa.PublicKey: + digest, hashFunc, hashId := hs.finishedHash.hashForClientCertificate(signatureECDSA) + signed, err = key.Sign(c.config.rand(), digest, hashFunc) certVerify.signatureAndHash.signature = signatureECDSA certVerify.signatureAndHash.hash = hashId - case *rsa.PrivateKey: + case *rsa.PublicKey: digest, hashFunc, hashId := hs.finishedHash.hashForClientCertificate(signatureRSA) - signed, err = rsa.SignPKCS1v15(c.config.rand(), key, hashFunc, digest) + signed, err = key.Sign(c.config.rand(), digest, hashFunc) certVerify.signatureAndHash.signature = signatureRSA certVerify.signatureAndHash.hash = hashId default: - err = errors.New("unknown private key type") + err = fmt.Errorf("tls: unknown client certificate key type: %T", key) } if err != nil { c.sendAlert(alertInternalError) diff --git a/src/pkg/go/build/deps_test.go b/src/pkg/go/build/deps_test.go index 2a7173ba4..b74595ea8 100644 --- a/src/pkg/go/build/deps_test.go +++ b/src/pkg/go/build/deps_test.go @@ -284,7 +284,7 @@ var pkgDeps = map[string][]string{ // Mathematical crypto: dependencies on fmt (L4) and math/big. // We could avoid some of the fmt, but math/big imports fmt anyway. "crypto/dsa": {"L4", "CRYPTO", "math/big"}, - "crypto/ecdsa": {"L4", "CRYPTO", "crypto/elliptic", "math/big"}, + "crypto/ecdsa": {"L4", "CRYPTO", "crypto/elliptic", "math/big", "encoding/asn1"}, "crypto/elliptic": {"L4", "CRYPTO", "math/big"}, "crypto/rsa": {"L4", "CRYPTO", "crypto/rand", "math/big"}, |