mirror of https://github.com/golang/go.git
crypto: add Signer
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
This commit is contained in:
parent
7dc2b3cbd1
commit
7f2e68e982
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue