Implement server support for delegated credentials.
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>
This commit is contained in:
parent
4545503926
commit
6c1b376e1d
@ -75,6 +75,7 @@ SSL,157,INAPPROPRIATE_FALLBACK
|
||||
SSL,259,INVALID_ALPN_PROTOCOL
|
||||
SSL,158,INVALID_COMMAND
|
||||
SSL,256,INVALID_COMPRESSION_LIST
|
||||
SSL,301,INVALID_DELEGATED_CREDENTIAL
|
||||
SSL,159,INVALID_MESSAGE
|
||||
SSL,251,INVALID_OUTER_RECORD_TYPE
|
||||
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);
|
||||
|
||||
|
||||
// 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 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_WRONG_ENCRYPTION_LEVEL_RECEIVED 299
|
||||
#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_UNEXPECTED_MESSAGE 1010
|
||||
#define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
|
||||
|
@ -231,6 +231,10 @@ extern "C" {
|
||||
// ExtensionType value from RFC5746
|
||||
#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
|
||||
#define TLSEXT_TYPE_certificate_timestamp 18
|
||||
|
||||
|
@ -135,6 +135,7 @@ SSL_HANDSHAKE::SSL_HANDSHAKE(SSL *ssl_arg)
|
||||
cert_request(false),
|
||||
certificate_status_expected(false),
|
||||
ocsp_stapling_requested(false),
|
||||
delegated_credential_requested(false),
|
||||
should_ack_sni(false),
|
||||
in_false_start(false),
|
||||
in_early_data(false),
|
||||
|
@ -1370,6 +1370,49 @@ enum handback_t {
|
||||
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 {
|
||||
explicit SSL_HANDSHAKE(SSL *ssl);
|
||||
~SSL_HANDSHAKE();
|
||||
@ -1541,6 +1584,10 @@ struct SSL_HANDSHAKE {
|
||||
// ocsp_stapling_requested is true if a client requested OCSP stapling.
|
||||
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 be echoed in the ServerHello.
|
||||
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.
|
||||
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
|
||||
// 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
|
||||
@ -1879,6 +1935,19 @@ struct CERT {
|
||||
// ticket key. Only sessions with a matching value will be accepted.
|
||||
uint8_t 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.
|
||||
|
167
ssl/ssl_cert.cc
167
ssl/ssl_cert.cc
@ -180,6 +180,16 @@ UniquePtr<CERT> ssl_cert_dup(CERT *cert) {
|
||||
ret->sid_ctx_length = cert->sid_ctx_length;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -194,6 +204,10 @@ void ssl_cert_clear_certs(CERT *cert) {
|
||||
cert->chain.reset();
|
||||
cert->privatekey.reset();
|
||||
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),
|
||||
@ -741,10 +755,152 @@ bool ssl_on_certificate_selected(SSL_HANDSHAKE *hs) {
|
||||
CRYPTO_BUFFER_init_CBS(
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
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->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) {
|
||||
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,
|
||||
@ -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,
|
||||
uint16_t sigalg, Span<const uint8_t> in) {
|
||||
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;
|
||||
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 {
|
||||
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) {
|
||||
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;
|
||||
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())) {
|
||||
return ssl_private_key_failure;
|
||||
}
|
||||
|
@ -2715,6 +2715,36 @@ static bool ext_quic_transport_params_add_serverhello(SSL_HANDSHAKE *hs,
|
||||
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
|
||||
|
||||
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_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))
|
||||
@ -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) {
|
||||
SSL *const ssl = hs->ssl;
|
||||
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
|
||||
// handshake.
|
||||
@ -3641,19 +3680,13 @@ bool tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out) {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
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) {
|
||||
SSL *const ssl = hs->ssl;
|
||||
// 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
|
||||
// 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)
|
||||
|
||||
// peerSignatureAlgorithm returns the signature algorithm used by the
|
||||
|
@ -125,6 +125,7 @@ const (
|
||||
extensionRenegotiationInfo uint16 = 0xff01
|
||||
extensionQUICTransportParams uint16 = 0xffa5 // draft-ietf-quic-tls-13
|
||||
extensionChannelID uint16 = 30032 // not IANA assigned
|
||||
extensionDelegatedCredentials uint16 = 0xff02 // not IANA assigned
|
||||
)
|
||||
|
||||
// TLS signaling cipher suite values
|
||||
@ -1636,6 +1637,18 @@ type ProtocolBugs struct {
|
||||
// ExpectKeyShares, if not nil, lists (in order) the curves that a ClientHello
|
||||
// should have key shares for.
|
||||
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() {
|
||||
|
@ -32,6 +32,8 @@ type clientHandshakeState struct {
|
||||
masterSecret []byte
|
||||
session *ClientSessionState
|
||||
finishedBytes []byte
|
||||
peerPublicKey crypto.PublicKey
|
||||
skxAlgo signatureAlgorithm
|
||||
}
|
||||
|
||||
func mapClientHelloVersion(vers uint16, isDTLS bool) uint16 {
|
||||
@ -126,6 +128,7 @@ func (c *Conn) clientHandshake() error {
|
||||
pskBinderFirst: c.config.Bugs.PSKBinderFirst,
|
||||
omitExtensions: c.config.Bugs.OmitExtensions,
|
||||
emptyExtensions: c.config.Bugs.EmptyExtensions,
|
||||
delegatedCredentials: !c.config.Bugs.DisableDelegatedCredentials,
|
||||
}
|
||||
|
||||
if maxVersion >= VersionTLS13 {
|
||||
@ -978,7 +981,6 @@ func (hs *clientHandshakeState) doTLS13Handshake() error {
|
||||
if err := hs.verifyCertificates(certMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
leaf := c.peerCertificates[0]
|
||||
c.ocspResponse = certMsg.certificates[0].ocspResponse
|
||||
c.sctList = certMsg.certificates[0].sctList
|
||||
|
||||
@ -994,7 +996,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error {
|
||||
|
||||
c.peerSignatureAlgorithm = certVerifyMsg.signatureAlgorithm
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -1233,7 +1235,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
|
||||
skx, ok := msg.(*serverKeyExchangeMsg)
|
||||
if ok {
|
||||
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 {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return err
|
||||
@ -1377,6 +1379,23 @@ func (hs *clientHandshakeState) doFullHandshake() error {
|
||||
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 {
|
||||
c := hs.c
|
||||
|
||||
@ -1385,6 +1404,7 @@ func (hs *clientHandshakeState) verifyCertificates(certMsg *certificateMsg) erro
|
||||
return errors.New("tls: no certificates sent")
|
||||
}
|
||||
|
||||
var dc *delegatedCredential
|
||||
certs := make([]*x509.Certificate, len(certMsg.certificates))
|
||||
for i, certEntry := range certMsg.certificates {
|
||||
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())
|
||||
}
|
||||
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 {
|
||||
@ -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:
|
||||
break
|
||||
default:
|
||||
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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -297,6 +297,7 @@ type clientHelloMsg struct {
|
||||
emptyExtensions bool
|
||||
pad int
|
||||
compressedCertAlgs []uint16
|
||||
delegatedCredentials bool
|
||||
}
|
||||
|
||||
func (m *clientHelloMsg) equal(i interface{}) bool {
|
||||
@ -350,7 +351,8 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
|
||||
m.omitExtensions == m1.omitExtensions &&
|
||||
m.emptyExtensions == m1.emptyExtensions &&
|
||||
m.pad == m1.pad &&
|
||||
eqUint16s(m.compressedCertAlgs, m1.compressedCertAlgs)
|
||||
eqUint16s(m.compressedCertAlgs, m1.compressedCertAlgs) &&
|
||||
m.delegatedCredentials == m1.delegatedCredentials
|
||||
}
|
||||
|
||||
func (m *clientHelloMsg) marshalKeyShares(bb *byteBuilder) {
|
||||
@ -592,6 +594,10 @@ func (m *clientHelloMsg) marshal() []byte {
|
||||
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
|
||||
if len(m.pskIdentities) > 0 && !m.pskBinderFirst {
|
||||
extensions.addU16(extensionPreSharedKey)
|
||||
@ -717,6 +723,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
|
||||
m.alpnProtocols = nil
|
||||
m.extendedMasterSecret = false
|
||||
m.customExtension = ""
|
||||
m.delegatedCredentials = false
|
||||
|
||||
if len(reader) == 0 {
|
||||
// ClientHello is optionally followed by extension data
|
||||
@ -947,6 +954,11 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
case extensionDelegatedCredentials:
|
||||
if len(body) != 0 {
|
||||
return false
|
||||
}
|
||||
m.delegatedCredentials = true
|
||||
}
|
||||
|
||||
if isGREASEValue(extension) {
|
||||
@ -1602,6 +1614,18 @@ type certificateEntry struct {
|
||||
sctList []byte
|
||||
duplicateExtensions bool
|
||||
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 {
|
||||
@ -1700,6 +1724,30 @@ func (m *certificateMsg) unmarshal(data []byte) bool {
|
||||
}
|
||||
case extensionSignedCertificateTimestamp:
|
||||
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:
|
||||
return false
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
package runner
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
@ -133,7 +134,7 @@ func (ka *rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certif
|
||||
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")
|
||||
}
|
||||
|
||||
@ -456,7 +457,7 @@ func curveForCurveID(id CurveID, config *Config) (ecdhCurve, bool) {
|
||||
// to authenticate the ServerKeyExchange parameters.
|
||||
type keyAgreementAuthentication interface {
|
||||
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
|
||||
@ -469,7 +470,7 @@ func (ka *nilKeyAgreementAuthentication) signParameters(config *Config, cert *Ce
|
||||
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
|
||||
}
|
||||
|
||||
@ -529,9 +530,8 @@ func (ka *signedKeyAgreement) signParameters(config *Config, cert *Certificate,
|
||||
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.
|
||||
publicKey := getCertificatePublicKey(cert)
|
||||
switch ka.keyType {
|
||||
case keyTypeECDSA:
|
||||
_, edsaOk := publicKey.(*ecdsa.PublicKey)
|
||||
@ -646,7 +646,7 @@ func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Cert
|
||||
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 {
|
||||
return errServerKeyExchange
|
||||
}
|
||||
@ -671,7 +671,7 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
|
||||
// Check the signature.
|
||||
serverECDHParams := 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) {
|
||||
@ -722,7 +722,7 @@ func (ka *nilKeyAgreement) processClientKeyExchange(config *Config, cert *Certif
|
||||
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 {
|
||||
return errServerKeyExchange
|
||||
}
|
||||
@ -820,7 +820,7 @@ func (ka *pskKeyAgreement) processClientKeyExchange(config *Config, cert *Certif
|
||||
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 {
|
||||
return errServerKeyExchange
|
||||
}
|
||||
@ -833,7 +833,7 @@ func (ka *pskKeyAgreement) processServerKeyExchange(config *Config, clientHello
|
||||
// Process the remainder of the ServerKeyExchange.
|
||||
newSkx := new(serverKeyExchangeMsg)
|
||||
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) {
|
||||
|
@ -16,9 +16,11 @@ package runner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
@ -246,6 +248,145 @@ func initCertificates() {
|
||||
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 {
|
||||
for _, cert := range testCerts {
|
||||
if cert.id == t {
|
||||
@ -12418,7 +12559,7 @@ func addTLS13HandshakeTests() {
|
||||
config: Config{
|
||||
MaxVersion: VersionTLS13,
|
||||
// Require a HelloRetryRequest for every curve.
|
||||
DefaultCurves: []CurveID{},
|
||||
DefaultCurves: []CurveID{},
|
||||
CurvePreferences: []CurveID{CurveX25519},
|
||||
},
|
||||
expectedCurveID: CurveX25519,
|
||||
@ -12428,8 +12569,8 @@ func addTLS13HandshakeTests() {
|
||||
testType: serverTest,
|
||||
name: "SendHelloRetryRequest-2-TLS13",
|
||||
config: Config{
|
||||
MaxVersion: VersionTLS13,
|
||||
DefaultCurves: []CurveID{CurveP384},
|
||||
MaxVersion: VersionTLS13,
|
||||
DefaultCurves: []CurveID{CurveP384},
|
||||
CurvePreferences: []CurveID{CurveX25519, CurveP384},
|
||||
},
|
||||
// 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) {
|
||||
defer wg.Done()
|
||||
|
||||
@ -14732,6 +14995,7 @@ func main() {
|
||||
addOmitExtensionsTests()
|
||||
addCertCompressionTests()
|
||||
addJDK11WorkaroundTests()
|
||||
addDelegatedCredentialTests()
|
||||
|
||||
testCases = append(testCases, convertToSplitHandshakeTests(testCases)...)
|
||||
|
||||
|
@ -177,6 +177,7 @@ const Flag<std::string> kStringFlags[] = {
|
||||
{ "-expect-client-ca-list", &TestConfig::expected_client_ca_list },
|
||||
{ "-expect-msg-callback", &TestConfig::expect_msg_callback },
|
||||
{ "-handshaker-path", &TestConfig::handshaker_path },
|
||||
{ "-delegated-credential", &TestConfig::delegated_credential },
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -172,6 +172,7 @@ struct TestConfig {
|
||||
bool server_preference = false;
|
||||
bool export_traffic_secrets = false;
|
||||
bool key_update = false;
|
||||
std::string delegated_credential;
|
||||
|
||||
int argc;
|
||||
char **argv;
|
||||
|
@ -418,6 +418,7 @@ bool tls13_process_finished(SSL_HANDSHAKE *hs, const SSLMessage &msg,
|
||||
bool tls13_add_certificate(SSL_HANDSHAKE *hs) {
|
||||
SSL *const ssl = hs->ssl;
|
||||
CERT *const cert = hs->config->cert.get();
|
||||
DC *const dc = cert->dc.get();
|
||||
|
||||
ScopedCBB cbb;
|
||||
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++) {
|
||||
CRYPTO_BUFFER *cert_buf = sk_CRYPTO_BUFFER_value(cert->chain.get(), i);
|
||||
CBB child;
|
||||
|
Loading…
x
Reference in New Issue
Block a user