Remove delegated credential minting from the API
What's left are the minimal API changes required to use the delegated credential extension in the TLS handshake.
This commit is contained in:
parent
1ea9624098
commit
c5280001a4
4
13.go
4
13.go
@ -408,7 +408,7 @@ func (hs *serverHandshakeState) sendCertificate13() error {
|
|||||||
// server is using the delegated credential extension. In TLS 1.3, the DC is
|
// server is using the delegated credential extension. In TLS 1.3, the DC is
|
||||||
// added as an extension to the end-entity certificate, i.e., the last
|
// added as an extension to the end-entity certificate, i.e., the last
|
||||||
// CertificateEntry of Certificate.certficate_list (see
|
// CertificateEntry of Certificate.certficate_list (see
|
||||||
// https://tools.ietf.org/html/draft-ietf-tls-subcerts).
|
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-01).
|
||||||
if len(certEntries) > 0 && hs.clientHello.delegatedCredential && hs.delegatedCredential != nil {
|
if len(certEntries) > 0 && hs.clientHello.delegatedCredential && hs.delegatedCredential != nil {
|
||||||
certEntries[0].delegatedCredential = hs.delegatedCredential
|
certEntries[0].delegatedCredential = hs.delegatedCredential
|
||||||
}
|
}
|
||||||
@ -1069,7 +1069,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error {
|
|||||||
// then the CertificateVerify signature will have been produced with the
|
// then the CertificateVerify signature will have been produced with the
|
||||||
// DelegatedCredential's private key.
|
// DelegatedCredential's private key.
|
||||||
if hs.c.verifiedDc != nil {
|
if hs.c.verifiedDc != nil {
|
||||||
pk = hs.c.verifiedDc.PublicKey
|
pk = hs.c.verifiedDc.cred.publicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receive CertificateVerify message.
|
// Receive CertificateVerify message.
|
||||||
|
17
common.go
17
common.go
@ -219,6 +219,7 @@ type ConnectionState struct {
|
|||||||
VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates
|
VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates
|
||||||
SignedCertificateTimestamps [][]byte // SCTs from the server, if any
|
SignedCertificateTimestamps [][]byte // SCTs from the server, if any
|
||||||
OCSPResponse []byte // stapled OCSP response from server, if any
|
OCSPResponse []byte // stapled OCSP response from server, if any
|
||||||
|
DelegatedCredential []byte // Delegated credential sent by the server, if any
|
||||||
|
|
||||||
// TLSUnique contains the "tls-unique" channel binding value (see RFC
|
// TLSUnique contains the "tls-unique" channel binding value (see RFC
|
||||||
// 5929, section 3). For resumed sessions this value will be nil
|
// 5929, section 3). For resumed sessions this value will be nil
|
||||||
@ -619,16 +620,20 @@ type Config struct {
|
|||||||
//
|
//
|
||||||
// This value has no meaning for the server.
|
// This value has no meaning for the server.
|
||||||
//
|
//
|
||||||
// See https://tools.ietf.org/html/draft-ietf-tls-subcerts.
|
// See https://tools.ietf.org/html/draft-ietf-tls-subcerts-01.
|
||||||
AcceptDelegatedCredential bool
|
AcceptDelegatedCredential bool
|
||||||
|
|
||||||
// GetDelegatedCredential returns a DelegatedCredential for use with the
|
// GetDelegatedCredential returns a DC and its private key for use in the
|
||||||
// delegated credential extension based on the ClientHello and TLS version
|
// delegated credential extension. The inputs to the callback are some
|
||||||
// selected for the session. If this is nil, then the server will not offer
|
// information parsed from the ClientHello, as well as the protocol version
|
||||||
// a DelegatedCredential.
|
// selected by the server. This is necessary because the DC is bound to
|
||||||
|
// protocol version in which it's used. The return value is the raw DC
|
||||||
|
// encoded in the wire format specified in
|
||||||
|
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-01. If the return
|
||||||
|
// value is nil, then the server will not offer negotiate the extension.
|
||||||
//
|
//
|
||||||
// This value has no meaning for the client.
|
// This value has no meaning for the client.
|
||||||
GetDelegatedCredential func(*ClientHelloInfo, uint16) (*DelegatedCredential, crypto.PrivateKey, error)
|
GetDelegatedCredential func(*ClientHelloInfo, uint16) ([]byte, crypto.PrivateKey, error)
|
||||||
|
|
||||||
serverInitOnce sync.Once // guards calling (*Config).serverInit
|
serverInitOnce sync.Once // guards calling (*Config).serverInit
|
||||||
|
|
||||||
|
5
conn.go
5
conn.go
@ -58,7 +58,7 @@ type Conn struct {
|
|||||||
verifiedChains [][]*x509.Certificate
|
verifiedChains [][]*x509.Certificate
|
||||||
// verifiedDc is set by a client who negotiates the use of a valid delegated
|
// verifiedDc is set by a client who negotiates the use of a valid delegated
|
||||||
// credential.
|
// credential.
|
||||||
verifiedDc *DelegatedCredential
|
verifiedDc *delegatedCredential
|
||||||
// serverName contains the server name indicated by the client, if any.
|
// serverName contains the server name indicated by the client, if any.
|
||||||
serverName string
|
serverName string
|
||||||
// secureRenegotiation is true if the server echoed the secure
|
// secureRenegotiation is true if the server echoed the secure
|
||||||
@ -1666,6 +1666,9 @@ func (c *Conn) ConnectionState() ConnectionState {
|
|||||||
state.VerifiedChains = c.verifiedChains
|
state.VerifiedChains = c.verifiedChains
|
||||||
state.SignedCertificateTimestamps = c.scts
|
state.SignedCertificateTimestamps = c.scts
|
||||||
state.OCSPResponse = c.ocspResponse
|
state.OCSPResponse = c.ocspResponse
|
||||||
|
if c.verifiedDc != nil {
|
||||||
|
state.DelegatedCredential = c.verifiedDc.raw
|
||||||
|
}
|
||||||
state.HandshakeConfirmed = atomic.LoadInt32(&c.handshakeConfirmed) == 1
|
state.HandshakeConfirmed = atomic.LoadInt32(&c.handshakeConfirmed) == 1
|
||||||
if !state.HandshakeConfirmed {
|
if !state.HandshakeConfirmed {
|
||||||
state.Unique0RTTToken = c.binder
|
state.Unique0RTTToken = c.binder
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
@ -35,7 +34,6 @@ var (
|
|||||||
isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority")
|
isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority")
|
||||||
rsaBits = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set")
|
rsaBits = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set")
|
||||||
ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521")
|
ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521")
|
||||||
isDC = flag.Bool("dc", false, "whether this cert can be used with delegated credentials")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func publicKey(priv interface{}) interface{} {
|
func publicKey(priv interface{}) interface{} {
|
||||||
@ -139,11 +137,6 @@ func main() {
|
|||||||
template.KeyUsage |= x509.KeyUsageCertSign
|
template.KeyUsage |= x509.KeyUsageCertSign
|
||||||
}
|
}
|
||||||
|
|
||||||
if *isDC {
|
|
||||||
template.ExtraExtensions = append(
|
|
||||||
template.ExtraExtensions, *tls.CreateDelegationUsagePKIXExtension())
|
|
||||||
}
|
|
||||||
|
|
||||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
|
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to create certificate: %s", err)
|
log.Fatalf("Failed to create certificate: %s", err)
|
||||||
|
@ -413,15 +413,15 @@ func (hs *clientHandshakeState) processCertsFromServer(certificates [][]byte) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// processDelegatedCredentialFromServer unmarshals the DelegatedCredential
|
// processDelegatedCredentialFromServer unmarshals the delegated credential
|
||||||
// offered by the server (if present) and validates it using the peer
|
// offered by the server (if present) and validates it using the peer
|
||||||
// certificate.
|
// certificate.
|
||||||
func (hs *clientHandshakeState) processDelegatedCredentialFromServer(dc []byte) error {
|
func (hs *clientHandshakeState) processDelegatedCredentialFromServer(serialized []byte) error {
|
||||||
c := hs.c
|
c := hs.c
|
||||||
|
|
||||||
var cred *DelegatedCredential
|
var dc *delegatedCredential
|
||||||
var err error
|
var err error
|
||||||
if dc != nil {
|
if serialized != nil {
|
||||||
|
|
||||||
// Assert that the DC extension was indicated by the client.
|
// Assert that the DC extension was indicated by the client.
|
||||||
if !hs.hello.delegatedCredential {
|
if !hs.hello.delegatedCredential {
|
||||||
@ -436,15 +436,15 @@ func (hs *clientHandshakeState) processDelegatedCredentialFromServer(dc []byte)
|
|||||||
return errors.New("tls: ServerHello with delegated credential extension in TLS != 1.2")
|
return errors.New("tls: ServerHello with delegated credential extension in TLS != 1.2")
|
||||||
}
|
}
|
||||||
|
|
||||||
cred, err = UnmarshalDelegatedCredential(dc)
|
dc, err = unmarshalDelegatedCredential(serialized)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.sendAlert(alertDecodeError)
|
c.sendAlert(alertDecodeError)
|
||||||
return fmt.Errorf("tls: delegated credential: %s", err)
|
return fmt.Errorf("tls: delegated credential: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cred != nil && !c.config.InsecureSkipVerify {
|
if dc != nil && !c.config.InsecureSkipVerify {
|
||||||
if v, err := cred.Validate(c.peerCertificates[0], hs.c.vers, c.config.time()); err != nil {
|
if v, err := dc.validate(c.peerCertificates[0], hs.c.vers, c.config.time()); err != nil {
|
||||||
c.sendAlert(alertIllegalParameter)
|
c.sendAlert(alertIllegalParameter)
|
||||||
return fmt.Errorf("delegated credential: %s", err)
|
return fmt.Errorf("delegated credential: %s", err)
|
||||||
} else if !v {
|
} else if !v {
|
||||||
@ -453,7 +453,7 @@ func (hs *clientHandshakeState) processDelegatedCredentialFromServer(dc []byte)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.verifiedDc = cred
|
c.verifiedDc = dc
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,9 +533,10 @@ func (hs *clientHandshakeState) doFullHandshake() error {
|
|||||||
pk := c.peerCertificates[0].PublicKey
|
pk := c.peerCertificates[0].PublicKey
|
||||||
|
|
||||||
// If the delegated credential extension has successfully been negotiated,
|
// If the delegated credential extension has successfully been negotiated,
|
||||||
// then the ServerKeyExchange DelegatedCredential's private key.
|
// then the ServerKeyExchange is signed with delegated credential private
|
||||||
|
// key.
|
||||||
if c.verifiedDc != nil {
|
if c.verifiedDc != nil {
|
||||||
pk = c.verifiedDc.PublicKey
|
pk = c.verifiedDc.cred.publicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
skx, ok := msg.(*serverKeyExchangeMsg)
|
skx, ok := msg.(*serverKeyExchangeMsg)
|
||||||
|
@ -759,7 +759,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) alert {
|
|||||||
// https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.8
|
// https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.8
|
||||||
m.earlyData = true
|
m.earlyData = true
|
||||||
case extensionDelegatedCredential:
|
case extensionDelegatedCredential:
|
||||||
// https://tools.ietf.org/html/draft-ietf-tls-subcerts
|
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-01
|
||||||
m.delegatedCredential = true
|
m.delegatedCredential = true
|
||||||
}
|
}
|
||||||
data = data[length:]
|
data = data[length:]
|
||||||
|
@ -329,14 +329,7 @@ Curves:
|
|||||||
// Set the handshake private key.
|
// Set the handshake private key.
|
||||||
if dc != nil {
|
if dc != nil {
|
||||||
hs.privateKey = sk
|
hs.privateKey = sk
|
||||||
if dc.Raw == nil {
|
hs.delegatedCredential = dc
|
||||||
dc.Raw, err = dc.Marshal()
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hs.delegatedCredential = dc.Raw
|
|
||||||
|
|
||||||
// For TLS 1.2, the DC is an extension to the ServerHello.
|
// For TLS 1.2, the DC is an extension to the ServerHello.
|
||||||
if c.vers == VersionTLS12 {
|
if c.vers == VersionTLS12 {
|
||||||
|
280
subcerts.go
280
subcerts.go
@ -5,29 +5,30 @@
|
|||||||
package tls
|
package tls
|
||||||
|
|
||||||
// Delegated credentials for TLS
|
// Delegated credentials for TLS
|
||||||
// (https://tools.ietf.org/html/draft-ietf-tls-subcerts) is an IETF Internet
|
// (https://tools.ietf.org/html/draft-ietf-tls-subcerts-01) is an IETF Internet
|
||||||
// draft and proposed TLS extension. If the client supports this extension, then
|
// draft and proposed TLS extension. This allows a backend server to delegate
|
||||||
// the server may use a "delegated credential" as the signing key in the
|
// TLS termination to a trusted frontend. If the client supports this extension,
|
||||||
// handshake. A delegated credential is a short lived public/secret key pair
|
// then the frontend may use a "delegated credential" as the signing key in the
|
||||||
// delegated to the server by an entity trusted by the client. This allows a
|
// handshake. A delegated credential is a short lived key pair delegated to the
|
||||||
// middlebox to terminate a TLS connection on behalf of the entity; for example,
|
// server by an entity trusted by the client. Once issued, credentials can't be
|
||||||
// this can be used to delegate TLS termination to a reverse proxy. Credentials
|
// revoked; in order to mitigate risk in case the frontend is compromised, the
|
||||||
// can't be revoked; in order to mitigate risk in case the middlebox is
|
// credential is only valid for a short time (days, hours, or even minutes).
|
||||||
// compromised, the credential is only valid for a short time (days, hours, or
|
|
||||||
// even minutes).
|
|
||||||
//
|
//
|
||||||
// BUG(cjpatton) Subcerts: Need to add support for PKCS1, PSS, and EdDSA.
|
// This implements draft 01. This draft doesn't specify an object identifier for
|
||||||
// Currently delegated credentials only support ECDSA. The delegator must also
|
// the X.509 extension; we use one assigned by Cloudflare. In addition, IANA has
|
||||||
// use an ECDSA key.
|
// not assigned an extension ID for this extension; we picked up one that's not
|
||||||
|
// yet taken.
|
||||||
|
//
|
||||||
|
// TODO(cjpatton) Only ECDSA is supported with delegated credentials for now;
|
||||||
|
// we'd like to suppoort for EcDSA signatures once these have better support
|
||||||
|
// upstream.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
@ -49,22 +50,6 @@ var errNoDelegationUsage = errors.New("certificate not authorized for delegation
|
|||||||
// NOTE(cjpatton) This OID is a child of Cloudflare's IANA-assigned OID.
|
// NOTE(cjpatton) This OID is a child of Cloudflare's IANA-assigned OID.
|
||||||
var delegationUsageId = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 44}
|
var delegationUsageId = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 44}
|
||||||
|
|
||||||
// CreateDelegationUsagePKIXExtension returns a pkix.Extension that every delegation
|
|
||||||
// certificate must have.
|
|
||||||
//
|
|
||||||
// NOTE(cjpatton) Brendan McMillion suggests adding the delegationUsage
|
|
||||||
// extension as a flag `PermitsDelegationUsage` for the `x509.Certificate`
|
|
||||||
// structure. But we can't make this change unless tris includes crypto/x509,
|
|
||||||
// too. Once we upstream this code, we'll want to do modify x509.Certficate and
|
|
||||||
// do away with this function.
|
|
||||||
func CreateDelegationUsagePKIXExtension() *pkix.Extension {
|
|
||||||
return &pkix.Extension{
|
|
||||||
Id: delegationUsageId,
|
|
||||||
Critical: false,
|
|
||||||
Value: nil,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// canDelegate returns true if a certificate can be used for delegated
|
// canDelegate returns true if a certificate can be used for delegated
|
||||||
// credentials.
|
// credentials.
|
||||||
func canDelegate(cert *x509.Certificate) bool {
|
func canDelegate(cert *x509.Certificate) bool {
|
||||||
@ -83,11 +68,41 @@ func canDelegate(cert *x509.Certificate) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// This structure stores the public components of a credential.
|
// credential stores the public components of a credential.
|
||||||
type credential struct {
|
type credential struct {
|
||||||
|
// The amount of time for which the credential is valid. Specifically, the
|
||||||
|
// credential expires `ValidTime` seconds after the `notBefore` of the
|
||||||
|
// delegation certificate. The delegator shall not issue delegated
|
||||||
|
// credentials that are valid for more than 7 days from the current time.
|
||||||
|
//
|
||||||
|
// When this data structure is serialized, this value is converted to a
|
||||||
|
// uint32 representing the duration in seconds.
|
||||||
validTime time.Duration
|
validTime time.Duration
|
||||||
|
|
||||||
|
// The credential public key.
|
||||||
publicKey crypto.PublicKey
|
publicKey crypto.PublicKey
|
||||||
scheme SignatureScheme
|
|
||||||
|
// The signature scheme associated with the credential public key.
|
||||||
|
//
|
||||||
|
// NOTE(cjpatton) This is used for bookkeeping and is not actually part of
|
||||||
|
// the credential structure specified in the standard.
|
||||||
|
scheme SignatureScheme
|
||||||
|
}
|
||||||
|
|
||||||
|
// isExpired returns true if the credential has expired. The end of the validity
|
||||||
|
// interval is defined as the delegator certificate's notBefore field (`start`)
|
||||||
|
// plus ValidTime seconds. This function simply checks that the current time
|
||||||
|
// (`now`) is before the end of the valdity interval.
|
||||||
|
func (cred *credential) isExpired(start, now time.Time) bool {
|
||||||
|
end := start.Add(cred.validTime)
|
||||||
|
return !now.Before(end)
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalidTTL returns true if the credential's validity period is longer than the
|
||||||
|
// maximum permitted. This is defined by the certificate's notBefore field
|
||||||
|
// (`start`) plus the ValidTime, minus the current time (`now`).
|
||||||
|
func (cred *credential) invalidTTL(start, now time.Time) bool {
|
||||||
|
return cred.validTime > (now.Sub(start) + dcMaxTTL).Round(time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
// marshalSubjectPublicKeyInfo returns a DER encoded SubjectPublicKeyInfo structure
|
// marshalSubjectPublicKeyInfo returns a DER encoded SubjectPublicKeyInfo structure
|
||||||
@ -108,7 +123,8 @@ func (cred *credential) marshalSubjectPublicKeyInfo() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// marshal encodes a credential as per the spec.
|
// marshal encodes a credential in the wire format specified in
|
||||||
|
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-01.
|
||||||
func (cred *credential) marshal() ([]byte, error) {
|
func (cred *credential) marshal() ([]byte, error) {
|
||||||
// Write the valid_time field.
|
// Write the valid_time field.
|
||||||
serialized := make([]byte, 6)
|
serialized := make([]byte, 6)
|
||||||
@ -198,173 +214,62 @@ func getCredentialLen(serialized []byte) (int, error) {
|
|||||||
return 6 + serializedPublicKeyLen, nil
|
return 6 + serializedPublicKeyLen, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DelegatedCredential stores a credential and its delegation.
|
// delegatedCredential stores a credential and its delegation.
|
||||||
type DelegatedCredential struct {
|
type delegatedCredential struct {
|
||||||
// The serialized form of the credential.
|
raw []byte
|
||||||
Raw []byte
|
|
||||||
|
|
||||||
// The amount of time for which the credential is valid. Specifically, the
|
// The credential, which contains a public and its validity time.
|
||||||
// the credential expires `ValidTime` seconds after the `notBefore` of the
|
cred *credential
|
||||||
// delegation certificate. The delegator shall not issue delegated
|
|
||||||
// credentials that are valid for more than 7 days from the current time.
|
|
||||||
//
|
|
||||||
// When this data structure is serialized, this value is converted to a
|
|
||||||
// uint32 representing the duration in seconds.
|
|
||||||
ValidTime time.Duration
|
|
||||||
|
|
||||||
// The credential public key.
|
|
||||||
PublicKey crypto.PublicKey
|
|
||||||
|
|
||||||
// The signature scheme associated with the credential public key.
|
|
||||||
publicKeyScheme SignatureScheme
|
|
||||||
|
|
||||||
// The signature scheme used to sign the credential.
|
// The signature scheme used to sign the credential.
|
||||||
Scheme SignatureScheme
|
scheme SignatureScheme
|
||||||
|
|
||||||
// The credential's delegation.
|
// The credential's delegation.
|
||||||
Signature []byte
|
signature []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDelegatedCredential creates a new delegated credential using `cert` for
|
// ensureCertificateHasLeaf parses the leaf certificate if needed.
|
||||||
// delegation. It generates a public/private key pair for the provided signature
|
func ensureCertificateHasLeaf(cert *Certificate) error {
|
||||||
// algorithm (`scheme`), validity interval (defined by `cert.Leaf.notBefore` and
|
|
||||||
// `validTime`), and TLS version (`vers`), and signs it using `cert.PrivateKey`.
|
|
||||||
func NewDelegatedCredential(cert *Certificate, scheme SignatureScheme, validTime time.Duration, vers uint16) (*DelegatedCredential, crypto.PrivateKey, error) {
|
|
||||||
// The granularity of DC validity is seconds.
|
|
||||||
validTime = validTime.Round(time.Second)
|
|
||||||
|
|
||||||
// Parse the leaf certificate if needed.
|
|
||||||
var err error
|
var err error
|
||||||
if cert.Leaf == nil {
|
if cert.Leaf == nil {
|
||||||
if len(cert.Certificate[0]) == 0 {
|
if len(cert.Certificate[0]) == 0 {
|
||||||
return nil, nil, errors.New("missing leaf certificate")
|
return errors.New("missing leaf certificate")
|
||||||
}
|
}
|
||||||
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
|
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
// Check that the leaf certificate can be used for delegation.
|
|
||||||
if !canDelegate(cert.Leaf) {
|
|
||||||
return nil, nil, errNoDelegationUsage
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the delegator signature scheme from the certificate.
|
|
||||||
var delegatorScheme SignatureScheme
|
|
||||||
switch sk := cert.PrivateKey.(type) {
|
|
||||||
case *ecdsa.PrivateKey:
|
|
||||||
// Set scheme.
|
|
||||||
pk := sk.Public().(*ecdsa.PublicKey)
|
|
||||||
curveName := pk.Curve.Params().Name
|
|
||||||
certAlg := cert.Leaf.SignatureAlgorithm
|
|
||||||
if certAlg == x509.ECDSAWithSHA256 && curveName == "P-256" {
|
|
||||||
delegatorScheme = ECDSAWithP256AndSHA256
|
|
||||||
} else if certAlg == x509.ECDSAWithSHA384 && curveName == "P-384" {
|
|
||||||
delegatorScheme = ECDSAWithP384AndSHA384
|
|
||||||
} else if certAlg == x509.ECDSAWithSHA512 && curveName == "P-521" {
|
|
||||||
delegatorScheme = ECDSAWithP521AndSHA512
|
|
||||||
} else {
|
|
||||||
return nil, nil, fmt.Errorf(
|
|
||||||
"using curve %s for %s is not supported",
|
|
||||||
curveName, cert.Leaf.SignatureAlgorithm)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, nil, fmt.Errorf("unsupported delgation key type: %T", sk)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a new key pair.
|
|
||||||
var sk crypto.PrivateKey
|
|
||||||
var pk crypto.PublicKey
|
|
||||||
switch scheme {
|
|
||||||
case ECDSAWithP256AndSHA256,
|
|
||||||
ECDSAWithP384AndSHA384,
|
|
||||||
ECDSAWithP521AndSHA512:
|
|
||||||
sk, err = ecdsa.GenerateKey(getCurve(scheme), rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
pk = sk.(*ecdsa.PrivateKey).Public()
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, nil, fmt.Errorf("unsupported signature scheme: 0x%04x", scheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare the credential for digital signing.
|
|
||||||
hash := getHash(delegatorScheme)
|
|
||||||
cred := &credential{validTime, pk, scheme}
|
|
||||||
in, err := prepareDelegation(hash, cred, cert.Leaf.Raw, delegatorScheme, vers)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign the credential.
|
|
||||||
var sig []byte
|
|
||||||
switch sk := cert.PrivateKey.(type) {
|
|
||||||
case *ecdsa.PrivateKey:
|
|
||||||
opts := crypto.SignerOpts(hash)
|
|
||||||
sig, err = sk.Sign(rand.Reader, in, opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, nil, fmt.Errorf("unsupported delgation key type: %T", sk)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &DelegatedCredential{
|
|
||||||
ValidTime: validTime,
|
|
||||||
PublicKey: pk,
|
|
||||||
publicKeyScheme: scheme,
|
|
||||||
Scheme: delegatorScheme,
|
|
||||||
Signature: sig,
|
|
||||||
}, sk, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsExpired returns true if the credential has expired. The end of the validity
|
// validate checks that that the signature is valid, that the credential hasn't
|
||||||
// interval is defined as the delegator certificate's notBefore field (`start`)
|
|
||||||
// plus ValidTime seconds. This function simply checks that the current time
|
|
||||||
// (`now`) is before the end of the valdity interval.
|
|
||||||
func (dc *DelegatedCredential) IsExpired(start, now time.Time) bool {
|
|
||||||
end := start.Add(dc.ValidTime)
|
|
||||||
return !now.Before(end)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvalidTTL returns true if the credential's validity period is longer than the
|
|
||||||
// maximum permitted. This is defined by the certificate's notBefore field
|
|
||||||
// (`start`) plus the ValidTime, minus the current time (`now`).
|
|
||||||
func (dc *DelegatedCredential) InvalidTTL(start, now time.Time) bool {
|
|
||||||
return dc.ValidTime > (now.Sub(start) + dcMaxTTL).Round(time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate checks that that the signature is valid, that the credential hasn't
|
|
||||||
// expired, and that the TTL is valid. It also checks that certificate can be
|
// expired, and that the TTL is valid. It also checks that certificate can be
|
||||||
// used for delegation.
|
// used for delegation.
|
||||||
func (dc *DelegatedCredential) Validate(cert *x509.Certificate, vers uint16, now time.Time) (bool, error) {
|
func (dc *delegatedCredential) validate(cert *x509.Certificate, vers uint16, now time.Time) (bool, error) {
|
||||||
// Check that the cert can delegate.
|
// Check that the cert can delegate.
|
||||||
if !canDelegate(cert) {
|
if !canDelegate(cert) {
|
||||||
return false, errNoDelegationUsage
|
return false, errNoDelegationUsage
|
||||||
}
|
}
|
||||||
|
|
||||||
if dc.IsExpired(cert.NotBefore, now) {
|
if dc.cred.isExpired(cert.NotBefore, now) {
|
||||||
return false, errors.New("credential has expired")
|
return false, errors.New("credential has expired")
|
||||||
}
|
}
|
||||||
|
|
||||||
if dc.InvalidTTL(cert.NotBefore, now) {
|
if dc.cred.invalidTTL(cert.NotBefore, now) {
|
||||||
return false, errors.New("credential TTL is invalid")
|
return false, errors.New("credential TTL is invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the credential for verification.
|
// Prepare the credential for verification.
|
||||||
hash := getHash(dc.Scheme)
|
hash := getHash(dc.scheme)
|
||||||
cred := &credential{dc.ValidTime, dc.PublicKey, dc.publicKeyScheme}
|
in, err := prepareDelegation(hash, dc.cred, cert.Raw, dc.scheme, vers)
|
||||||
in, err := prepareDelegation(hash, cred, cert.Raw, dc.Scheme, vers)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(any) This code overlaps signficantly with verifyHandshakeSignature()
|
// TODO(any) This code overlaps signficantly with verifyHandshakeSignature()
|
||||||
// in ../auth.go. This should be refactored.
|
// in ../auth.go. This should be refactored.
|
||||||
switch dc.Scheme {
|
switch dc.scheme {
|
||||||
case ECDSAWithP256AndSHA256,
|
case ECDSAWithP256AndSHA256,
|
||||||
ECDSAWithP384AndSHA384,
|
ECDSAWithP384AndSHA384,
|
||||||
ECDSAWithP521AndSHA512:
|
ECDSAWithP521AndSHA512:
|
||||||
@ -373,47 +278,19 @@ func (dc *DelegatedCredential) Validate(cert *x509.Certificate, vers uint16, now
|
|||||||
return false, errors.New("expected ECDSA public key")
|
return false, errors.New("expected ECDSA public key")
|
||||||
}
|
}
|
||||||
sig := new(ecdsaSignature)
|
sig := new(ecdsaSignature)
|
||||||
if _, err = asn1.Unmarshal(dc.Signature, sig); err != nil {
|
if _, err = asn1.Unmarshal(dc.signature, sig); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return ecdsa.Verify(pk, in, sig.R, sig.S), nil
|
return ecdsa.Verify(pk, in, sig.R, sig.S), nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false, fmt.Errorf(
|
return false, fmt.Errorf(
|
||||||
"unsupported signature scheme: 0x%04x", dc.Scheme)
|
"unsupported signature scheme: 0x%04x", dc.scheme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal encodes a DelegatedCredential structure per the spec. It also sets
|
// unmarshalDelegatedCredential decodes a DelegatedCredential structure.
|
||||||
// dc.Raw to the output as a side effect.
|
func unmarshalDelegatedCredential(serialized []byte) (*delegatedCredential, error) {
|
||||||
func (dc *DelegatedCredential) Marshal() ([]byte, error) {
|
|
||||||
// The credential.
|
|
||||||
cred := &credential{dc.ValidTime, dc.PublicKey, dc.publicKeyScheme}
|
|
||||||
serialized, err := cred.marshal()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// The scheme.
|
|
||||||
serializedScheme := make([]byte, 2)
|
|
||||||
binary.BigEndian.PutUint16(serializedScheme, uint16(dc.Scheme))
|
|
||||||
serialized = append(serialized, serializedScheme...)
|
|
||||||
|
|
||||||
// The signature.
|
|
||||||
if len(dc.Signature) > dcMaxSignatureLen {
|
|
||||||
return nil, errors.New("signature is too long")
|
|
||||||
}
|
|
||||||
serializedSignature := make([]byte, 2)
|
|
||||||
binary.BigEndian.PutUint16(serializedSignature, uint16(len(dc.Signature)))
|
|
||||||
serializedSignature = append(serializedSignature, dc.Signature...)
|
|
||||||
serialized = append(serialized, serializedSignature...)
|
|
||||||
|
|
||||||
dc.Raw = serialized
|
|
||||||
return serialized, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalDelegatedCredential decodes a DelegatedCredential structure.
|
|
||||||
func UnmarshalDelegatedCredential(serialized []byte) (*DelegatedCredential, error) {
|
|
||||||
// Get the length of the serialized credential that begins at the start of
|
// Get the length of the serialized credential that begins at the start of
|
||||||
// the input slice.
|
// the input slice.
|
||||||
serializedCredentialLen, err := getCredentialLen(serialized)
|
serializedCredentialLen, err := getCredentialLen(serialized)
|
||||||
@ -445,12 +322,11 @@ func UnmarshalDelegatedCredential(serialized []byte) (*DelegatedCredential, erro
|
|||||||
}
|
}
|
||||||
sig := serialized[:serializedSignatureLen]
|
sig := serialized[:serializedSignatureLen]
|
||||||
|
|
||||||
return &DelegatedCredential{
|
return &delegatedCredential{
|
||||||
ValidTime: cred.validTime,
|
raw: serialized,
|
||||||
PublicKey: cred.publicKey,
|
cred: cred,
|
||||||
publicKeyScheme: cred.scheme,
|
scheme: scheme,
|
||||||
Scheme: scheme,
|
signature: sig,
|
||||||
Signature: sig,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
126
subcerts_test.go
126
subcerts_test.go
@ -14,14 +14,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These test keys were generated with the following program, available in the
|
// A PEM-encoded "delegation certificate", an X.509 certificate with the
|
||||||
// crypto/tls directory:
|
// DelegationUsage extension. The extension is defined in
|
||||||
//
|
// specified in https://tools.ietf.org/html/draft-ietf-tls-subcerts-01.
|
||||||
// go run generate_cert.go -ecdsa-curve P256 -host 127.0.0.1 -dc
|
var dcDelegationCertPEM = `-----BEGIN CERTIFICATE-----
|
||||||
//
|
|
||||||
// To get a certificate without the DelegationUsage extension, remove the `-dc`
|
|
||||||
// parameter.
|
|
||||||
var delegatorCertPEM = `-----BEGIN CERTIFICATE-----
|
|
||||||
MIIBdzCCAR2gAwIBAgIQLVIvEpo0/0TzRja4ImvB1TAKBggqhkjOPQQDAjASMRAw
|
MIIBdzCCAR2gAwIBAgIQLVIvEpo0/0TzRja4ImvB1TAKBggqhkjOPQQDAjASMRAw
|
||||||
DgYDVQQKEwdBY21lIENvMB4XDTE4MDcwMzE2NTE1M1oXDTE5MDcwMzE2NTE1M1ow
|
DgYDVQQKEwdBY21lIENvMB4XDTE4MDcwMzE2NTE1M1oXDTE5MDcwMzE2NTE1M1ow
|
||||||
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOhB
|
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOhB
|
||||||
@ -33,14 +29,17 @@ OetDIvU4mdyoAiAGN97y3GJccYn9ZOJS4UOqhr9oO8PuZMLgdq4OrMRiiA==
|
|||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
`
|
`
|
||||||
|
|
||||||
var delegatorKeyPEM = `-----BEGIN EC PRIVATE KEY-----
|
// The PEM-encoded "delegation key", the secret key associated with the
|
||||||
|
// delegation certificate.
|
||||||
|
var dcDelegationKeyPEM = `-----BEGIN EC PRIVATE KEY-----
|
||||||
MHcCAQEEIJDVlo+sJolMcNjMkfCGDUjMJcE4UgclcXGCrOtbJAi2oAoGCCqGSM49
|
MHcCAQEEIJDVlo+sJolMcNjMkfCGDUjMJcE4UgclcXGCrOtbJAi2oAoGCCqGSM49
|
||||||
AwEHoUQDQgAE6EFTpp1oCCWItoVzU8Cj0cE7haKyHUbh1/cgrkRcvL6ihh+aR/NP
|
AwEHoUQDQgAE6EFTpp1oCCWItoVzU8Cj0cE7haKyHUbh1/cgrkRcvL6ihh+aR/NP
|
||||||
UXGps0tm581jO97bl+alqX/VUkCOlXIqrQ==
|
UXGps0tm581jO97bl+alqX/VUkCOlXIqrQ==
|
||||||
-----END EC PRIVATE KEY-----
|
-----END EC PRIVATE KEY-----
|
||||||
`
|
`
|
||||||
|
|
||||||
var nonDelegatorCertPEM = `-----BEGIN CERTIFICATE-----
|
// A certificate without the DelegationUsage extension.
|
||||||
|
var dcCertPEM = `-----BEGIN CERTIFICATE-----
|
||||||
MIIBaTCCAQ6gAwIBAgIQSUo+9uaip3qCW+1EPeHZgDAKBggqhkjOPQQDAjASMRAw
|
MIIBaTCCAQ6gAwIBAgIQSUo+9uaip3qCW+1EPeHZgDAKBggqhkjOPQQDAjASMRAw
|
||||||
DgYDVQQKEwdBY21lIENvMB4XDTE4MDYxMjIzNDAyNloXDTE5MDYxMjIzNDAyNlow
|
DgYDVQQKEwdBY21lIENvMB4XDTE4MDYxMjIzNDAyNloXDTE5MDYxMjIzNDAyNlow
|
||||||
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLf7
|
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLf7
|
||||||
@ -52,13 +51,25 @@ AwIDSQAwRgIhANXG0zmrVtQBK0TNZZoEGMOtSwxmiZzXNe+IjdpxO3TiAiEA5VYx
|
|||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
`
|
`
|
||||||
|
|
||||||
var nonDelegatorKeyPEM = `-----BEGIN EC PRIVATE KEY-----
|
// The secret key associatted with dcCertPEM.
|
||||||
|
var dcKeyPEM = `-----BEGIN EC PRIVATE KEY-----
|
||||||
MHcCAQEEIMw9DiOfGI1E/XZrrW2huZSjYi0EKwvVjAe+dYtyFsSloAoGCCqGSM49
|
MHcCAQEEIMw9DiOfGI1E/XZrrW2huZSjYi0EKwvVjAe+dYtyFsSloAoGCCqGSM49
|
||||||
AwEHoUQDQgAEt/t+LOc9V1zdXmYzfKazBTb8iglqr914Dp2B2PnSjN1jJGIA+PHN
|
AwEHoUQDQgAEt/t+LOc9V1zdXmYzfKazBTb8iglqr914Dp2B2PnSjN1jJGIA+PHN
|
||||||
zP3NGxnDVqlMX+HI1+IuFXgQTVWvBdxPkw==
|
zP3NGxnDVqlMX+HI1+IuFXgQTVWvBdxPkw==
|
||||||
-----END EC PRIVATE KEY-----
|
-----END EC PRIVATE KEY-----
|
||||||
`
|
`
|
||||||
|
|
||||||
|
// dcTestDC stores delegated credentials and their secret keys.
|
||||||
|
type dcTestDC struct {
|
||||||
|
Name string
|
||||||
|
Version int
|
||||||
|
Scheme int
|
||||||
|
DC []byte
|
||||||
|
PrivateKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test data used for testing the TLS handshake with the delegated creedential
|
||||||
|
// extension. The PEM block encodes a DER encoded slice of dcTestDC's.
|
||||||
var dcTestDCsPEM = `-----BEGIN DC TEST DATA-----
|
var dcTestDCsPEM = `-----BEGIN DC TEST DATA-----
|
||||||
MIIGQzCCAToTBXRsczEyAgIDAwICBAMEga0ACUp3AFswWTATBgcqhkjOPQIBBggq
|
MIIGQzCCAToTBXRsczEyAgIDAwICBAMEga0ACUp3AFswWTATBgcqhkjOPQIBBggq
|
||||||
hkjOPQMBBwNCAAQ9z9RDrMvyRzPOkw9SK2S/O5DiwfRNjAwYcq7e/sKdN0ZcSP1K
|
hkjOPQMBBwNCAAQ9z9RDrMvyRzPOkw9SK2S/O5DiwfRNjAwYcq7e/sKdN0ZcSP1K
|
||||||
@ -97,14 +108,6 @@ HJbn52hmB027JEIMPWsxKxPkr7udk7Q=
|
|||||||
-----END DC TEST DATA-----
|
-----END DC TEST DATA-----
|
||||||
`
|
`
|
||||||
|
|
||||||
type dcTestDC struct {
|
|
||||||
Name string
|
|
||||||
Version int
|
|
||||||
Scheme int
|
|
||||||
DC []byte
|
|
||||||
PrivateKey []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
var dcTestDCs []dcTestDC
|
var dcTestDCs []dcTestDC
|
||||||
var dcTestConfig *Config
|
var dcTestConfig *Config
|
||||||
var dcTestDelegationCert Certificate
|
var dcTestDelegationCert Certificate
|
||||||
@ -112,8 +115,7 @@ var dcTestCert Certificate
|
|||||||
var dcTestNow time.Time
|
var dcTestNow time.Time
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
// Parse the PEM block containing the test DCs.
|
||||||
// Parse the PEM-encoded DER block containing the test DCs.
|
|
||||||
block, _ := pem.Decode([]byte(dcTestDCsPEM))
|
block, _ := pem.Decode([]byte(dcTestDCsPEM))
|
||||||
if block == nil {
|
if block == nil {
|
||||||
panic("failed to decode DC tests PEM block")
|
panic("failed to decode DC tests PEM block")
|
||||||
@ -142,7 +144,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The delegation certificate.
|
// The delegation certificate.
|
||||||
dcTestDelegationCert, err = X509KeyPair([]byte(delegatorCertPEM), []byte(delegatorKeyPEM))
|
dcTestDelegationCert, err = X509KeyPair([]byte(dcDelegationCertPEM), []byte(dcDelegationKeyPEM))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -152,7 +154,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A certificate without the the DelegationUsage extension for X.509.
|
// A certificate without the the DelegationUsage extension for X.509.
|
||||||
dcTestCert, err = X509KeyPair([]byte(nonDelegatorCertPEM), []byte(nonDelegatorKeyPEM))
|
dcTestCert, err = X509KeyPair([]byte(dcCertPEM), []byte(dcKeyPEM))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -179,9 +181,9 @@ func init() {
|
|||||||
dcTestConfig.RootCAs.AddCert(root)
|
dcTestConfig.RootCAs.AddCert(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests the handshake and one round of application data. Returns true if the
|
// Executes the handshake with the given configuration and returns true if the
|
||||||
// connection used a DC.
|
// delegated credential extension was successfully negotiated.
|
||||||
func testConnWithDC(t *testing.T, clientMsg, serverMsg string, clientConfig, serverConfig *Config) (bool, error) {
|
func testConnWithDC(t *testing.T, clientConfig, serverConfig *Config) (bool, error) {
|
||||||
ln := newLocalListener(t)
|
ln := newLocalListener(t)
|
||||||
defer ln.Close()
|
defer ln.Close()
|
||||||
|
|
||||||
@ -216,28 +218,9 @@ func testConnWithDC(t *testing.T, clientMsg, serverMsg string, clientConfig, ser
|
|||||||
return false, serr
|
return false, serr
|
||||||
}
|
}
|
||||||
|
|
||||||
bufLen := len(clientMsg)
|
|
||||||
if len(serverMsg) > len(clientMsg) {
|
|
||||||
bufLen = len(serverMsg)
|
|
||||||
}
|
|
||||||
buf := make([]byte, bufLen)
|
|
||||||
|
|
||||||
// Client sends a message to the server, which is read by the server.
|
|
||||||
cli.Write([]byte(clientMsg))
|
|
||||||
n, err := srv.Read(buf)
|
|
||||||
if n != len(clientMsg) || string(buf[:n]) != clientMsg {
|
|
||||||
return false, fmt.Errorf("Server read = %d, buf= %q; want %d, %s", n, buf, len(clientMsg), clientMsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server reads a message from the client, which is read by the client.
|
|
||||||
srv.Write([]byte(serverMsg))
|
|
||||||
n, err = cli.Read(buf)
|
|
||||||
if n != len(serverMsg) || err != nil || string(buf[:n]) != serverMsg {
|
|
||||||
return false, fmt.Errorf("Client read = %d, %v, data %q; want %d, nil, %s", n, err, buf, len(serverMsg), serverMsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return true if the client's conn.dc structure was instantiated.
|
// Return true if the client's conn.dc structure was instantiated.
|
||||||
return (cli.verifiedDc != nil), nil
|
st := cli.ConnectionState()
|
||||||
|
return (st.DelegatedCredential != nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks that the client suppports a version >= 1.2 and accepts delegated
|
// Checks that the client suppports a version >= 1.2 and accepts delegated
|
||||||
@ -256,7 +239,7 @@ func testServerGetCertificate(ch *ClientHelloInfo) (*Certificate, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Various test cases for handshakes involving DCs.
|
// Various test cases for handshakes involving DCs.
|
||||||
var dcTests = []struct {
|
var dcTesters = []struct {
|
||||||
clientDC bool
|
clientDC bool
|
||||||
serverDC bool
|
serverDC bool
|
||||||
clientSkipVerify bool
|
clientSkipVerify bool
|
||||||
@ -284,53 +267,46 @@ var dcTests = []struct {
|
|||||||
// Tests the handshake with the delegated credential extension for each test
|
// Tests the handshake with the delegated credential extension for each test
|
||||||
// case in dcTests.
|
// case in dcTests.
|
||||||
func TestDCHandshake(t *testing.T) {
|
func TestDCHandshake(t *testing.T) {
|
||||||
serverMsg := "hello"
|
|
||||||
clientMsg := "world"
|
|
||||||
|
|
||||||
clientConfig := dcTestConfig.Clone()
|
clientConfig := dcTestConfig.Clone()
|
||||||
serverConfig := dcTestConfig.Clone()
|
serverConfig := dcTestConfig.Clone()
|
||||||
serverConfig.GetCertificate = testServerGetCertificate
|
serverConfig.GetCertificate = testServerGetCertificate
|
||||||
|
|
||||||
for i, test := range dcTests {
|
for i, tester := range dcTesters {
|
||||||
clientConfig.MaxVersion = test.clientMaxVers
|
clientConfig.MaxVersion = tester.clientMaxVers
|
||||||
serverConfig.MaxVersion = test.serverMaxVers
|
serverConfig.MaxVersion = tester.serverMaxVers
|
||||||
clientConfig.InsecureSkipVerify = test.clientSkipVerify
|
clientConfig.InsecureSkipVerify = tester.clientSkipVerify
|
||||||
clientConfig.AcceptDelegatedCredential = test.clientDC
|
clientConfig.AcceptDelegatedCredential = tester.clientDC
|
||||||
clientConfig.Time = func() time.Time {
|
clientConfig.Time = func() time.Time {
|
||||||
return dcTestNow.Add(time.Duration(test.nowOffset))
|
return dcTestNow.Add(time.Duration(tester.nowOffset))
|
||||||
}
|
}
|
||||||
|
|
||||||
if test.serverDC {
|
if tester.serverDC {
|
||||||
serverConfig.GetDelegatedCredential = func(
|
serverConfig.GetDelegatedCredential = func(
|
||||||
ch *ClientHelloInfo, vers uint16) (*DelegatedCredential, crypto.PrivateKey, error) {
|
ch *ClientHelloInfo, vers uint16) ([]byte, crypto.PrivateKey, error) {
|
||||||
for _, t := range dcTestDCs {
|
for _, test := range dcTestDCs {
|
||||||
if t.Name == test.dcTestName {
|
if test.Name == tester.dcTestName {
|
||||||
dc, err := UnmarshalDelegatedCredential(t.DC)
|
sk, err := x509.ParseECPrivateKey(test.PrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
sk, err := x509.ParseECPrivateKey(t.PrivateKey)
|
return test.DC, sk, nil
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return dc, sk, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, nil, fmt.Errorf("Test DC with name '%s' not found", test.dcTestName)
|
return nil, nil, fmt.Errorf("Test DC with name '%s' not found", tester.dcTestName)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
serverConfig.GetDelegatedCredential = nil
|
serverConfig.GetDelegatedCredential = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
usedDC, err := testConnWithDC(t, clientMsg, serverMsg, clientConfig, serverConfig)
|
usedDC, err := testConnWithDC(t, clientConfig, serverConfig)
|
||||||
if err != nil && test.expectSuccess {
|
if err != nil && tester.expectSuccess {
|
||||||
t.Errorf("test #%d (%s) fails: %s", i+1, test.name, err)
|
t.Errorf("test #%d (%s) fails: %s", i+1, tester.name, err)
|
||||||
} else if err == nil && !test.expectSuccess {
|
} else if err == nil && !tester.expectSuccess {
|
||||||
t.Errorf("test #%d (%s) succeeds; expected failure", i+1, test.name)
|
t.Errorf("test #%d (%s) succeeds; expected failure", i+1, tester.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if usedDC != test.expectDC {
|
if usedDC != tester.expectDC {
|
||||||
t.Errorf("test #%d (%s) usedDC = %v; expected %v", i+1, test.name, usedDC, test.expectDC)
|
t.Errorf("test #%d (%s) usedDC = %v; expected %v", i+1, tester.name, usedDC, tester.expectDC)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user