This implements the server-side of delegated credentials, a proposed extension for TLS: https://tools.ietf.org/html/draft-ietf-tls-subcerts-02 Change-Id: I6a29cf1ead87b90aeca225335063aaf190a417ff Reviewed-on: https://boringssl-review.googlesource.com/c/33666 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: Adam Langley <agl@google.com>kris/onging/CECPQ3_patch15
@@ -75,6 +75,7 @@ SSL,157,INAPPROPRIATE_FALLBACK | |||||
SSL,259,INVALID_ALPN_PROTOCOL | SSL,259,INVALID_ALPN_PROTOCOL | ||||
SSL,158,INVALID_COMMAND | SSL,158,INVALID_COMMAND | ||||
SSL,256,INVALID_COMPRESSION_LIST | SSL,256,INVALID_COMPRESSION_LIST | ||||
SSL,301,INVALID_DELEGATED_CREDENTIAL | |||||
SSL,159,INVALID_MESSAGE | SSL,159,INVALID_MESSAGE | ||||
SSL,251,INVALID_OUTER_RECORD_TYPE | SSL,251,INVALID_OUTER_RECORD_TYPE | ||||
SSL,269,INVALID_SCT_LIST | SSL,269,INVALID_SCT_LIST | ||||
@@ -3054,6 +3054,41 @@ OPENSSL_EXPORT void SSL_get_peer_quic_transport_params(const SSL *ssl, | |||||
size_t *out_params_len); | size_t *out_params_len); | ||||
// Delegated credentials. | |||||
// | |||||
// *** 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-02 | |||||
// 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. | |||||
// | |||||
// 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-02. | |||||
// 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-02), 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 | |||||
// the handshake will use the private key (or private key method) associated | |||||
// with the certificate. | |||||
OPENSSL_EXPORT int SSL_set1_delegated_credential( | |||||
SSL *ssl, CRYPTO_BUFFER *dc, EVP_PKEY *pkey, | |||||
const SSL_PRIVATE_KEY_METHOD *key_method); | |||||
// QUIC integration. | // QUIC integration. | ||||
// | // | ||||
// QUIC acts as an underlying transport for the TLS 1.3 handshake. The following | // QUIC acts as an underlying transport for the TLS 1.3 handshake. The following | ||||
@@ -4934,6 +4969,7 @@ BSSL_NAMESPACE_END | |||||
#define SSL_R_QUIC_INTERNAL_ERROR 298 | #define SSL_R_QUIC_INTERNAL_ERROR 298 | ||||
#define SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED 299 | #define SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED 299 | ||||
#define SSL_R_TOO_MUCH_READ_EARLY_DATA 300 | #define SSL_R_TOO_MUCH_READ_EARLY_DATA 300 | ||||
#define SSL_R_INVALID_DELEGATED_CREDENTIAL 301 | |||||
#define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000 | #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000 | ||||
#define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010 | #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010 | ||||
#define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020 | #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020 | ||||
@@ -231,6 +231,10 @@ extern "C" { | |||||
// ExtensionType value from RFC5746 | // ExtensionType value from RFC5746 | ||||
#define TLSEXT_TYPE_renegotiate 0xff01 | #define TLSEXT_TYPE_renegotiate 0xff01 | ||||
// ExtensionType value from draft-ietf-tls-subcerts. This is not an IANA defined | |||||
// extension number. | |||||
#define TLSEXT_TYPE_delegated_credential 0xff02 | |||||
// ExtensionType value from RFC6962 | // ExtensionType value from RFC6962 | ||||
#define TLSEXT_TYPE_certificate_timestamp 18 | #define TLSEXT_TYPE_certificate_timestamp 18 | ||||
@@ -135,6 +135,7 @@ SSL_HANDSHAKE::SSL_HANDSHAKE(SSL *ssl_arg) | |||||
cert_request(false), | cert_request(false), | ||||
certificate_status_expected(false), | certificate_status_expected(false), | ||||
ocsp_stapling_requested(false), | ocsp_stapling_requested(false), | ||||
delegated_credential_requested(false), | |||||
should_ack_sni(false), | should_ack_sni(false), | ||||
in_false_start(false), | in_false_start(false), | ||||
in_early_data(false), | in_early_data(false), | ||||
@@ -1370,6 +1370,49 @@ enum handback_t { | |||||
handback_after_handshake, | handback_after_handshake, | ||||
}; | }; | ||||
// Delegated credentials. | |||||
// This structure stores a delegated credential (DC) as defined by | |||||
// draft-ietf-tls-subcerts-02. | |||||
struct DC { | |||||
static constexpr bool kAllowUniquePtr = true; | |||||
~DC(); | |||||
// Dup returns a copy of this DC and takes references to |raw| and |pkey|. | |||||
UniquePtr<DC> Dup(); | |||||
// Parse parses the delegated credential stored in |in|. If successful it | |||||
// returns the parsed structure, otherwise it returns |nullptr| and sets | |||||
// |*out_alert|. | |||||
static UniquePtr<DC> Parse(CRYPTO_BUFFER *in, uint8_t *out_alert); | |||||
// raw is the delegated credential encoded as specified in draft-ietf-tls- | |||||
// subcerts-02. | |||||
UniquePtr<CRYPTO_BUFFER> raw; | |||||
// expected_cert_verify_algorithm is the signature scheme of the DC public | |||||
// key. | |||||
uint16_t expected_cert_verify_algorithm = 0; | |||||
// expected_version is the protocol in which the DC must be used. | |||||
uint16_t expected_version = 0; | |||||
// pkey is the public key parsed from |public_key|. | |||||
UniquePtr<EVP_PKEY> pkey; | |||||
private: | |||||
friend DC* New<DC>(); | |||||
DC(); | |||||
}; | |||||
// ssl_signing_with_dc returns true if the peer has indicated support for | |||||
// delegated credentials and this host has sent a delegated credential in | |||||
// response. If this is true then we've committed to using the DC in the | |||||
// handshake. | |||||
bool ssl_signing_with_dc(const SSL_HANDSHAKE *hs); | |||||
struct SSL_HANDSHAKE { | struct SSL_HANDSHAKE { | ||||
explicit SSL_HANDSHAKE(SSL *ssl); | explicit SSL_HANDSHAKE(SSL *ssl); | ||||
~SSL_HANDSHAKE(); | ~SSL_HANDSHAKE(); | ||||
@@ -1541,6 +1584,10 @@ struct SSL_HANDSHAKE { | |||||
// ocsp_stapling_requested is true if a client requested OCSP stapling. | // ocsp_stapling_requested is true if a client requested OCSP stapling. | ||||
bool ocsp_stapling_requested : 1; | bool ocsp_stapling_requested : 1; | ||||
// delegated_credential_requested is true if the peer indicated support for | |||||
// the delegated credential extension. | |||||
bool delegated_credential_requested : 1; | |||||
// should_ack_sni is used by a server and indicates that the SNI extension | // should_ack_sni is used by a server and indicates that the SNI extension | ||||
// should be echoed in the ServerHello. | // should be echoed in the ServerHello. | ||||
bool should_ack_sni : 1; | bool should_ack_sni : 1; | ||||
@@ -1786,6 +1833,15 @@ bool tls1_get_legacy_signature_algorithm(uint16_t *out, const EVP_PKEY *pkey); | |||||
// supported. It returns true on success and false on error. | // supported. It returns true on success and false on error. | ||||
bool tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out); | bool tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out); | ||||
// tls1_get_peer_verify_algorithms returns the signature schemes for which the | |||||
// peer indicated support. | |||||
// | |||||
// NOTE: The related function |SSL_get0_peer_verify_algorithms| only has | |||||
// well-defined behavior during the callbacks set by |SSL_CTX_set_cert_cb| and | |||||
// |SSL_CTX_set_client_cert_cb|, or when the handshake is paused because of | |||||
// them. | |||||
Span<const uint16_t> tls1_get_peer_verify_algorithms(const SSL_HANDSHAKE *hs); | |||||
// tls12_add_verify_sigalgs adds the signature algorithms acceptable for the | // tls12_add_verify_sigalgs adds the signature algorithms acceptable for the | ||||
// peer signature to |out|. It returns true on success and false on error. If | // peer signature to |out|. It returns true on success and false on error. If | ||||
// |for_certs| is true, the potentially more restrictive list of algorithms for | // |for_certs| is true, the potentially more restrictive list of algorithms for | ||||
@@ -1879,6 +1935,19 @@ struct CERT { | |||||
// ticket key. Only sessions with a matching value will be accepted. | // ticket key. Only sessions with a matching value will be accepted. | ||||
uint8_t sid_ctx_length = 0; | uint8_t sid_ctx_length = 0; | ||||
uint8_t sid_ctx[SSL_MAX_SID_CTX_LENGTH] = {0}; | uint8_t sid_ctx[SSL_MAX_SID_CTX_LENGTH] = {0}; | ||||
// Delegated credentials. | |||||
// dc is the delegated credential to send to the peer (if requested). | |||||
UniquePtr<DC> dc = nullptr; | |||||
// dc_privatekey is used instead of |privatekey| or |key_method| to | |||||
// authenticate the host if a delegated credential is used in the handshake. | |||||
UniquePtr<EVP_PKEY> dc_privatekey = nullptr; | |||||
// dc_key_method, if not NULL, is used instead of |dc_privatekey| to | |||||
// authenticate the host. | |||||
const SSL_PRIVATE_KEY_METHOD *dc_key_method = nullptr; | |||||
}; | }; | ||||
// |SSL_PROTOCOL_METHOD| abstracts between TLS and DTLS. | // |SSL_PROTOCOL_METHOD| abstracts between TLS and DTLS. | ||||
@@ -180,6 +180,16 @@ UniquePtr<CERT> ssl_cert_dup(CERT *cert) { | |||||
ret->sid_ctx_length = cert->sid_ctx_length; | ret->sid_ctx_length = cert->sid_ctx_length; | ||||
OPENSSL_memcpy(ret->sid_ctx, cert->sid_ctx, sizeof(ret->sid_ctx)); | OPENSSL_memcpy(ret->sid_ctx, cert->sid_ctx, sizeof(ret->sid_ctx)); | ||||
if (cert->dc) { | |||||
ret->dc = cert->dc->Dup(); | |||||
if (!ret->dc) { | |||||
return nullptr; | |||||
} | |||||
} | |||||
ret->dc_privatekey = UpRef(cert->dc_privatekey); | |||||
ret->dc_key_method = cert->dc_key_method; | |||||
return ret; | return ret; | ||||
} | } | ||||
@@ -194,6 +204,10 @@ void ssl_cert_clear_certs(CERT *cert) { | |||||
cert->chain.reset(); | cert->chain.reset(); | ||||
cert->privatekey.reset(); | cert->privatekey.reset(); | ||||
cert->key_method = nullptr; | cert->key_method = nullptr; | ||||
cert->dc.reset(); | |||||
cert->dc_privatekey.reset(); | |||||
cert->dc_key_method = nullptr; | |||||
} | } | ||||
static void ssl_cert_set_cert_cb(CERT *cert, int (*cb)(SSL *ssl, void *arg), | static void ssl_cert_set_cert_cb(CERT *cert, int (*cb)(SSL *ssl, void *arg), | ||||
@@ -741,10 +755,152 @@ bool ssl_on_certificate_selected(SSL_HANDSHAKE *hs) { | |||||
CRYPTO_BUFFER_init_CBS( | CRYPTO_BUFFER_init_CBS( | ||||
sk_CRYPTO_BUFFER_value(hs->config->cert->chain.get(), 0), &leaf); | sk_CRYPTO_BUFFER_value(hs->config->cert->chain.get(), 0), &leaf); | ||||
hs->local_pubkey = ssl_cert_parse_pubkey(&leaf); | |||||
if (ssl_signing_with_dc(hs)) { | |||||
hs->local_pubkey = UpRef(hs->config->cert->dc->pkey); | |||||
} else { | |||||
hs->local_pubkey = ssl_cert_parse_pubkey(&leaf); | |||||
} | |||||
return hs->local_pubkey != NULL; | return hs->local_pubkey != NULL; | ||||
} | } | ||||
// Delegated credentials. | |||||
DC::DC() = default; | |||||
DC::~DC() = default; | |||||
UniquePtr<DC> DC::Dup() { | |||||
bssl::UniquePtr<DC> ret = MakeUnique<DC>(); | |||||
if (!ret) { | |||||
return nullptr; | |||||
} | |||||
ret->raw = UpRef(raw); | |||||
ret->expected_cert_verify_algorithm = expected_cert_verify_algorithm; | |||||
ret->expected_version = expected_version; | |||||
ret->pkey = UpRef(pkey); | |||||
return ret; | |||||
} | |||||
// static | |||||
UniquePtr<DC> DC::Parse(CRYPTO_BUFFER *in, uint8_t *out_alert) { | |||||
UniquePtr<DC> dc = MakeUnique<DC>(); | |||||
if (!dc) { | |||||
*out_alert = SSL_AD_INTERNAL_ERROR; | |||||
return nullptr; | |||||
} | |||||
dc->raw = UpRef(in); | |||||
CBS pubkey, deleg, sig; | |||||
uint32_t valid_time; | |||||
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->expected_version) || | |||||
!CBS_get_u24_length_prefixed(&deleg, &pubkey) || | |||||
!CBS_get_u16(&deleg, &algorithm) || | |||||
!CBS_get_u16_length_prefixed(&deleg, &sig) || | |||||
CBS_len(&deleg) != 0) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); | |||||
*out_alert = SSL_AD_DECODE_ERROR; | |||||
return nullptr; | |||||
} | |||||
dc->pkey.reset(EVP_parse_public_key(&pubkey)); | |||||
if (dc->pkey == nullptr) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); | |||||
*out_alert = SSL_AD_DECODE_ERROR; | |||||
return nullptr; | |||||
} | |||||
return dc; | |||||
} | |||||
// ssl_can_serve_dc returns true if the host has configured a DC that it can | |||||
// serve in the handshake. Specifically, it checks that a DC has been | |||||
// configured, that the DC protocol version is the same as the negotiated | |||||
// protocol version, and that the DC signature algorithm is supported by the | |||||
// peer. | |||||
static bool ssl_can_serve_dc(const SSL_HANDSHAKE *hs) { | |||||
// Check that a DC has been configured. | |||||
const CERT *cert = hs->config->cert.get(); | |||||
if (cert->dc == nullptr || | |||||
cert->dc->raw == nullptr || | |||||
(cert->dc_privatekey == nullptr && cert->dc_key_method == nullptr)) { | |||||
return false; | |||||
} | |||||
// Check that the negotiated version matches the protocol version to which the | |||||
// DC is bound, and that 1.3 or higher has been negotiated. | |||||
// | |||||
// NOTE: We use |hs->ssl->version| for checking the DC expected version. We | |||||
// don't call |ssl_protocol_version| because we need the version sent on the | |||||
// wire. For example, a delegated credential can be bound to a draft of TLS | |||||
// 1.3. | |||||
const DC *dc = cert->dc.get(); | |||||
assert(hs->ssl->s3->have_version); | |||||
if (hs->ssl->version != dc->expected_version || | |||||
ssl_protocol_version(hs->ssl) < TLS1_3_VERSION) { | |||||
return false; | |||||
} | |||||
// Check that the DC signature algorithm is supported by the peer. | |||||
Span<const uint16_t> peer_sigalgs = tls1_get_peer_verify_algorithms(hs); | |||||
bool sigalg_found = false; | |||||
for (uint16_t peer_sigalg : peer_sigalgs) { | |||||
if (dc->expected_cert_verify_algorithm == peer_sigalg) { | |||||
sigalg_found = true; | |||||
break; | |||||
} | |||||
} | |||||
return sigalg_found; | |||||
} | |||||
bool ssl_signing_with_dc(const SSL_HANDSHAKE *hs) { | |||||
// As of draft-ietf-tls-subcert-02, only the server may use delegated | |||||
// credentials to authenticate itself. | |||||
return hs->ssl->server && | |||||
hs->delegated_credential_requested && | |||||
ssl_can_serve_dc(hs); | |||||
} | |||||
static int cert_set_dc(CERT *cert, CRYPTO_BUFFER *const raw, EVP_PKEY *privkey, | |||||
const SSL_PRIVATE_KEY_METHOD *key_method) { | |||||
if (privkey == nullptr && key_method == nullptr) { | |||||
OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); | |||||
return 0; | |||||
} | |||||
if (privkey != nullptr && key_method != nullptr) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_HAVE_BOTH_PRIVKEY_AND_METHOD); | |||||
return 0; | |||||
} | |||||
uint8_t alert; | |||||
UniquePtr<DC> dc = DC::Parse(raw, &alert); | |||||
if (dc == nullptr) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_DELEGATED_CREDENTIAL); | |||||
return 0; | |||||
} | |||||
if (privkey) { | |||||
// Check that the public and private keys match. | |||||
if (!ssl_compare_public_and_private_key(dc->pkey.get(), privkey)) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_AND_PRIVATE_KEY_MISMATCH); | |||||
return 0; | |||||
} | |||||
} | |||||
cert->dc = std::move(dc); | |||||
cert->dc_privatekey = UpRef(privkey); | |||||
cert->dc_key_method = key_method; | |||||
return 1; | |||||
} | |||||
BSSL_NAMESPACE_END | BSSL_NAMESPACE_END | ||||
using namespace bssl; | using namespace bssl; | ||||
@@ -870,3 +1026,12 @@ void SSL_set0_client_CAs(SSL *ssl, STACK_OF(CRYPTO_BUFFER) *name_list) { | |||||
ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl->config.get()); | ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl->config.get()); | ||||
ssl->config->client_CA.reset(name_list); | ssl->config->client_CA.reset(name_list); | ||||
} | } | ||||
int SSL_set1_delegated_credential(SSL *ssl, CRYPTO_BUFFER *dc, EVP_PKEY *pkey, | |||||
const SSL_PRIVATE_KEY_METHOD *key_method) { | |||||
if (!ssl->config) { | |||||
return 0; | |||||
} | |||||
return cert_set_dc(ssl->config->cert.get(), dc, pkey, key_method); | |||||
} |
@@ -134,8 +134,13 @@ static const SSL_SIGNATURE_ALGORITHM *get_signature_algorithm(uint16_t sigalg) { | |||||
} | } | ||||
bool ssl_has_private_key(const SSL_HANDSHAKE *hs) { | bool ssl_has_private_key(const SSL_HANDSHAKE *hs) { | ||||
return (hs->config->cert->privatekey != nullptr || | |||||
hs->config->cert->key_method != nullptr); | |||||
if (hs->config->cert->privatekey != nullptr || | |||||
hs->config->cert->key_method != nullptr || | |||||
ssl_signing_with_dc(hs)) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | } | ||||
static bool pkey_supports_algorithm(const SSL *ssl, EVP_PKEY *pkey, | static bool pkey_supports_algorithm(const SSL *ssl, EVP_PKEY *pkey, | ||||
@@ -196,13 +201,20 @@ enum ssl_private_key_result_t ssl_private_key_sign( | |||||
SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out, | SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out, | ||||
uint16_t sigalg, Span<const uint8_t> in) { | uint16_t sigalg, Span<const uint8_t> in) { | ||||
SSL *const ssl = hs->ssl; | SSL *const ssl = hs->ssl; | ||||
if (hs->config->cert->key_method != NULL) { | |||||
const SSL_PRIVATE_KEY_METHOD *key_method = hs->config->cert->key_method; | |||||
EVP_PKEY *privatekey = hs->config->cert->privatekey.get(); | |||||
if (ssl_signing_with_dc(hs)) { | |||||
key_method = hs->config->cert->dc_key_method; | |||||
privatekey = hs->config->cert->dc_privatekey.get(); | |||||
} | |||||
if (key_method != NULL) { | |||||
enum ssl_private_key_result_t ret; | enum ssl_private_key_result_t ret; | ||||
if (hs->pending_private_key_op) { | if (hs->pending_private_key_op) { | ||||
ret = hs->config->cert->key_method->complete(ssl, out, out_len, max_out); | |||||
ret = key_method->complete(ssl, out, out_len, max_out); | |||||
} else { | } else { | ||||
ret = hs->config->cert->key_method->sign(ssl, out, out_len, max_out, | |||||
sigalg, in.data(), in.size()); | |||||
ret = key_method->sign(ssl, out, out_len, max_out, | |||||
sigalg, in.data(), in.size()); | |||||
} | } | ||||
if (ret == ssl_private_key_failure) { | if (ret == ssl_private_key_failure) { | ||||
OPENSSL_PUT_ERROR(SSL, SSL_R_PRIVATE_KEY_OPERATION_FAILED); | OPENSSL_PUT_ERROR(SSL, SSL_R_PRIVATE_KEY_OPERATION_FAILED); | ||||
@@ -213,8 +225,7 @@ enum ssl_private_key_result_t ssl_private_key_sign( | |||||
*out_len = max_out; | *out_len = max_out; | ||||
ScopedEVP_MD_CTX ctx; | ScopedEVP_MD_CTX ctx; | ||||
if (!setup_ctx(ssl, ctx.get(), hs->config->cert->privatekey.get(), sigalg, | |||||
false /* sign */) || | |||||
if (!setup_ctx(ssl, ctx.get(), privatekey, sigalg, false /* sign */) || | |||||
!EVP_DigestSign(ctx.get(), out, out_len, in.data(), in.size())) { | !EVP_DigestSign(ctx.get(), out, out_len, in.data(), in.size())) { | ||||
return ssl_private_key_failure; | return ssl_private_key_failure; | ||||
} | } | ||||
@@ -2715,6 +2715,36 @@ static bool ext_quic_transport_params_add_serverhello(SSL_HANDSHAKE *hs, | |||||
return true; | return true; | ||||
} | } | ||||
// Delegated credentials. | |||||
// | |||||
// https://tools.ietf.org/html/draft-ietf-tls-subcerts | |||||
static bool ext_delegated_credential_add_clienthello(SSL_HANDSHAKE *hs, | |||||
CBB *out) { | |||||
return true; | |||||
} | |||||
static bool ext_delegated_credential_parse_clienthello(SSL_HANDSHAKE *hs, | |||||
uint8_t *out_alert, | |||||
CBS *contents) { | |||||
assert(TLSEXT_TYPE_delegated_credential == 0xff02); | |||||
// TODO: Check that the extension is empty. | |||||
// | |||||
// As of draft-02, the client sends an empty extension in order indicate | |||||
// support for delegated credentials. This could change, however, since the | |||||
// spec is not yet finalized. This assertion is here to remind us to enforce | |||||
// this check once the extension ID is assigned. | |||||
if (contents == nullptr || ssl_protocol_version(hs->ssl) < TLS1_3_VERSION) { | |||||
// Don't use delegated credentials unless we're negotiating TLS 1.3 or | |||||
// higher. | |||||
return true; | |||||
} | |||||
hs->delegated_credential_requested = true; | |||||
return true; | |||||
} | |||||
// Certificate compression | // Certificate compression | ||||
static bool cert_compression_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { | static bool cert_compression_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { | ||||
@@ -3003,6 +3033,14 @@ static const struct tls_extension kExtensions[] = { | |||||
cert_compression_parse_clienthello, | cert_compression_parse_clienthello, | ||||
cert_compression_add_serverhello, | cert_compression_add_serverhello, | ||||
}, | }, | ||||
{ | |||||
TLSEXT_TYPE_delegated_credential, | |||||
NULL, | |||||
ext_delegated_credential_add_clienthello, | |||||
forbid_parse_serverhello, | |||||
ext_delegated_credential_parse_clienthello, | |||||
dont_add_serverhello, | |||||
}, | |||||
}; | }; | ||||
#define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension)) | #define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension)) | ||||
@@ -3629,6 +3667,7 @@ bool tls1_get_legacy_signature_algorithm(uint16_t *out, const EVP_PKEY *pkey) { | |||||
bool tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out) { | bool tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out) { | ||||
SSL *const ssl = hs->ssl; | SSL *const ssl = hs->ssl; | ||||
CERT *cert = hs->config->cert.get(); | CERT *cert = hs->config->cert.get(); | ||||
DC *dc = cert->dc.get(); | |||||
// Before TLS 1.2, the signature algorithm isn't negotiated as part of the | // Before TLS 1.2, the signature algorithm isn't negotiated as part of the | ||||
// handshake. | // handshake. | ||||
@@ -3641,19 +3680,13 @@ bool tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out) { | |||||
} | } | ||||
Span<const uint16_t> sigalgs = kSignSignatureAlgorithms; | Span<const uint16_t> sigalgs = kSignSignatureAlgorithms; | ||||
if (!cert->sigalgs.empty()) { | |||||
if (ssl_signing_with_dc(hs)) { | |||||
sigalgs = MakeConstSpan(&dc->expected_cert_verify_algorithm, 1); | |||||
} else if (!cert->sigalgs.empty()) { | |||||
sigalgs = cert->sigalgs; | sigalgs = cert->sigalgs; | ||||
} | } | ||||
Span<const uint16_t> peer_sigalgs = hs->peer_sigalgs; | |||||
if (peer_sigalgs.empty() && ssl_protocol_version(ssl) < TLS1_3_VERSION) { | |||||
// If the client didn't specify any signature_algorithms extension then | |||||
// we can assume that it supports SHA1. See | |||||
// http://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 | |||||
static const uint16_t kDefaultPeerAlgorithms[] = {SSL_SIGN_RSA_PKCS1_SHA1, | |||||
SSL_SIGN_ECDSA_SHA1}; | |||||
peer_sigalgs = kDefaultPeerAlgorithms; | |||||
} | |||||
Span<const uint16_t> peer_sigalgs = tls1_get_peer_verify_algorithms(hs); | |||||
for (uint16_t sigalg : sigalgs) { | for (uint16_t sigalg : sigalgs) { | ||||
// SSL_SIGN_RSA_PKCS1_MD5_SHA1 is an internal value and should never be | // SSL_SIGN_RSA_PKCS1_MD5_SHA1 is an internal value and should never be | ||||
@@ -3675,6 +3708,19 @@ bool tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out) { | |||||
return false; | return false; | ||||
} | } | ||||
Span<const uint16_t> tls1_get_peer_verify_algorithms(const SSL_HANDSHAKE *hs) { | |||||
Span<const uint16_t> peer_sigalgs = hs->peer_sigalgs; | |||||
if (peer_sigalgs.empty() && ssl_protocol_version(hs->ssl) < TLS1_3_VERSION) { | |||||
// If the client didn't specify any signature_algorithms extension then | |||||
// we can assume that it supports SHA1. See | |||||
// http://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 | |||||
static const uint16_t kDefaultPeerAlgorithms[] = {SSL_SIGN_RSA_PKCS1_SHA1, | |||||
SSL_SIGN_ECDSA_SHA1}; | |||||
peer_sigalgs = kDefaultPeerAlgorithms; | |||||
} | |||||
return peer_sigalgs; | |||||
} | |||||
bool tls1_verify_channel_id(SSL_HANDSHAKE *hs, const SSLMessage &msg) { | bool tls1_verify_channel_id(SSL_HANDSHAKE *hs, const SSLMessage &msg) { | ||||
SSL *const ssl = hs->ssl; | SSL *const ssl = hs->ssl; | ||||
// A Channel ID handshake message is structured to contain multiple | // A Channel ID handshake message is structured to contain multiple | ||||
@@ -33,7 +33,7 @@ type keyAgreement interface { | |||||
// This method may not be called if the server doesn't send a | // This method may not be called if the server doesn't send a | ||||
// ServerKeyExchange message. | // ServerKeyExchange message. | ||||
processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error | |||||
processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, crypto.PublicKey, *serverKeyExchangeMsg) error | |||||
generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) | generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) | ||||
// peerSignatureAlgorithm returns the signature algorithm used by the | // peerSignatureAlgorithm returns the signature algorithm used by the | ||||
@@ -125,6 +125,7 @@ const ( | |||||
extensionRenegotiationInfo uint16 = 0xff01 | extensionRenegotiationInfo uint16 = 0xff01 | ||||
extensionQUICTransportParams uint16 = 0xffa5 // draft-ietf-quic-tls-13 | extensionQUICTransportParams uint16 = 0xffa5 // draft-ietf-quic-tls-13 | ||||
extensionChannelID uint16 = 30032 // not IANA assigned | extensionChannelID uint16 = 30032 // not IANA assigned | ||||
extensionDelegatedCredentials uint16 = 0xff02 // not IANA assigned | |||||
) | ) | ||||
// TLS signaling cipher suite values | // TLS signaling cipher suite values | ||||
@@ -1636,6 +1637,18 @@ type ProtocolBugs struct { | |||||
// ExpectKeyShares, if not nil, lists (in order) the curves that a ClientHello | // ExpectKeyShares, if not nil, lists (in order) the curves that a ClientHello | ||||
// should have key shares for. | // should have key shares for. | ||||
ExpectedKeyShares []CurveID | ExpectedKeyShares []CurveID | ||||
// ExpectDelegatedCredentials, if true, requires that the handshake present | |||||
// delegated credentials. | |||||
ExpectDelegatedCredentials bool | |||||
// FailIfDelegatedCredentials, if true, causes a handshake failure if the | |||||
// server returns delegated credentials. | |||||
FailIfDelegatedCredentials bool | |||||
// DisableDelegatedCredentials, if true, disables client support for delegated | |||||
// credentials. | |||||
DisableDelegatedCredentials bool | |||||
} | } | ||||
func (c *Config) serverInit() { | func (c *Config) serverInit() { | ||||
@@ -32,6 +32,8 @@ type clientHandshakeState struct { | |||||
masterSecret []byte | masterSecret []byte | ||||
session *ClientSessionState | session *ClientSessionState | ||||
finishedBytes []byte | finishedBytes []byte | ||||
peerPublicKey crypto.PublicKey | |||||
skxAlgo signatureAlgorithm | |||||
} | } | ||||
func mapClientHelloVersion(vers uint16, isDTLS bool) uint16 { | func mapClientHelloVersion(vers uint16, isDTLS bool) uint16 { | ||||
@@ -126,6 +128,7 @@ func (c *Conn) clientHandshake() error { | |||||
pskBinderFirst: c.config.Bugs.PSKBinderFirst, | pskBinderFirst: c.config.Bugs.PSKBinderFirst, | ||||
omitExtensions: c.config.Bugs.OmitExtensions, | omitExtensions: c.config.Bugs.OmitExtensions, | ||||
emptyExtensions: c.config.Bugs.EmptyExtensions, | emptyExtensions: c.config.Bugs.EmptyExtensions, | ||||
delegatedCredentials: !c.config.Bugs.DisableDelegatedCredentials, | |||||
} | } | ||||
if maxVersion >= VersionTLS13 { | if maxVersion >= VersionTLS13 { | ||||
@@ -978,7 +981,6 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { | |||||
if err := hs.verifyCertificates(certMsg); err != nil { | if err := hs.verifyCertificates(certMsg); err != nil { | ||||
return err | return err | ||||
} | } | ||||
leaf := c.peerCertificates[0] | |||||
c.ocspResponse = certMsg.certificates[0].ocspResponse | c.ocspResponse = certMsg.certificates[0].ocspResponse | ||||
c.sctList = certMsg.certificates[0].sctList | c.sctList = certMsg.certificates[0].sctList | ||||
@@ -994,7 +996,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { | |||||
c.peerSignatureAlgorithm = certVerifyMsg.signatureAlgorithm | c.peerSignatureAlgorithm = certVerifyMsg.signatureAlgorithm | ||||
input := hs.finishedHash.certificateVerifyInput(serverCertificateVerifyContextTLS13) | input := hs.finishedHash.certificateVerifyInput(serverCertificateVerifyContextTLS13) | ||||
err = verifyMessage(c.vers, getCertificatePublicKey(leaf), c.config, certVerifyMsg.signatureAlgorithm, input, certVerifyMsg.signature) | |||||
err = verifyMessage(c.vers, hs.peerPublicKey, c.config, certVerifyMsg.signatureAlgorithm, input, certVerifyMsg.signature) | |||||
if err != nil { | if err != nil { | ||||
return err | return err | ||||
} | } | ||||
@@ -1233,7 +1235,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { | |||||
skx, ok := msg.(*serverKeyExchangeMsg) | skx, ok := msg.(*serverKeyExchangeMsg) | ||||
if ok { | if ok { | ||||
hs.writeServerHash(skx.marshal()) | hs.writeServerHash(skx.marshal()) | ||||
err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, leaf, skx) | |||||
err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, hs.peerPublicKey, skx) | |||||
if err != nil { | if err != nil { | ||||
c.sendAlert(alertUnexpectedMessage) | c.sendAlert(alertUnexpectedMessage) | ||||
return err | return err | ||||
@@ -1377,6 +1379,23 @@ func (hs *clientHandshakeState) doFullHandshake() error { | |||||
return nil | return nil | ||||
} | } | ||||
// 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-02#section-3 | |||||
ret := make([]byte, 64, 128) | |||||
for i := range ret { | |||||
ret[i] = 0x20 | |||||
} | |||||
ret = append(ret, []byte("TLS, server delegated credentials\x00")...) | |||||
ret = append(ret, leafDER...) | |||||
ret = append(ret, byte(algorithm>>8), byte(algorithm)) | |||||
ret = append(ret, credBytes...) | |||||
return ret | |||||
} | |||||
func (hs *clientHandshakeState) verifyCertificates(certMsg *certificateMsg) error { | func (hs *clientHandshakeState) verifyCertificates(certMsg *certificateMsg) error { | ||||
c := hs.c | c := hs.c | ||||
@@ -1385,6 +1404,7 @@ func (hs *clientHandshakeState) verifyCertificates(certMsg *certificateMsg) erro | |||||
return errors.New("tls: no certificates sent") | return errors.New("tls: no certificates sent") | ||||
} | } | ||||
var dc *delegatedCredential | |||||
certs := make([]*x509.Certificate, len(certMsg.certificates)) | certs := make([]*x509.Certificate, len(certMsg.certificates)) | ||||
for i, certEntry := range certMsg.certificates { | for i, certEntry := range certMsg.certificates { | ||||
cert, err := x509.ParseCertificate(certEntry.data) | cert, err := x509.ParseCertificate(certEntry.data) | ||||
@@ -1393,6 +1413,22 @@ func (hs *clientHandshakeState) verifyCertificates(certMsg *certificateMsg) erro | |||||
return errors.New("tls: failed to parse certificate from server: " + err.Error()) | return errors.New("tls: failed to parse certificate from server: " + err.Error()) | ||||
} | } | ||||
certs[i] = cert | certs[i] = cert | ||||
if certEntry.delegatedCredential != nil { | |||||
if c.config.Bugs.FailIfDelegatedCredentials { | |||||
c.sendAlert(alertIllegalParameter) | |||||
return errors.New("tls: unexpected delegated credential") | |||||
} | |||||
if i != 0 { | |||||
c.sendAlert(alertIllegalParameter) | |||||
return errors.New("tls: non-leaf certificate has a delegated credential") | |||||
} | |||||
if c.config.Bugs.DisableDelegatedCredentials { | |||||
c.sendAlert(alertIllegalParameter) | |||||
return errors.New("tls: server sent delegated credential without it being requested") | |||||
} | |||||
dc = certEntry.delegatedCredential | |||||
} | |||||
} | } | ||||
if !c.config.InsecureSkipVerify { | if !c.config.InsecureSkipVerify { | ||||
@@ -1417,16 +1453,50 @@ func (hs *clientHandshakeState) verifyCertificates(certMsg *certificateMsg) erro | |||||
} | } | ||||
} | } | ||||
publicKey := getCertificatePublicKey(certs[0]) | |||||
switch publicKey.(type) { | |||||
leafPublicKey := getCertificatePublicKey(certs[0]) | |||||
switch leafPublicKey.(type) { | |||||
case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey: | case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey: | ||||
break | break | ||||
default: | default: | ||||
c.sendAlert(alertUnsupportedCertificate) | c.sendAlert(alertUnsupportedCertificate) | ||||
return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", publicKey) | |||||
return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", leafPublicKey) | |||||
} | } | ||||
c.peerCertificates = certs | c.peerCertificates = certs | ||||
if dc != nil { | |||||
// Note that this doesn't check a) the delegated credential temporal | |||||
// validity nor b) that the certificate has the special OID asserted. | |||||
if dc.expectedTLSVersion != c.wireVersion { | |||||
c.sendAlert(alertBadCertificate) | |||||
return errors.New("tls: delegated credential is for wrong TLS version") | |||||
} | |||||
hs.skxAlgo = dc.expectedCertVerifyAlgo | |||||
var err error | |||||
if hs.peerPublicKey, err = x509.ParsePKIXPublicKey(dc.pkixPublicKey); err != nil { | |||||
c.sendAlert(alertBadCertificate) | |||||
return errors.New("tls: failed to parse public key from delegated credential: " + err.Error()) | |||||
} | |||||
verifier, err := getSigner(c.vers, hs.peerPublicKey, c.config, dc.algorithm, true) | |||||
if err != nil { | |||||
c.sendAlert(alertBadCertificate) | |||||
return errors.New("tls: failed to get verifier for delegated credential: " + err.Error()) | |||||
} | |||||
if err := verifier.verifyMessage(leafPublicKey, delegatedCredentialSignedMessage(dc.signedBytes, dc.algorithm, certs[0].Raw), dc.signature); err != nil { | |||||
c.sendAlert(alertBadCertificate) | |||||
return errors.New("tls: failed to verify delegated credential: " + err.Error()) | |||||
} | |||||
} else if c.config.Bugs.ExpectDelegatedCredentials { | |||||
c.sendAlert(alertInternalError) | |||||
return errors.New("tls: delegated credentials missing") | |||||
} else { | |||||
hs.peerPublicKey = leafPublicKey | |||||
} | |||||
return nil | return nil | ||||
} | } | ||||
@@ -297,6 +297,7 @@ type clientHelloMsg struct { | |||||
emptyExtensions bool | emptyExtensions bool | ||||
pad int | pad int | ||||
compressedCertAlgs []uint16 | compressedCertAlgs []uint16 | ||||
delegatedCredentials bool | |||||
} | } | ||||
func (m *clientHelloMsg) equal(i interface{}) bool { | func (m *clientHelloMsg) equal(i interface{}) bool { | ||||
@@ -350,7 +351,8 @@ func (m *clientHelloMsg) equal(i interface{}) bool { | |||||
m.omitExtensions == m1.omitExtensions && | m.omitExtensions == m1.omitExtensions && | ||||
m.emptyExtensions == m1.emptyExtensions && | m.emptyExtensions == m1.emptyExtensions && | ||||
m.pad == m1.pad && | m.pad == m1.pad && | ||||
eqUint16s(m.compressedCertAlgs, m1.compressedCertAlgs) | |||||
eqUint16s(m.compressedCertAlgs, m1.compressedCertAlgs) && | |||||
m.delegatedCredentials == m1.delegatedCredentials | |||||
} | } | ||||
func (m *clientHelloMsg) marshalKeyShares(bb *byteBuilder) { | func (m *clientHelloMsg) marshalKeyShares(bb *byteBuilder) { | ||||
@@ -592,6 +594,10 @@ func (m *clientHelloMsg) marshal() []byte { | |||||
algIDs.addU16(v) | algIDs.addU16(v) | ||||
} | } | ||||
} | } | ||||
if m.delegatedCredentials { | |||||
extensions.addU16(extensionDelegatedCredentials) | |||||
extensions.addU16(0) // Length is always 0 | |||||
} | |||||
// The PSK extension must be last. See https://tools.ietf.org/html/rfc8446#section-4.2.11 | // The PSK extension must be last. See https://tools.ietf.org/html/rfc8446#section-4.2.11 | ||||
if len(m.pskIdentities) > 0 && !m.pskBinderFirst { | if len(m.pskIdentities) > 0 && !m.pskBinderFirst { | ||||
extensions.addU16(extensionPreSharedKey) | extensions.addU16(extensionPreSharedKey) | ||||
@@ -717,6 +723,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { | |||||
m.alpnProtocols = nil | m.alpnProtocols = nil | ||||
m.extendedMasterSecret = false | m.extendedMasterSecret = false | ||||
m.customExtension = "" | m.customExtension = "" | ||||
m.delegatedCredentials = false | |||||
if len(reader) == 0 { | if len(reader) == 0 { | ||||
// ClientHello is optionally followed by extension data | // ClientHello is optionally followed by extension data | ||||
@@ -947,6 +954,11 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { | |||||
return false | return false | ||||
} | } | ||||
} | } | ||||
case extensionDelegatedCredentials: | |||||
if len(body) != 0 { | |||||
return false | |||||
} | |||||
m.delegatedCredentials = true | |||||
} | } | ||||
if isGREASEValue(extension) { | if isGREASEValue(extension) { | ||||
@@ -1602,6 +1614,18 @@ type certificateEntry struct { | |||||
sctList []byte | sctList []byte | ||||
duplicateExtensions bool | duplicateExtensions bool | ||||
extraExtension []byte | extraExtension []byte | ||||
delegatedCredential *delegatedCredential | |||||
} | |||||
type delegatedCredential struct { | |||||
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-02#section-3 | |||||
signedBytes []byte | |||||
lifetimeSecs uint32 | |||||
expectedCertVerifyAlgo signatureAlgorithm | |||||
expectedTLSVersion uint16 | |||||
pkixPublicKey []byte | |||||
algorithm signatureAlgorithm | |||||
signature []byte | |||||
} | } | ||||
type certificateMsg struct { | type certificateMsg struct { | ||||
@@ -1700,6 +1724,30 @@ func (m *certificateMsg) unmarshal(data []byte) bool { | |||||
} | } | ||||
case extensionSignedCertificateTimestamp: | case extensionSignedCertificateTimestamp: | ||||
cert.sctList = []byte(body) | cert.sctList = []byte(body) | ||||
case extensionDelegatedCredentials: | |||||
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-02#section-3 | |||||
if cert.delegatedCredential != nil { | |||||
return false | |||||
} | |||||
dc := new(delegatedCredential) | |||||
origBody := body | |||||
var expectedCertVerifyAlgo, algorithm uint16 | |||||
if !body.readU32(&dc.lifetimeSecs) || | |||||
!body.readU16(&expectedCertVerifyAlgo) || | |||||
!body.readU16(&dc.expectedTLSVersion) || | |||||
!body.readU24LengthPrefixedBytes(&dc.pkixPublicKey) || | |||||
!body.readU16(&algorithm) || | |||||
!body.readU16LengthPrefixedBytes(&dc.signature) || | |||||
len(body) != 0 { | |||||
return false | |||||
} | |||||
dc.expectedCertVerifyAlgo = signatureAlgorithm(expectedCertVerifyAlgo) | |||||
dc.algorithm = signatureAlgorithm(algorithm) | |||||
dc.signedBytes = []byte(origBody)[:4+2+2+3+len(dc.pkixPublicKey)] | |||||
cert.delegatedCredential = dc | |||||
default: | default: | ||||
return false | return false | ||||
} | } | ||||
@@ -5,6 +5,7 @@ | |||||
package runner | package runner | ||||
import ( | import ( | ||||
"crypto" | |||||
"crypto/ecdsa" | "crypto/ecdsa" | ||||
"crypto/elliptic" | "crypto/elliptic" | ||||
"crypto/rsa" | "crypto/rsa" | ||||
@@ -133,7 +134,7 @@ func (ka *rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certif | |||||
return preMasterSecret, nil | return preMasterSecret, nil | ||||
} | } | ||||
func (ka *rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { | |||||
func (ka *rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, key crypto.PublicKey, skx *serverKeyExchangeMsg) error { | |||||
return errors.New("tls: unexpected ServerKeyExchange") | return errors.New("tls: unexpected ServerKeyExchange") | ||||
} | } | ||||
@@ -456,7 +457,7 @@ func curveForCurveID(id CurveID, config *Config) (ecdhCurve, bool) { | |||||
// to authenticate the ServerKeyExchange parameters. | // to authenticate the ServerKeyExchange parameters. | ||||
type keyAgreementAuthentication interface { | type keyAgreementAuthentication interface { | ||||
signParameters(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error) | signParameters(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error) | ||||
verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, params []byte, sig []byte) error | |||||
verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, key crypto.PublicKey, params []byte, sig []byte) error | |||||
} | } | ||||
// nilKeyAgreementAuthentication does not authenticate the key | // nilKeyAgreementAuthentication does not authenticate the key | ||||
@@ -469,7 +470,7 @@ func (ka *nilKeyAgreementAuthentication) signParameters(config *Config, cert *Ce | |||||
return skx, nil | return skx, nil | ||||
} | } | ||||
func (ka *nilKeyAgreementAuthentication) verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, params []byte, sig []byte) error { | |||||
func (ka *nilKeyAgreementAuthentication) verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, key crypto.PublicKey, params []byte, sig []byte) error { | |||||
return nil | return nil | ||||
} | } | ||||
@@ -529,9 +530,8 @@ func (ka *signedKeyAgreement) signParameters(config *Config, cert *Certificate, | |||||
return skx, nil | return skx, nil | ||||
} | } | ||||
func (ka *signedKeyAgreement) verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, params []byte, sig []byte) error { | |||||
func (ka *signedKeyAgreement) verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, publicKey crypto.PublicKey, params []byte, sig []byte) error { | |||||
// The peer's key must match the cipher type. | // The peer's key must match the cipher type. | ||||
publicKey := getCertificatePublicKey(cert) | |||||
switch ka.keyType { | switch ka.keyType { | ||||
case keyTypeECDSA: | case keyTypeECDSA: | ||||
_, edsaOk := publicKey.(*ecdsa.PublicKey) | _, edsaOk := publicKey.(*ecdsa.PublicKey) | ||||
@@ -646,7 +646,7 @@ func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Cert | |||||
return ka.curve.finish(ckx.ciphertext[1:]) | return ka.curve.finish(ckx.ciphertext[1:]) | ||||
} | } | ||||
func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { | |||||
func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, key crypto.PublicKey, skx *serverKeyExchangeMsg) error { | |||||
if len(skx.key) < 4 { | if len(skx.key) < 4 { | ||||
return errServerKeyExchange | return errServerKeyExchange | ||||
} | } | ||||
@@ -671,7 +671,7 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell | |||||
// Check the signature. | // Check the signature. | ||||
serverECDHParams := skx.key[:4+publicLen] | serverECDHParams := skx.key[:4+publicLen] | ||||
sig := skx.key[4+publicLen:] | sig := skx.key[4+publicLen:] | ||||
return ka.auth.verifyParameters(config, clientHello, serverHello, cert, serverECDHParams, sig) | |||||
return ka.auth.verifyParameters(config, clientHello, serverHello, key, serverECDHParams, sig) | |||||
} | } | ||||
func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { | func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { | ||||
@@ -722,7 +722,7 @@ func (ka *nilKeyAgreement) processClientKeyExchange(config *Config, cert *Certif | |||||
return nil, nil | return nil, nil | ||||
} | } | ||||
func (ka *nilKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { | |||||
func (ka *nilKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, key crypto.PublicKey, skx *serverKeyExchangeMsg) error { | |||||
if len(skx.key) != 0 { | if len(skx.key) != 0 { | ||||
return errServerKeyExchange | return errServerKeyExchange | ||||
} | } | ||||
@@ -820,7 +820,7 @@ func (ka *pskKeyAgreement) processClientKeyExchange(config *Config, cert *Certif | |||||
return makePSKPremaster(otherSecret, config.PreSharedKey), nil | return makePSKPremaster(otherSecret, config.PreSharedKey), nil | ||||
} | } | ||||
func (ka *pskKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { | |||||
func (ka *pskKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, key crypto.PublicKey, skx *serverKeyExchangeMsg) error { | |||||
if len(skx.key) < 2 { | if len(skx.key) < 2 { | ||||
return errServerKeyExchange | return errServerKeyExchange | ||||
} | } | ||||
@@ -833,7 +833,7 @@ func (ka *pskKeyAgreement) processServerKeyExchange(config *Config, clientHello | |||||
// Process the remainder of the ServerKeyExchange. | // Process the remainder of the ServerKeyExchange. | ||||
newSkx := new(serverKeyExchangeMsg) | newSkx := new(serverKeyExchangeMsg) | ||||
newSkx.key = skx.key[2+identityLen:] | newSkx.key = skx.key[2+identityLen:] | ||||
return ka.base.processServerKeyExchange(config, clientHello, serverHello, cert, newSkx) | |||||
return ka.base.processServerKeyExchange(config, clientHello, serverHello, key, newSkx) | |||||
} | } | ||||
func (ka *pskKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { | func (ka *pskKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { | ||||
@@ -16,9 +16,11 @@ package runner | |||||
import ( | import ( | ||||
"bytes" | "bytes" | ||||
"crypto" | |||||
"crypto/ecdsa" | "crypto/ecdsa" | ||||
"crypto/elliptic" | "crypto/elliptic" | ||||
"crypto/rand" | "crypto/rand" | ||||
"crypto/rsa" | |||||
"crypto/x509" | "crypto/x509" | ||||
"crypto/x509/pkix" | "crypto/x509/pkix" | ||||
"encoding/base64" | "encoding/base64" | ||||
@@ -246,6 +248,145 @@ func initCertificates() { | |||||
garbageCertificate.PrivateKey = rsaCertificate.PrivateKey | garbageCertificate.PrivateKey = rsaCertificate.PrivateKey | ||||
} | } | ||||
// delegatedCredentialConfig specifies the shape of a delegated credential, not | |||||
// including the keys themselves. | |||||
type delegatedCredentialConfig struct { | |||||
// lifetime is the amount of time, from the notBefore of the parent | |||||
// 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 | |||||
// tlsVersion is the version of TLS that should be used with this delegated | |||||
// credential. If zero, TLS 1.3 is assumed. | |||||
tlsVersion uint16 | |||||
// algo is the signature algorithm that the delegated credential itself is | |||||
// signed with. Cannot be zero. | |||||
algo signatureAlgorithm | |||||
} | |||||
func loadRSAPrivateKey(filename string) (priv *rsa.PrivateKey, privPKCS8 []byte, err error) { | |||||
pemPath := path.Join(*resourceDir, filename) | |||||
pemBytes, err := ioutil.ReadFile(pemPath) | |||||
if err != nil { | |||||
return nil, nil, err | |||||
} | |||||
block, _ := pem.Decode(pemBytes) | |||||
if block == nil { | |||||
return nil, nil, fmt.Errorf("no PEM block found in %q", pemPath) | |||||
} | |||||
privPKCS8 = block.Bytes | |||||
parsed, err := x509.ParsePKCS8PrivateKey(privPKCS8) | |||||
if err != nil { | |||||
return nil, nil, fmt.Errorf("failed to parse PKCS#8 key from %q", pemPath) | |||||
} | |||||
priv, ok := parsed.(*rsa.PrivateKey) | |||||
if !ok { | |||||
return nil, nil, fmt.Errorf("found %T in %q rather than an RSA private key", parsed, pemPath) | |||||
} | |||||
return priv, privPKCS8, nil | |||||
} | |||||
func createDelegatedCredential(config delegatedCredentialConfig, parentDER []byte, parentPriv crypto.PrivateKey) (dc, privPKCS8 []uint8, err error) { | |||||
expectedAlgo := config.expectedAlgo | |||||
if expectedAlgo == signatureAlgorithm(0) { | |||||
expectedAlgo = signatureECDSAWithP256AndSHA256 | |||||
} | |||||
var pub crypto.PublicKey | |||||
switch expectedAlgo { | |||||
case signatureRSAPKCS1WithMD5, signatureRSAPKCS1WithSHA1, signatureRSAPKCS1WithSHA256, signatureRSAPKCS1WithSHA384, signatureRSAPKCS1WithSHA512, signatureRSAPSSWithSHA256, signatureRSAPSSWithSHA384, signatureRSAPSSWithSHA512: | |||||
// RSA keys are expensive to generate so load from disk instead. | |||||
var priv *rsa.PrivateKey | |||||
if priv, privPKCS8, err = loadRSAPrivateKey(rsaKeyFile); err != nil { | |||||
return nil, nil, err | |||||
} | |||||
pub = &priv.PublicKey | |||||
case signatureECDSAWithSHA1, signatureECDSAWithP256AndSHA256, signatureECDSAWithP384AndSHA384, signatureECDSAWithP521AndSHA512: | |||||
var curve elliptic.Curve | |||||
switch expectedAlgo { | |||||
case signatureECDSAWithSHA1, signatureECDSAWithP256AndSHA256: | |||||
curve = elliptic.P256() | |||||
case signatureECDSAWithP384AndSHA384: | |||||
curve = elliptic.P384() | |||||
case signatureECDSAWithP521AndSHA512: | |||||
curve = elliptic.P521() | |||||
default: | |||||
panic("internal error") | |||||
} | |||||
priv, err := ecdsa.GenerateKey(curve, rand.Reader) | |||||
if err != nil { | |||||
return nil, nil, err | |||||
} | |||||
if privPKCS8, err = x509.MarshalPKCS8PrivateKey(priv); err != nil { | |||||
return nil, nil, err | |||||
} | |||||
pub = &priv.PublicKey | |||||
default: | |||||
return nil, nil, fmt.Errorf("unsupported expected signature algorithm: %x", expectedAlgo) | |||||
} | |||||
lifetime := config.lifetime | |||||
if lifetime == 0 { | |||||
lifetime = 24 * time.Hour | |||||
} | |||||
lifetimeSecs := int64(lifetime.Seconds()) | |||||
if lifetimeSecs > 1<<32 { | |||||
return nil, nil, fmt.Errorf("lifetime %s is too long to be expressed", lifetime) | |||||
} | |||||
tlsVersion := config.tlsVersion | |||||
if tlsVersion == 0 { | |||||
tlsVersion = VersionTLS13 | |||||
} | |||||
if tlsVersion < VersionTLS13 { | |||||
return nil, nil, fmt.Errorf("delegated credentials require TLS 1.3") | |||||
} | |||||
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-02#section-3 | |||||
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(tlsVersion>>8), byte(tlsVersion)) | |||||
pubBytes, err := x509.MarshalPKIXPublicKey(pub) | |||||
if err != nil { | |||||
return nil, nil, err | |||||
} | |||||
dc = append(dc, byte(len(pubBytes)>>16), byte(len(pubBytes)>>8), byte(len(pubBytes))) | |||||
dc = append(dc, pubBytes...) | |||||
var dummyConfig Config | |||||
parentSigner, err := getSigner(tlsVersion, parentPriv, &dummyConfig, config.algo, false /* not for verification */) | |||||
if err != nil { | |||||
return nil, nil, err | |||||
} | |||||
parentSignature, err := parentSigner.signMessage(parentPriv, &dummyConfig, delegatedCredentialSignedMessage(dc, config.algo, parentDER)) | |||||
if err != nil { | |||||
return nil, nil, err | |||||
} | |||||
dc = append(dc, byte(config.algo>>8), byte(config.algo)) | |||||
dc = append(dc, byte(len(parentSignature)>>8), byte(len(parentSignature))) | |||||
dc = append(dc, parentSignature...) | |||||
return dc, privPKCS8, nil | |||||
} | |||||
func getRunnerCertificate(t testCert) Certificate { | func getRunnerCertificate(t testCert) Certificate { | ||||
for _, cert := range testCerts { | for _, cert := range testCerts { | ||||
if cert.id == t { | if cert.id == t { | ||||
@@ -12418,7 +12559,7 @@ func addTLS13HandshakeTests() { | |||||
config: Config{ | config: Config{ | ||||
MaxVersion: VersionTLS13, | MaxVersion: VersionTLS13, | ||||
// Require a HelloRetryRequest for every curve. | // Require a HelloRetryRequest for every curve. | ||||
DefaultCurves: []CurveID{}, | |||||
DefaultCurves: []CurveID{}, | |||||
CurvePreferences: []CurveID{CurveX25519}, | CurvePreferences: []CurveID{CurveX25519}, | ||||
}, | }, | ||||
expectedCurveID: CurveX25519, | expectedCurveID: CurveX25519, | ||||
@@ -12428,8 +12569,8 @@ func addTLS13HandshakeTests() { | |||||
testType: serverTest, | testType: serverTest, | ||||
name: "SendHelloRetryRequest-2-TLS13", | name: "SendHelloRetryRequest-2-TLS13", | ||||
config: Config{ | config: Config{ | ||||
MaxVersion: VersionTLS13, | |||||
DefaultCurves: []CurveID{CurveP384}, | |||||
MaxVersion: VersionTLS13, | |||||
DefaultCurves: []CurveID{CurveP384}, | |||||
CurvePreferences: []CurveID{CurveX25519, CurveP384}, | CurvePreferences: []CurveID{CurveX25519, CurveP384}, | ||||
}, | }, | ||||
// Although the ClientHello did not predict our preferred curve, | // Although the ClientHello did not predict our preferred curve, | ||||
@@ -14597,6 +14738,128 @@ func addJDK11WorkaroundTests() { | |||||
} | } | ||||
} | } | ||||
func addDelegatedCredentialTests() { | |||||
certPath := path.Join(*resourceDir, rsaCertificateFile) | |||||
pemBytes, err := ioutil.ReadFile(certPath) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
block, _ := pem.Decode(pemBytes) | |||||
if block == nil { | |||||
panic(fmt.Sprintf("no PEM block found in %q", certPath)) | |||||
} | |||||
parentDER := block.Bytes | |||||
rsaPriv, _, err := loadRSAPrivateKey(rsaKeyFile) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
ecdsaDC, ecdsaPKCS8, err := createDelegatedCredential(delegatedCredentialConfig{ | |||||
algo: signatureRSAPSSWithSHA256, | |||||
}, parentDER, rsaPriv) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
ecdsaFlagValue := fmt.Sprintf("%x,%x", ecdsaDC, ecdsaPKCS8) | |||||
testCases = append(testCases, testCase{ | |||||
testType: serverTest, | |||||
name: "DelegatedCredentials-NoClientSupport", | |||||
config: Config{ | |||||
MinVersion: VersionTLS13, | |||||
MaxVersion: VersionTLS13, | |||||
Bugs: ProtocolBugs{ | |||||
DisableDelegatedCredentials: true, | |||||
}, | |||||
}, | |||||
flags: []string{ | |||||
"-delegated-credential", ecdsaFlagValue, | |||||
}, | |||||
}) | |||||
testCases = append(testCases, testCase{ | |||||
testType: serverTest, | |||||
name: "DelegatedCredentials-Basic", | |||||
config: Config{ | |||||
MinVersion: VersionTLS13, | |||||
MaxVersion: VersionTLS13, | |||||
Bugs: ProtocolBugs{ | |||||
ExpectDelegatedCredentials: true, | |||||
}, | |||||
}, | |||||
flags: []string{ | |||||
"-delegated-credential", ecdsaFlagValue, | |||||
}, | |||||
}) | |||||
testCases = append(testCases, testCase{ | |||||
testType: serverTest, | |||||
name: "DelegatedCredentials-SigAlgoMissing", | |||||
config: Config{ | |||||
MinVersion: VersionTLS13, | |||||
MaxVersion: VersionTLS13, | |||||
Bugs: ProtocolBugs{ | |||||
FailIfDelegatedCredentials: true, | |||||
}, | |||||
// If the client doesn't support the delegated credential signature | |||||
// algorithm then the handshake should complete without using delegated | |||||
// credentials. | |||||
VerifySignatureAlgorithms: []signatureAlgorithm{signatureRSAPSSWithSHA256}, | |||||
}, | |||||
flags: []string{ | |||||
"-delegated-credential", ecdsaFlagValue, | |||||
}, | |||||
}) | |||||
badTLSVersionDC, badTLSVersionPKCS8, err := createDelegatedCredential(delegatedCredentialConfig{ | |||||
algo: signatureRSAPSSWithSHA256, | |||||
tlsVersion: 0x1234, | |||||
}, parentDER, rsaPriv) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
badTLSVersionFlagValue := fmt.Sprintf("%x,%x", badTLSVersionDC, badTLSVersionPKCS8) | |||||
testCases = append(testCases, testCase{ | |||||
testType: serverTest, | |||||
name: "DelegatedCredentials-BadTLSVersion", | |||||
config: Config{ | |||||
// The delegated credential specifies a crazy TLS version, which should | |||||
// prevent its use. | |||||
MinVersion: VersionTLS13, | |||||
MaxVersion: VersionTLS13, | |||||
Bugs: ProtocolBugs{ | |||||
FailIfDelegatedCredentials: true, | |||||
}, | |||||
}, | |||||
flags: []string{ | |||||
"-delegated-credential", badTLSVersionFlagValue, | |||||
}, | |||||
}) | |||||
// This flag value has mismatched public and private keys which should cause a | |||||
// configuration error in the shim. | |||||
mismatchFlagValue := fmt.Sprintf("%x,%x", ecdsaDC, badTLSVersionPKCS8) | |||||
testCases = append(testCases, testCase{ | |||||
testType: serverTest, | |||||
name: "DelegatedCredentials-KeyMismatch", | |||||
config: Config{ | |||||
MinVersion: VersionTLS13, | |||||
MaxVersion: VersionTLS13, | |||||
Bugs: ProtocolBugs{ | |||||
FailIfDelegatedCredentials: true, | |||||
}, | |||||
}, | |||||
flags: []string{ | |||||
"-delegated-credential", mismatchFlagValue, | |||||
}, | |||||
shouldFail: true, | |||||
expectedError: ":KEY_VALUES_MISMATCH:", | |||||
}) | |||||
} | |||||
func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) { | func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) { | ||||
defer wg.Done() | defer wg.Done() | ||||
@@ -14732,6 +14995,7 @@ func main() { | |||||
addOmitExtensionsTests() | addOmitExtensionsTests() | ||||
addCertCompressionTests() | addCertCompressionTests() | ||||
addJDK11WorkaroundTests() | addJDK11WorkaroundTests() | ||||
addDelegatedCredentialTests() | |||||
testCases = append(testCases, convertToSplitHandshakeTests(testCases)...) | testCases = append(testCases, convertToSplitHandshakeTests(testCases)...) | ||||
@@ -177,6 +177,7 @@ const Flag<std::string> kStringFlags[] = { | |||||
{ "-expect-client-ca-list", &TestConfig::expected_client_ca_list }, | { "-expect-client-ca-list", &TestConfig::expected_client_ca_list }, | ||||
{ "-expect-msg-callback", &TestConfig::expect_msg_callback }, | { "-expect-msg-callback", &TestConfig::expect_msg_callback }, | ||||
{ "-handshaker-path", &TestConfig::handshaker_path }, | { "-handshaker-path", &TestConfig::handshaker_path }, | ||||
{ "-delegated-credential", &TestConfig::delegated_credential }, | |||||
}; | }; | ||||
const Flag<std::string> kBase64Flags[] = { | const Flag<std::string> kBase64Flags[] = { | ||||
@@ -1670,5 +1671,40 @@ bssl::UniquePtr<SSL> TestConfig::NewSSL( | |||||
} | } | ||||
} | } | ||||
if (!delegated_credential.empty()) { | |||||
std::string::size_type comma = delegated_credential.find(','); | |||||
if (comma == std::string::npos) { | |||||
fprintf(stderr, "failed to find comma in delegated credential argument"); | |||||
return nullptr; | |||||
} | |||||
const std::string dc_hex = delegated_credential.substr(0, comma); | |||||
const std::string pkcs8_hex = delegated_credential.substr(comma + 1); | |||||
std::string dc, pkcs8; | |||||
if (!HexDecode(&dc, dc_hex) || !HexDecode(&pkcs8, pkcs8_hex)) { | |||||
fprintf(stderr, "failed to hex decode delegated credential argument"); | |||||
return nullptr; | |||||
} | |||||
CBS dc_cbs(bssl::Span<const uint8_t>( | |||||
reinterpret_cast<const uint8_t *>(dc.data()), dc.size())); | |||||
CBS pkcs8_cbs(bssl::Span<const uint8_t>( | |||||
reinterpret_cast<const uint8_t *>(pkcs8.data()), pkcs8.size())); | |||||
bssl::UniquePtr<EVP_PKEY> priv(EVP_parse_private_key(&pkcs8_cbs)); | |||||
if (!priv) { | |||||
fprintf(stderr, "failed to parse delegated credential private key"); | |||||
return nullptr; | |||||
} | |||||
bssl::UniquePtr<CRYPTO_BUFFER> dc_buf( | |||||
CRYPTO_BUFFER_new_from_CBS(&dc_cbs, nullptr)); | |||||
if (!SSL_set1_delegated_credential(ssl.get(), dc_buf.get(), | |||||
priv.get(), nullptr)) { | |||||
fprintf(stderr, "SSL_set1_delegated_credential failed.\n"); | |||||
return nullptr; | |||||
} | |||||
} | |||||
return ssl; | return ssl; | ||||
} | } |
@@ -172,6 +172,7 @@ struct TestConfig { | |||||
bool server_preference = false; | bool server_preference = false; | ||||
bool export_traffic_secrets = false; | bool export_traffic_secrets = false; | ||||
bool key_update = false; | bool key_update = false; | ||||
std::string delegated_credential; | |||||
int argc; | int argc; | ||||
char **argv; | char **argv; | ||||
@@ -418,6 +418,7 @@ bool tls13_process_finished(SSL_HANDSHAKE *hs, const SSLMessage &msg, | |||||
bool tls13_add_certificate(SSL_HANDSHAKE *hs) { | bool tls13_add_certificate(SSL_HANDSHAKE *hs) { | ||||
SSL *const ssl = hs->ssl; | SSL *const ssl = hs->ssl; | ||||
CERT *const cert = hs->config->cert.get(); | CERT *const cert = hs->config->cert.get(); | ||||
DC *const dc = cert->dc.get(); | |||||
ScopedCBB cbb; | ScopedCBB cbb; | ||||
CBB *body, body_storage, certificate_list; | CBB *body, body_storage, certificate_list; | ||||
@@ -484,6 +485,19 @@ bool tls13_add_certificate(SSL_HANDSHAKE *hs) { | |||||
} | } | ||||
} | } | ||||
if (ssl_signing_with_dc(hs)) { | |||||
const CRYPTO_BUFFER *raw = dc->raw.get(); | |||||
if (!CBB_add_u16(&extensions, TLSEXT_TYPE_delegated_credential) || | |||||
!CBB_add_u16(&extensions, CRYPTO_BUFFER_len(raw)) || | |||||
!CBB_add_bytes(&extensions, | |||||
CRYPTO_BUFFER_data(raw), | |||||
CRYPTO_BUFFER_len(raw)) || | |||||
!CBB_flush(&extensions)) { | |||||
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); | |||||
return 0; | |||||
} | |||||
} | |||||
for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain.get()); i++) { | for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain.get()); i++) { | ||||
CRYPTO_BUFFER *cert_buf = sk_CRYPTO_BUFFER_value(cert->chain.get(), i); | CRYPTO_BUFFER *cert_buf = sk_CRYPTO_BUFFER_value(cert->chain.get(), i); | ||||
CBB child; | CBB child; | ||||