diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 839cf2f73a..97fe96e201 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -3342,27 +3342,24 @@ OPENSSL_EXPORT const char *SSL_get_psk_identity(const SSL *ssl); // // *** EXPERIMENTAL — PRONE TO CHANGE *** // -// draft-ietf-tls-subcerts is a proposed extension for TLS 1.3 and above that -// allows an end point to use its certificate to delegate credentials for -// authentication. If the peer indicates support for this extension, then this -// host may use a delegated credential to sign the handshake. Once issued, -// credentials can't be revoked. In order to mitigate the damage in case the -// credential secret key is compromised, the credential is only valid for a -// short time (days, hours, or even minutes). This library implements draft-03 -// of the protocol spec. -// -// The extension ID has not been assigned; we're using 0xff02 for the time -// being. Currently only the server side is implemented. +// Delegated credentials (RFC 9345) allow a TLS 1.3 end point to use its +// certificate to issue new credentials for authentication. If the peer +// indicates support for this extension, then this host may use a delegated +// credential to sign the handshake. Once issued, credentials can't be revoked. +// In order to mitigate the damage in case the credential secret key is +// compromised, the credential is only valid for a short time (days, hours, or +// even minutes). +// +// Currently only the server side is implemented. // // Servers configure a DC for use in the handshake via // |SSL_set1_delegated_credential|. It must be signed by the host's end-entity -// certificate as defined in draft-ietf-tls-subcerts-03. +// certificate as defined in RFC 9345. // SSL_set1_delegated_credential configures the delegated credential (DC) that // will be sent to the peer for the current connection. |dc| is the DC in wire // format, and |pkey| or |key_method| is the corresponding private key. -// Currently (as of draft-03), only servers may configure a DC to use in the -// handshake. +// Currently, only servers may configure a DC to use in the handshake. // // The DC will only be used if the protocol version is correct and the signature // scheme is supported by the peer. If not, the DC will not be negotiated and diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h index c1207a3b7c..60301885a2 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -239,8 +239,8 @@ extern "C" { // ExtensionType value from RFC 5746 #define TLSEXT_TYPE_renegotiate 0xff01 -// ExtensionType value from draft-ietf-tls-subcerts. -#define TLSEXT_TYPE_delegated_credential 0x22 +// ExtensionType value from RFC 9345 +#define TLSEXT_TYPE_delegated_credential 34 // ExtensionType value from draft-vvv-tls-alps. This is not an IANA defined // extension number. diff --git a/ssl/extensions.cc b/ssl/extensions.cc index b134000974..73adf74abe 100644 --- a/ssl/extensions.cc +++ b/ssl/extensions.cc @@ -2752,7 +2752,7 @@ static bool ext_quic_transport_params_add_serverhello_legacy(SSL_HANDSHAKE *hs, // Delegated credentials. // -// https://tools.ietf.org/html/draft-ietf-tls-subcerts +// https://www.rfc-editor.org/rfc/rfc9345.html static bool ext_delegated_credential_add_clienthello( const SSL_HANDSHAKE *hs, CBB *out, CBB *out_compressible, @@ -4122,7 +4122,7 @@ bool tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out) { Span sigalgs = kSignSignatureAlgorithms; if (ssl_signing_with_dc(hs)) { - sigalgs = MakeConstSpan(&dc->expected_cert_verify_algorithm, 1); + sigalgs = MakeConstSpan(&dc->dc_cert_verify_algorithm, 1); } else if (!cert->sigalgs.empty()) { sigalgs = cert->sigalgs; } diff --git a/ssl/internal.h b/ssl/internal.h index d9972dc835..2896a32927 100644 --- a/ssl/internal.h +++ b/ssl/internal.h @@ -1574,8 +1574,7 @@ bool ssl_encrypt_client_hello(SSL_HANDSHAKE *hs, Span enc); // Delegated credentials. -// This structure stores a delegated credential (DC) as defined by -// draft-ietf-tls-subcerts-03. +// This structure stores a delegated credential (DC) as defined by RFC 9345. struct DC { static constexpr bool kAllowUniquePtr = true; ~DC(); @@ -1588,13 +1587,11 @@ struct DC { // |*out_alert|. static UniquePtr Parse(CRYPTO_BUFFER *in, uint8_t *out_alert); - // raw is the delegated credential encoded as specified in draft-ietf-tls- - // subcerts-03. + // raw is the delegated credential encoded as specified in RFC 9345. UniquePtr raw; - // expected_cert_verify_algorithm is the signature scheme of the DC public - // key. - uint16_t expected_cert_verify_algorithm = 0; + // dc_cert_verify_algorithm is the signature scheme of the DC public key. + uint16_t dc_cert_verify_algorithm = 0; // pkey is the public key parsed from |public_key|. UniquePtr pkey; diff --git a/ssl/ssl_cert.cc b/ssl/ssl_cert.cc index 1b6b574cc5..6c6ae7b9eb 100644 --- a/ssl/ssl_cert.cc +++ b/ssl/ssl_cert.cc @@ -755,7 +755,7 @@ UniquePtr DC::Dup() { } ret->raw = UpRef(raw); - ret->expected_cert_verify_algorithm = expected_cert_verify_algorithm; + ret->dc_cert_verify_algorithm = dc_cert_verify_algorithm; ret->pkey = UpRef(pkey); return ret; } @@ -775,7 +775,7 @@ UniquePtr DC::Parse(CRYPTO_BUFFER *in, uint8_t *out_alert) { uint16_t algorithm; CRYPTO_BUFFER_init_CBS(dc->raw.get(), &deleg); if (!CBS_get_u32(&deleg, &valid_time) || - !CBS_get_u16(&deleg, &dc->expected_cert_verify_algorithm) || + !CBS_get_u16(&deleg, &dc->dc_cert_verify_algorithm) || !CBS_get_u24_length_prefixed(&deleg, &pubkey) || !CBS_get_u16(&deleg, &algorithm) || !CBS_get_u16_length_prefixed(&deleg, &sig) || @@ -817,7 +817,7 @@ static bool ssl_can_serve_dc(const SSL_HANDSHAKE *hs) { // Check that the DC signature algorithm is supported by the peer. Span peer_sigalgs = hs->peer_delegated_credential_sigalgs; for (uint16_t peer_sigalg : peer_sigalgs) { - if (dc->expected_cert_verify_algorithm == peer_sigalg) { + if (dc->dc_cert_verify_algorithm == peer_sigalg) { return true; } } @@ -825,8 +825,7 @@ static bool ssl_can_serve_dc(const SSL_HANDSHAKE *hs) { } bool ssl_signing_with_dc(const SSL_HANDSHAKE *hs) { - // As of draft-ietf-tls-subcert-03, only the server may use delegated - // credentials to authenticate itself. + // We only support delegated credentials as a server. return hs->ssl->server && hs->delegated_credential_requested && ssl_can_serve_dc(hs); diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index ce080eea32..676826f017 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go @@ -110,6 +110,7 @@ const ( extensionPadding uint16 = 21 extensionExtendedMasterSecret uint16 = 23 extensionCompressedCertAlgs uint16 = 27 + extensionDelegatedCredentials uint16 = 34 extensionSessionTicket uint16 = 35 extensionPreSharedKey uint16 = 41 extensionEarlyData uint16 = 42 @@ -127,7 +128,6 @@ const ( extensionRenegotiationInfo uint16 = 0xff01 extensionQUICTransportParamsLegacy uint16 = 0xffa5 // draft-ietf-quic-tls-32 and earlier extensionChannelID uint16 = 30032 // not IANA assigned - extensionDelegatedCredentials uint16 = 0x22 // draft-ietf-tls-subcerts-06 extensionDuplicate uint16 = 0xffff // not IANA assigned extensionEncryptedClientHello uint16 = 0xfe0d // not IANA assigned extensionECHOuterExtensions uint16 = 0xfd00 // not IANA assigned diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index d074bb5e69..e6db3f4ff8 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go @@ -1803,7 +1803,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { // delegatedCredentialSignedMessage returns the bytes that are signed in order // to authenticate a delegated credential. func delegatedCredentialSignedMessage(credBytes []byte, algorithm signatureAlgorithm, leafDER []byte) []byte { - // https://tools.ietf.org/html/draft-ietf-tls-subcerts-03#section-3 + // https://www.rfc-editor.org/rfc/rfc9345.html#section-4 ret := make([]byte, 64, 128) for i := range ret { ret[i] = 0x20 diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index 991f08a2ed..26e1257943 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go @@ -1923,13 +1923,13 @@ type certificateEntry struct { } type delegatedCredential struct { - // https://tools.ietf.org/html/draft-ietf-tls-subcerts-03#section-3 - signedBytes []byte - lifetimeSecs uint32 - expectedCertVerifyAlgo signatureAlgorithm - pkixPublicKey []byte - algorithm signatureAlgorithm - signature []byte + // https://www.rfc-editor.org/rfc/rfc9345.html#section-4 + signedBytes []byte + lifetimeSecs uint32 + dcCertVerifyAlgo signatureAlgorithm + pkixPublicKey []byte + algorithm signatureAlgorithm + signature []byte } type certificateMsg struct { @@ -2030,17 +2030,17 @@ func (m *certificateMsg) unmarshal(data []byte) bool { case extensionSignedCertificateTimestamp: cert.sctList = []byte(body) case extensionDelegatedCredentials: - // https://tools.ietf.org/html/draft-ietf-tls-subcerts-03#section-3 + // https://www.rfc-editor.org/rfc/rfc9345.html#section-4 if cert.delegatedCredential != nil { return false } dc := new(delegatedCredential) origBody := body - var expectedCertVerifyAlgo, algorithm uint16 + var dcCertVerifyAlgo, algorithm uint16 if !body.ReadUint32(&dc.lifetimeSecs) || - !body.ReadUint16(&expectedCertVerifyAlgo) || + !body.ReadUint16(&dcCertVerifyAlgo) || !readUint24LengthPrefixedBytes(&body, &dc.pkixPublicKey) || !body.ReadUint16(&algorithm) || !readUint16LengthPrefixedBytes(&body, &dc.signature) || @@ -2048,7 +2048,7 @@ func (m *certificateMsg) unmarshal(data []byte) bool { return false } - dc.expectedCertVerifyAlgo = signatureAlgorithm(expectedCertVerifyAlgo) + dc.dcCertVerifyAlgo = signatureAlgorithm(dcCertVerifyAlgo) dc.algorithm = signatureAlgorithm(algorithm) dc.signedBytes = []byte(origBody)[:4+2+3+len(dc.pkixPublicKey)] cert.delegatedCredential = dc diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 286a4bedcf..cfccdcb6a8 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go @@ -282,9 +282,9 @@ type delegatedCredentialConfig struct { // certificate, that the delegated credential is valid for. If zero, then 24 // hours is assumed. lifetime time.Duration - // expectedAlgo is the signature scheme that should be used with this - // delegated credential. If zero, ECDSA with P-256 is assumed. - expectedAlgo signatureAlgorithm + // dcAlgo is the signature scheme that should be used with this delegated + // credential. If zero, ECDSA with P-256 is assumed. + dcAlgo signatureAlgorithm // tlsVersion is the version of TLS that should be used with this delegated // credential. If zero, TLS 1.3 is assumed. tlsVersion uint16 @@ -320,14 +320,14 @@ func loadRSAPrivateKey(filename string) (priv *rsa.PrivateKey, privPKCS8 []byte, } func createDelegatedCredential(config delegatedCredentialConfig, parentDER []byte, parentPriv crypto.PrivateKey) (dc, privPKCS8 []uint8, err error) { - expectedAlgo := config.expectedAlgo - if expectedAlgo == signatureAlgorithm(0) { - expectedAlgo = signatureECDSAWithP256AndSHA256 + dcAlgo := config.dcAlgo + if dcAlgo == signatureAlgorithm(0) { + dcAlgo = signatureECDSAWithP256AndSHA256 } var pub crypto.PublicKey - switch expectedAlgo { + switch dcAlgo { case signatureRSAPKCS1WithMD5, signatureRSAPKCS1WithSHA1, signatureRSAPKCS1WithSHA256, signatureRSAPKCS1WithSHA384, signatureRSAPKCS1WithSHA512, signatureRSAPSSWithSHA256, signatureRSAPSSWithSHA384, signatureRSAPSSWithSHA512: // RSA keys are expensive to generate so load from disk instead. var priv *rsa.PrivateKey @@ -339,7 +339,7 @@ func createDelegatedCredential(config delegatedCredentialConfig, parentDER []byt case signatureECDSAWithSHA1, signatureECDSAWithP256AndSHA256, signatureECDSAWithP384AndSHA384, signatureECDSAWithP521AndSHA512: var curve elliptic.Curve - switch expectedAlgo { + switch dcAlgo { case signatureECDSAWithSHA1, signatureECDSAWithP256AndSHA256: curve = elliptic.P256() case signatureECDSAWithP384AndSHA384: @@ -362,7 +362,7 @@ func createDelegatedCredential(config delegatedCredentialConfig, parentDER []byt pub = &priv.PublicKey default: - return nil, nil, fmt.Errorf("unsupported expected signature algorithm: %x", expectedAlgo) + return nil, nil, fmt.Errorf("unsupported DC signature algorithm: %x", dcAlgo) } lifetime := config.lifetime @@ -382,9 +382,9 @@ func createDelegatedCredential(config delegatedCredentialConfig, parentDER []byt return nil, nil, fmt.Errorf("delegated credentials require TLS 1.3") } - // https://tools.ietf.org/html/draft-ietf-tls-subcerts-03#section-3 + // https://www.rfc-editor.org/rfc/rfc9345.html#section-4 dc = append(dc, byte(lifetimeSecs>>24), byte(lifetimeSecs>>16), byte(lifetimeSecs>>8), byte(lifetimeSecs)) - dc = append(dc, byte(expectedAlgo>>8), byte(expectedAlgo)) + dc = append(dc, byte(dcAlgo>>8), byte(dcAlgo)) pubBytes, err := x509.MarshalPKIXPublicKey(pub) if err != nil { @@ -7490,7 +7490,7 @@ func addExtensionTests() { ApplicationSettings: map[string][]byte{"proto": []byte("runner2")}, ALPSUseNewCodepoint: clientSends, }, - resumeSession: true, + resumeSession: true, flags: append([]string{ "-select-alpn", "proto", "-on-initial-application-settings", "proto,shim1",