Commit 7f2e68e9 authored by Adam Langley's avatar Adam Langley

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
parent 7dc2b3cb
......@@ -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"},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment