Update implementation of draft-ietf-tls-subcerts to draft 02 (#108)

Drops support for delegated credentials with TLS 1.2 and adds the
protocol version and signature algorithm to the credential structure.
This commit is contained in:
Christopher Patton 2018-08-09 11:24:40 -07:00 committed by Kris Kwiatkowski
parent 77d1fbf262
commit 174a68a0fb
7 changed files with 190 additions and 224 deletions

36
13.go
View File

@ -405,10 +405,10 @@ func (hs *serverHandshakeState) sendCertificate13() error {
}
// If hs.delegatedCredential is set (see hs.readClientHello()) then the
// 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
// CertificateEntry of Certificate.certficate_list (see
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-01).
// server is using the delegated credential extension. The DC is added as an
// extension to the end-entity certificate, i.e., the last CertificateEntry
// of Certificate.certficate_list. (For details, see
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-02.)
if len(certEntries) > 0 && hs.clientHello.delegatedCredential && hs.delegatedCredential != nil {
certEntries[0].delegatedCredential = hs.delegatedCredential
}
@ -1051,13 +1051,24 @@ func (hs *clientHandshakeState) doTLS13Handshake() error {
return err
}
// Receive CertificateVerify message.
msg, err = c.readHandshake()
if err != nil {
return err
}
certVerifyMsg, ok := msg.(*certificateVerifyMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(certVerifyMsg, msg)
}
// Validate the DC if present. The DC is only processed if the extension was
// indicated by the ClientHello; otherwise this call will result in an
// "illegal_parameter" alert. The call also asserts that the DC extension
// did not appear in the ServerHello.
// "illegal_parameter" alert.
if len(certMsg.certificates) > 0 {
if err := hs.processDelegatedCredentialFromServer(
certMsg.certificates[0].delegatedCredential); err != nil {
certMsg.certificates[0].delegatedCredential,
certVerifyMsg.signatureAlgorithm); err != nil {
return err
}
}
@ -1072,16 +1083,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error {
pk = hs.c.verifiedDc.cred.publicKey
}
// Receive CertificateVerify message.
msg, err = c.readHandshake()
if err != nil {
return err
}
certVerifyMsg, ok := msg.(*certificateVerifyMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(certVerifyMsg, msg)
}
// Verify the handshake signature.
err, alertCode := verifyPeerHandshakeSignature(
certVerifyMsg,
pk,

View File

@ -620,16 +620,16 @@ type Config struct {
//
// This value has no meaning for the server.
//
// See https://tools.ietf.org/html/draft-ietf-tls-subcerts-01.
// See https://tools.ietf.org/html/draft-ietf-tls-subcerts-02.
AcceptDelegatedCredential bool
// GetDelegatedCredential returns a DC and its private key for use in the
// delegated credential extension. The inputs to the callback are some
// information parsed from the ClientHello, as well as the protocol version
// selected by the server. This is necessary because the DC is bound to
// selected by the server. This is necessary because the DC is bound to the
// 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
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-02. If the return
// value is nil, then the server will not offer negotiate the extension.
//
// This value has no meaning for the client.

View File

@ -415,27 +415,21 @@ func (hs *clientHandshakeState) processCertsFromServer(certificates [][]byte) er
// processDelegatedCredentialFromServer unmarshals the delegated credential
// offered by the server (if present) and validates it using the peer
// certificate.
func (hs *clientHandshakeState) processDelegatedCredentialFromServer(serialized []byte) error {
// certificate and the signature scheme (`scheme`) indicated by the server in
// the "signature_scheme" extension.
func (hs *clientHandshakeState) processDelegatedCredentialFromServer(serialized []byte, scheme SignatureScheme) error {
c := hs.c
var dc *delegatedCredential
var err error
if serialized != nil {
// Assert that the DC extension was indicated by the client.
if !hs.hello.delegatedCredential {
c.sendAlert(alertUnexpectedMessage)
return errors.New("tls: got delegated credential extension without indication")
}
// Assert that the DC was sent in the ServerHello in (and only in)
// version 1.2.
if hs.serverHello.delegatedCredential != nil && hs.serverHello.vers != VersionTLS12 {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: ServerHello with delegated credential extension in TLS != 1.2")
}
// Parse the delegated credential.
dc, err = unmarshalDelegatedCredential(serialized)
if err != nil {
c.sendAlert(alertDecodeError)
@ -444,12 +438,18 @@ func (hs *clientHandshakeState) processDelegatedCredentialFromServer(serialized
}
if dc != nil && !c.config.InsecureSkipVerify {
if v, err := dc.validate(c.peerCertificates[0], hs.c.vers, c.config.time()); err != nil {
if v, err := dc.validate(c.peerCertificates[0], c.config.time()); err != nil {
c.sendAlert(alertIllegalParameter)
return fmt.Errorf("delegated credential: %s", err)
} else if !v {
c.sendAlert(alertIllegalParameter)
return errors.New("delegated credential: signature invalid")
} else if dc.cred.expectedVersion != hs.c.vers {
c.sendAlert(alertIllegalParameter)
return errors.New("delegated credential: protocol version mismatch")
} else if dc.cred.expectedCertVerifyAlgorithm != scheme {
c.sendAlert(alertIllegalParameter)
return errors.New("delegated credential: signature scheme mismatch")
}
}
@ -477,13 +477,6 @@ func (hs *clientHandshakeState) doFullHandshake() error {
if err := hs.processCertsFromServer(certMsg.certificates); err != nil {
return err
}
// Validate the DC if present. The DC is only processed if the
// extension was indicated by the ClientHello; otherwise this call will
// result in an "illegal_parameter" alert. It also asserts that the DC
// was sent in the ServerHello if and only if TLS 1.2 is in use.
if err := hs.processDelegatedCredentialFromServer(hs.serverHello.delegatedCredential); err != nil {
return err
}
} else {
// This is a renegotiation handshake. We require that the
// server's identity (i.e. leaf certificate) is unchanged and
@ -532,13 +525,6 @@ func (hs *clientHandshakeState) doFullHandshake() error {
// Set the public key used to verify the handshake.
pk := c.peerCertificates[0].PublicKey
// If the delegated credential extension has successfully been negotiated,
// then the ServerKeyExchange is signed with delegated credential private
// key.
if c.verifiedDc != nil {
pk = c.verifiedDc.cred.publicKey
}
skx, ok := msg.(*serverKeyExchangeMsg)
if ok {
hs.finishedHash.Write(skx.marshal())

View File

@ -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
m.earlyData = true
case extensionDelegatedCredential:
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-01
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-02
m.delegatedCredential = true
}
data = data[length:]
@ -789,10 +789,6 @@ type serverHelloMsg struct {
secureRenegotiationSupported bool
alpnProtocol string
// TLS 1.2. In TLS 1.3, the DC extension is included in of the end-entity
// certificate in the Certificate message.
delegatedCredential []byte
// TLS 1.3
keyShare keyShare
psk bool
@ -828,7 +824,6 @@ func (m *serverHelloMsg) equal(i interface{}) bool {
bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) &&
m.alpnProtocol == m1.alpnProtocol &&
m.keyShare.group == m1.keyShare.group &&
bytes.Equal(m.delegatedCredential, m1.delegatedCredential) &&
bytes.Equal(m.keyShare.data, m1.keyShare.data) &&
m.psk == m1.psk &&
m.pskIdentity == m1.pskIdentity
@ -882,10 +877,6 @@ func (m *serverHelloMsg) marshal() []byte {
extensionsLength += 2 + sctLen
numExtensions++
}
if dcLen := len(m.delegatedCredential); dcLen > 0 && m.vers == VersionTLS12 {
extensionsLength += 4 + dcLen
numExtensions++
}
if m.keyShare.group != 0 {
extensionsLength += 4 + len(m.keyShare.data)
numExtensions++
@ -1015,13 +1006,6 @@ func (m *serverHelloMsg) marshal() []byte {
z = z[len(sct)+2:]
}
}
if dcLen := len(m.delegatedCredential); dcLen > 0 && m.vers == VersionTLS12 {
binary.BigEndian.PutUint16(z, extensionDelegatedCredential)
binary.BigEndian.PutUint16(z[2:], uint16(dcLen))
z = z[4:]
copy(z, m.delegatedCredential)
z = z[dcLen:]
}
if m.keyShare.group != 0 {
z[0] = uint8(extensionKeyShare >> 8)
z[1] = uint8(extensionKeyShare)
@ -1209,11 +1193,6 @@ func (m *serverHelloMsg) unmarshal(data []byte) alert {
m.scts = append(m.scts, d[:sctLen])
d = d[sctLen:]
}
case extensionDelegatedCredential:
if m.vers != VersionTLS12 {
return alertUnexpectedMessage
}
m.delegatedCredential = data[:length]
case extensionKeyShare:
d := data[:length]

View File

@ -330,11 +330,6 @@ Curves:
if dc != nil {
hs.privateKey = sk
hs.delegatedCredential = dc
// For TLS 1.2, the DC is an extension to the ServerHello.
if c.vers == VersionTLS12 {
hs.hello.delegatedCredential = hs.delegatedCredential
}
}
}

View File

@ -5,7 +5,7 @@
package tls
// Delegated credentials for TLS
// (https://tools.ietf.org/html/draft-ietf-tls-subcerts-01) is an IETF Internet
// (https://tools.ietf.org/html/draft-ietf-tls-subcerts-02) is an IETF Internet
// draft and proposed TLS extension. This allows a backend server to delegate
// TLS termination to a trusted frontend. If the client supports this extension,
// then the frontend may use a "delegated credential" as the signing key in the
@ -14,7 +14,7 @@ package tls
// revoked; in order to mitigate risk in case the frontend is compromised, the
// credential is only valid for a short time (days, hours, or even minutes).
//
// This implements draft 01. This draft doesn't specify an object identifier for
// This implements draft 02. This draft doesn't specify an object identifier for
// the X.509 extension; we use one assigned by Cloudflare. In addition, IANA has
// not assigned an extension ID for this extension; we picked up one that's not
// yet taken.
@ -70,8 +70,11 @@ func canDelegate(cert *x509.Certificate) bool {
// credential stores the public components of a credential.
type credential struct {
// The serialized form of the credential.
raw []byte
// The amount of time for which the credential is valid. Specifically, the
// credential expires `ValidTime` seconds after the `notBefore` of the
// 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.
//
@ -79,14 +82,14 @@ type credential struct {
// uint32 representing the duration in seconds.
validTime time.Duration
// The signature scheme associated with the delegated credential public key.
expectedCertVerifyAlgorithm SignatureScheme
// The version of TLS in which the credential will be used.
expectedVersion uint16
// The credential public key.
publicKey crypto.PublicKey
// 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
@ -108,7 +111,7 @@ func (cred *credential) invalidTTL(start, now time.Time) bool {
// marshalSubjectPublicKeyInfo returns a DER encoded SubjectPublicKeyInfo structure
// (as defined in the X.509 standard) for the credential.
func (cred *credential) marshalSubjectPublicKeyInfo() ([]byte, error) {
switch cred.scheme {
switch cred.expectedCertVerifyAlgorithm {
case ECDSAWithP256AndSHA256,
ECDSAWithP384AndSHA384,
ECDSAWithP521AndSHA512:
@ -119,19 +122,26 @@ func (cred *credential) marshalSubjectPublicKeyInfo() ([]byte, error) {
return serializedPublicKey, nil
default:
return nil, fmt.Errorf("unsupported signature scheme: 0x%04x", cred.scheme)
return nil, fmt.Errorf("unsupported signature scheme: 0x%04x", cred.expectedCertVerifyAlgorithm)
}
}
// marshal encodes a credential in the wire format specified in
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-01.
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-02.
func (cred *credential) marshal() ([]byte, error) {
// Write the valid_time field.
serialized := make([]byte, 6)
// The number of bytes comprising the DC parameters, which includes the
// validity time (4 bytes), the signature scheme of the public key (2 bytes), and
// the protocol version (2 bytes).
paramsLen := 8
// The first 4 bytes are the valid_time, scheme, and version fields.
serialized := make([]byte, paramsLen+2)
binary.BigEndian.PutUint32(serialized, uint32(cred.validTime/time.Second))
binary.BigEndian.PutUint16(serialized[4:], uint16(cred.expectedCertVerifyAlgorithm))
binary.BigEndian.PutUint16(serialized[6:], cred.expectedVersion)
// Encode the public key and assert that the encoding is no longer than 2^16
// bytes (per the spect).
// bytes (per the spec).
serializedPublicKey, err := cred.marshalSubjectPublicKeyInfo()
if err != nil {
return nil, err
@ -140,68 +150,58 @@ func (cred *credential) marshal() ([]byte, error) {
return nil, errors.New("public key is too long")
}
// Write the length of the public_key field.
binary.BigEndian.PutUint16(serialized[4:], uint16(len(serializedPublicKey)))
// The next 2 bytes are the length of the public key field.
binary.BigEndian.PutUint16(serialized[paramsLen:], uint16(len(serializedPublicKey)))
// Write the public key.
return append(serialized, serializedPublicKey...), nil
// The remaining bytes are the public key itself.
serialized = append(serialized, serializedPublicKey...)
cred.raw = serialized
return serialized, nil
}
// unmarshalCredential decodes a credential and returns it.
func unmarshalCredential(serialized []byte) (*credential, error) {
// Bytes 0-3 are the validity time field; bytes 4-6 are the length of the
// serialized SubjectPublicKeyInfo.
if len(serialized) < 6 {
// The number of bytes comprising the DC parameters.
paramsLen := 8
if len(serialized) < paramsLen+2 {
return nil, errors.New("credential is too short")
}
// Parse the validity time.
// Parse the valid_time, scheme, and version fields.
validTime := time.Duration(binary.BigEndian.Uint32(serialized)) * time.Second
scheme := SignatureScheme(binary.BigEndian.Uint16(serialized[4:]))
version := binary.BigEndian.Uint16(serialized[6:])
// Parse the SubjectPublicKeyInfo.
pk, scheme, err := unmarshalSubjectPublicKeyInfo(serialized[6:])
pk, err := x509.ParsePKIXPublicKey(serialized[paramsLen+2:])
if err != nil {
return nil, err
}
return &credential{validTime, pk, scheme}, nil
}
// unmarshalSubjectPublicKeyInfo parses a DER encoded SubjectPublicKeyInfo
// structure into a public key and its corresponding algorithm.
func unmarshalSubjectPublicKeyInfo(serialized []byte) (crypto.PublicKey, SignatureScheme, error) {
publicKey, err := x509.ParsePKIXPublicKey(serialized)
if err != nil {
return nil, 0, err
if _, ok := pk.(*ecdsa.PublicKey); !ok {
return nil, fmt.Errorf("unsupported delegation key type: %T", pk)
}
switch pk := publicKey.(type) {
case *ecdsa.PublicKey:
curveName := pk.Curve.Params().Name
if curveName == "P-256" {
return pk, ECDSAWithP256AndSHA256, nil
} else if curveName == "P-384" {
return pk, ECDSAWithP384AndSHA384, nil
} else if curveName == "P-521" {
return pk, ECDSAWithP521AndSHA512, nil
} else {
return nil, 0, fmt.Errorf("curve %s s not supported", curveName)
}
default:
return nil, 0, fmt.Errorf("unsupported delgation key type: %T", pk)
}
return &credential{
raw: serialized,
validTime: validTime,
expectedCertVerifyAlgorithm: scheme,
expectedVersion: version,
publicKey: pk,
}, nil
}
// getCredentialLen returns the number of bytes comprising the serialized
// credential that starts at the beginning of the input slice. It returns an
// error if the input is too short to contain a credential.
func getCredentialLen(serialized []byte) (int, error) {
if len(serialized) < 6 {
paramsLen := 8
if len(serialized) < paramsLen+2 {
return 0, errors.New("credential is too short")
}
// First 4 bytes is the validity time.
serialized = serialized[4:]
// First several bytes are the valid_time, scheme, and version fields.
serialized = serialized[paramsLen:]
// The next 2 bytes are the length of the serialized public key.
serializedPublicKeyLen := int(binary.BigEndian.Uint16(serialized))
@ -211,7 +211,7 @@ func getCredentialLen(serialized []byte) (int, error) {
return 0, errors.New("public key of credential is too short")
}
return 6 + serializedPublicKeyLen, nil
return paramsLen + 2 + serializedPublicKeyLen, nil
}
// delegatedCredential stores a credential and its delegation.
@ -222,7 +222,7 @@ type delegatedCredential struct {
cred *credential
// The signature scheme used to sign the credential.
scheme SignatureScheme
algorithm SignatureScheme
// The credential's delegation.
signature []byte
@ -246,7 +246,7 @@ func ensureCertificateHasLeaf(cert *Certificate) error {
// 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
// used for delegation.
func (dc *delegatedCredential) validate(cert *x509.Certificate, vers uint16, now time.Time) (bool, error) {
func (dc *delegatedCredential) validate(cert *x509.Certificate, now time.Time) (bool, error) {
// Check that the cert can delegate.
if !canDelegate(cert) {
return false, errNoDelegationUsage
@ -261,15 +261,16 @@ func (dc *delegatedCredential) validate(cert *x509.Certificate, vers uint16, now
}
// Prepare the credential for verification.
hash := getHash(dc.scheme)
in, err := prepareDelegation(hash, dc.cred, cert.Raw, dc.scheme, vers)
rawCred, err := dc.cred.marshal()
if err != nil {
return false, err
}
hash := getHash(dc.algorithm)
in := prepareDelegation(hash, rawCred, cert.Raw, dc.algorithm)
// TODO(any) This code overlaps signficantly with verifyHandshakeSignature()
// TODO(any) This code overlaps significantly with verifyHandshakeSignature()
// in ../auth.go. This should be refactored.
switch dc.scheme {
switch dc.algorithm {
case ECDSAWithP256AndSHA256,
ECDSAWithP384AndSHA384,
ECDSAWithP521AndSHA512:
@ -285,7 +286,7 @@ func (dc *delegatedCredential) validate(cert *x509.Certificate, vers uint16, now
default:
return false, fmt.Errorf(
"unsupported signature scheme: 0x%04x", dc.scheme)
"unsupported signature scheme: 0x%04x", dc.algorithm)
}
}
@ -325,7 +326,7 @@ func unmarshalDelegatedCredential(serialized []byte) (*delegatedCredential, erro
return &delegatedCredential{
raw: serialized,
cred: cred,
scheme: scheme,
algorithm: scheme,
signature: sig,
}, nil
}
@ -361,11 +362,10 @@ func getHash(scheme SignatureScheme) crypto.Hash {
}
// prepareDelegation returns a hash of the message that the delegator is to
// sign. The inputs are the credential (cred), the DER-encoded delegator
// certificate (`delegatorCert`), the signature scheme of the delegator
// (`delegatorScheme`), and the protocol version (`vers`) in which the credential
// is to be used.
func prepareDelegation(hash crypto.Hash, cred *credential, delegatorCert []byte, delegatorScheme SignatureScheme, vers uint16) ([]byte, error) {
// sign. The inputs are the credential (`cred`), the DER-encoded delegator
// certificate (`delegatorCert`) and the signature scheme of the delegator
// (`delegatorAlgorithm`).
func prepareDelegation(hash crypto.Hash, cred, delegatorCert []byte, delegatorAlgorithm SignatureScheme) []byte {
h := hash.New()
// The header.
@ -373,25 +373,16 @@ func prepareDelegation(hash crypto.Hash, cred *credential, delegatorCert []byte,
h.Write([]byte("TLS, server delegated credentials"))
h.Write([]byte{0x00})
// The protocol version.
var serializedVers [2]byte
binary.BigEndian.PutUint16(serializedVers[:], uint16(vers))
h.Write(serializedVers[:])
// The delegation certificate.
h.Write(delegatorCert)
// The credential.
h.Write(cred)
// The delegator signature scheme.
var serializedScheme [2]byte
binary.BigEndian.PutUint16(serializedScheme[:], uint16(delegatorScheme))
binary.BigEndian.PutUint16(serializedScheme[:], uint16(delegatorAlgorithm))
h.Write(serializedScheme[:])
// The credential.
serializedCred, err := cred.marshal()
if err != nil {
return nil, err
}
h.Write(serializedCred)
return h.Sum(nil), nil
return h.Sum(nil)
}

View File

@ -16,46 +16,46 @@ import (
// A PEM-encoded "delegation certificate", an X.509 certificate with the
// DelegationUsage extension. The extension is defined in
// specified in https://tools.ietf.org/html/draft-ietf-tls-subcerts-01.
// specified in https://tools.ietf.org/html/draft-ietf-tls-subcerts-02.
var dcDelegationCertPEM = `-----BEGIN CERTIFICATE-----
MIIBdzCCAR2gAwIBAgIQLVIvEpo0/0TzRja4ImvB1TAKBggqhkjOPQQDAjASMRAw
DgYDVQQKEwdBY21lIENvMB4XDTE4MDcwMzE2NTE1M1oXDTE5MDcwMzE2NTE1M1ow
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOhB
U6adaAgliLaFc1PAo9HBO4Wish1G4df3IK5EXLy+ooYfmkfzT1FxqbNLZufNYzve
25fmpal/1VJAjpVyKq2jVTBTMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr
BgEFBQcDATAMBgNVHRMBAf8EAjAAMA8GA1UdEQQIMAaHBH8AAAEwDQYJKwYBBAGC
2kssBAAwCgYIKoZIzj0EAwIDSAAwRQIhAPNwRk6cygm6zO5rjOzohKYWS+1KuWCM
OetDIvU4mdyoAiAGN97y3GJccYn9ZOJS4UOqhr9oO8PuZMLgdq4OrMRiiA==
MIIBejCCASGgAwIBAgIQXXtl0v50W2OadoW0QwLUlzAKBggqhkjOPQQDAjAUMRIw
EAYDVQQKEwlBY21lIEluYy4wHhcNMTgwNzMwMjAxMTE5WhcNMTgwODA2MjAxMTE5
WjAUMRIwEAYDVQQKEwlBY21lIEluYy4wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
AATcQuuaUNJ3kqKGs4DBdJVd7zWzyGANT4uBNGVkZ2cgaDsdFnx99fGibfgoWer8
HLt9Z+S6Hs+8bDPBHNgTR/Lfo1UwUzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAww
CgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAPBgNVHREECDAGhwR/AAABMA0GCSsG
AQQBgtpLLAQAMAoGCCqGSM49BAMCA0cAMEQCIEMdIkwwmzQAJ6RSDT3wcrsySx2B
5Lvx5HGzc43Fgu9eAiAi4sFXnizFBVUL43qXZBq4ARw17o0JW3/7eec1xttQhw==
-----END CERTIFICATE-----
`
// The PEM-encoded "delegation key", the secret key associated with the
// delegation certificate.
// delegation certificate. This is a key for ECDSA with P256 and SHA256.
var dcDelegationKeyPEM = `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJDVlo+sJolMcNjMkfCGDUjMJcE4UgclcXGCrOtbJAi2oAoGCCqGSM49
AwEHoUQDQgAE6EFTpp1oCCWItoVzU8Cj0cE7haKyHUbh1/cgrkRcvL6ihh+aR/NP
UXGps0tm581jO97bl+alqX/VUkCOlXIqrQ==
MHcCAQEEIAS/pGktmxK1hlt3gF4N2nkMrJnoZihvOO63nnNcxXQroAoGCCqGSM49
AwEHoUQDQgAE3ELrmlDSd5KihrOAwXSVXe81s8hgDU+LgTRlZGdnIGg7HRZ8ffXx
om34KFnq/By7fWfkuh7PvGwzwRzYE0fy3w==
-----END EC PRIVATE KEY-----
`
// A certificate without the DelegationUsage extension.
var dcCertPEM = `-----BEGIN CERTIFICATE-----
MIIBaTCCAQ6gAwIBAgIQSUo+9uaip3qCW+1EPeHZgDAKBggqhkjOPQQDAjASMRAw
DgYDVQQKEwdBY21lIENvMB4XDTE4MDYxMjIzNDAyNloXDTE5MDYxMjIzNDAyNlow
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLf7
fiznPVdc3V5mM3ymswU2/IoJaq/deA6dgdj50ozdYyRiAPjxzcz9zRsZw1apTF/h
yNfiLhV4EE1VrwXcT5OjRjBEMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr
BgEFBQcDATAMBgNVHRMBAf8EAjAAMA8GA1UdEQQIMAaHBH8AAAEwCgYIKoZIzj0E
AwIDSQAwRgIhANXG0zmrVtQBK0TNZZoEGMOtSwxmiZzXNe+IjdpxO3TiAiEA5VYx
0CWJq5zqpVXbJMeKVMASo2nrXZoA6NhJvFQ97hw=
MIIBajCCAQ+gAwIBAgIRAMUg/VFqJaWWJwZ9iHoMjqIwCgYIKoZIzj0EAwIwEjEQ
MA4GA1UEChMHQWNtZSBDbzAeFw0xODA3MzAyMDExMTlaFw0xOTA3MzAyMDExMTla
MBIxEDAOBgNVBAoTB0FjbWUgQ28wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATA
n+oeWSvSNHhEskSRgkkerCQDoV/NA+r3S5AtCOFT5AYLt8xltSTWerFI/YlZLIcL
xlJPT7T+XpBnfS6xaAuxo0YwRDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYI
KwYBBQUHAwEwDAYDVR0TAQH/BAIwADAPBgNVHREECDAGhwR/AAABMAoGCCqGSM49
BAMCA0kAMEYCIQCFGWnoJmwH1rxNCKBJWVDBKDTSsYhySRk4h9RPyR8bUwIhAJxc
KFyrowMTan791RJnyANH/4uYhmvkfhfrFGSTXUli
-----END CERTIFICATE-----
`
// The secret key associatted with dcCertPEM.
var dcKeyPEM = `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIMw9DiOfGI1E/XZrrW2huZSjYi0EKwvVjAe+dYtyFsSloAoGCCqGSM49
AwEHoUQDQgAEt/t+LOc9V1zdXmYzfKazBTb8iglqr914Dp2B2PnSjN1jJGIA+PHN
zP3NGxnDVqlMX+HI1+IuFXgQTVWvBdxPkw==
MHcCAQEEIEP82pOhzx0tKkky9t0OmUo9MHgmfdAHxDN2cHmWGqOhoAoGCCqGSM49
AwEHoUQDQgAEwJ/qHlkr0jR4RLJEkYJJHqwkA6FfzQPq90uQLQjhU+QGC7fMZbUk
1nqxSP2JWSyHC8ZST0+0/l6QZ30usWgLsQ==
-----END EC PRIVATE KEY-----
`
@ -71,40 +71,50 @@ type dcTestDC struct {
// 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-----
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
HJbn52hmB027JEIMPWsxKxPkr7udk7Q=
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
-----END DC TEST DATA-----
`
@ -127,10 +137,6 @@ func init() {
panic("failed to unmarshal DC test ASN.1 data")
}
// Use a static time for testing. This is the point at which the test DCs
// were generated.
dcTestNow = time.Date(2018, 07, 03, 18, 0, 0, 234234, time.UTC)
// The base configuration for the client and server.
dcTestConfig = &Config{
Time: func() time.Time {
@ -163,6 +169,11 @@ func init() {
panic(err)
}
// For testing purposes, use the point at which the test DCs were generated
// as the current time. This is the same as the time at which the
// delegation certificate was generated.
dcTestNow = dcTestDelegationCert.Leaf.NotBefore
// Make these roots of these certificates the client's trusted CAs.
dcTestConfig.RootCAs = x509.NewCertPool()
@ -251,17 +262,16 @@ var dcTesters = []struct {
expectDC bool
name string
}{
{true, true, false, VersionTLS12, VersionTLS12, 0, "tls12", true, true, "tls12"},
{true, true, false, VersionTLS13, VersionTLS13, 0, "tls13", true, true, "tls13"},
{true, true, false, VersionTLS12, VersionTLS12, 0, "malformed12", false, false, "tls12, malformed dc"},
{true, true, false, VersionTLS13, VersionTLS13, 0, "malformed13", false, false, "tls13, malformed dc"},
{true, true, true, VersionTLS12, VersionTLS12, 0, "invalid", true, true, "tls12, invalid dc, skip verify"},
{true, true, true, VersionTLS13, VersionTLS13, 0, "invalid", true, true, "tls13, invalid dc, skip verify"},
{false, true, false, VersionTLS12, VersionTLS12, 0, "tls12", true, false, "client no dc"},
{true, false, false, VersionTLS12, VersionTLS12, 0, "tls12", true, false, "server no dc"},
{true, true, false, VersionTLS11, VersionTLS12, 0, "tls12", true, false, "client old"},
{true, true, false, VersionTLS12, VersionTLS11, 0, "tls12", true, false, "server old"},
{true, true, false, VersionTLS13, VersionTLS13, 0, "tls13p256", true, true, "tls13"},
{true, true, false, VersionTLS13, VersionTLS13, 0, "tls13p521", true, true, "tls13"},
{true, false, false, VersionTLS13, VersionTLS13, 0, "tls13p256", true, false, "server no dc"},
{true, true, false, VersionTLS12, VersionTLS13, 0, "tls13p256", true, false, "client old"},
{true, true, false, VersionTLS13, VersionTLS12, 0, "tls13p256", true, false, "server old"},
{true, true, false, VersionTLS13, VersionTLS13, 0, "badkey", false, false, "bad key"},
{true, true, true, VersionTLS13, VersionTLS13, 0, "badsig", true, true, "bad key, skip verify"},
{true, true, false, VersionTLS13, VersionTLS13, dcMaxTTL, "tls13", false, false, "expired dc"},
{true, true, false, VersionTLS13, VersionTLS13, 0, "badvers", false, false, "dc wrong version"},
{true, true, false, VersionTLS12, VersionTLS12, 0, "tls12", true, false, "tls12"},
}
// Tests the handshake with the delegated credential extension for each test
@ -283,6 +293,9 @@ func TestDCHandshake(t *testing.T) {
if tester.serverDC {
serverConfig.GetDelegatedCredential = func(
ch *ClientHelloInfo, vers uint16) ([]byte, crypto.PrivateKey, error) {
if vers < VersionTLS13 {
return nil, nil, nil
}
for _, test := range dcTestDCs {
if test.Name == tester.dcTestName {
sk, err := x509.ParseECPrivateKey(test.PrivateKey)