]> Cypherpunks repositories - gostls13.git/commitdiff
crypto: add Signer
authorAdam Langley <agl@golang.org>
Fri, 29 Aug 2014 19:36:30 +0000 (12:36 -0700)
committerAdam Langley <agl@golang.org>
Fri, 29 Aug 2014 19:36:30 +0000 (12:36 -0700)
Signer is an interface to support opaque private keys.
These keys typically result from being kept in special hardware
(i.e. a TPM) although sometimes operating systems provide a
similar interface using process isolation for security rather
than hardware boundaries.

This changes provides interfaces for representing them and
alters crypto/tls so that client certificates can use
opaque keys.

LGTM=bradfitz
R=bradfitz
CC=golang-codereviews, jdeprez
https://golang.org/cl/114680043

src/pkg/crypto/crypto.go
src/pkg/crypto/ecdsa/ecdsa.go
src/pkg/crypto/rsa/pss.go
src/pkg/crypto/rsa/rsa.go
src/pkg/crypto/tls/common.go
src/pkg/crypto/tls/handshake_client.go
src/pkg/go/build/deps_test.go

index 4b03628e69297f6ce303d7c8ed65c0f1de5f3987..c3a2364fe2d8a2cdce6b676775ab933ee51b5794 100644 (file)
@@ -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
+}
index 1bec7437a5368d1a3bf61a5c888dce95e012fbe2..d6135531bff762ec43edfbb928bc0a6df9234961 100644 (file)
@@ -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
index 18eafbc05f7cf547b4339fbe1f1840d0ae7613c8..e9f2908250cc3a2a9d3f2fc1c1a6cc875ccf6805 100644 (file)
@@ -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
index bce6ba4eba31121edfb394f6112b23500461ac14..2702311281ce07a64c71ff56223d4a75c0041bf0 100644 (file)
@@ -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
index f926d57728e0387b7ada1cf1f7eecf46836890c0..776b70c93c8fc632b6b82be296220b14dec3d0a5 100644 (file)
@@ -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
index 3d9ef9b14e497a3a10e2ae708df9371fb5e9e889..7f662e9c9f3501f5ded4b276dda1edaea958d926 100644 (file)
@@ -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)
index 2a7173ba4c42081c1b5fd9b68fde3f3db6d5cae2..b74595ea837e39ae3b0569dc01aa898df51c0669 100644 (file)
@@ -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"},