diff --git a/13.go b/13.go index 99346be..736f182 100644 --- a/13.go +++ b/13.go @@ -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, diff --git a/common.go b/common.go index 93fb353..803987b 100644 --- a/common.go +++ b/common.go @@ -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. diff --git a/handshake_client.go b/handshake_client.go index 94024fa..be05734 100644 --- a/handshake_client.go +++ b/handshake_client.go @@ -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()) diff --git a/handshake_messages.go b/handshake_messages.go index eac6a69..1a75ca7 100644 --- a/handshake_messages.go +++ b/handshake_messages.go @@ -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] diff --git a/handshake_server.go b/handshake_server.go index d625e3f..6825630 100644 --- a/handshake_server.go +++ b/handshake_server.go @@ -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 - } } } diff --git a/subcerts.go b/subcerts.go index 6c8071f..a21f901 100644 --- a/subcerts.go +++ b/subcerts.go @@ -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) } diff --git a/subcerts_test.go b/subcerts_test.go index 8824d36..5b96484 100644 --- a/subcerts_test.go +++ b/subcerts_test.go @@ -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)