MONO_API int mono_btls_ssl_ctx_is_cipher_supported (MonoBtlsSslCtx *ctx, uint16_t value) { const SSL_CIPHER *cipher; cipher = SSL_get_cipher_by_value (value); return cipher != NULL; }
static const SSL_CIPHER *choose_tls13_cipher( const SSL *ssl, const SSL_CLIENT_HELLO *client_hello) { if (client_hello->cipher_suites_len % 2 != 0) { return NULL; } CBS cipher_suites; CBS_init(&cipher_suites, client_hello->cipher_suites, client_hello->cipher_suites_len); const int aes_is_fine = EVP_has_aes_hardware(); const uint16_t version = ssl3_protocol_version(ssl); const SSL_CIPHER *best = NULL; while (CBS_len(&cipher_suites) > 0) { uint16_t cipher_suite; if (!CBS_get_u16(&cipher_suites, &cipher_suite)) { return NULL; } /* Limit to TLS 1.3 ciphers we know about. */ const SSL_CIPHER *candidate = SSL_get_cipher_by_value(cipher_suite); if (candidate == NULL || SSL_CIPHER_get_min_version(candidate) > version || SSL_CIPHER_get_max_version(candidate) < version) { continue; } /* TLS 1.3 removes legacy ciphers, so honor the client order, but prefer * ChaCha20 if we do not have AES hardware. */ if (aes_is_fine) { return candidate; } if (candidate->algorithm_enc == SSL_CHACHA20POLY1305) { return candidate; } if (best == NULL) { best = candidate; } } return best; }
int mono_btls_ssl_ctx_set_ciphers (MonoBtlsSslCtx *ctx, int count, const uint16_t *data, int allow_unsupported) { CBB cbb; int i, ret = 0; if (!CBB_init (&cbb, 64)) goto err; /* Assemble a cipher string with the specified ciphers' names. */ for (i = 0; i < count; i++) { const char *name; const SSL_CIPHER *cipher = SSL_get_cipher_by_value (data [i]); if (!cipher) { debug_printf (ctx, "mono_btls_ssl_ctx_set_ciphers(): unknown cipher %02x", data [i]); if (!allow_unsupported) goto err; continue; } name = SSL_CIPHER_get_name (cipher); if (i > 0 && !CBB_add_u8 (&cbb, ':')) goto err; if (!CBB_add_bytes (&cbb, (const uint8_t *)name, strlen(name))) goto err; } /* NUL-terminate the string. */ if (!CBB_add_u8 (&cbb, 0)) goto err; ret = SSL_CTX_set_cipher_list (ctx->ctx, (const char *)CBB_data (&cbb)); err: CBB_cleanup (&cbb); return ret; }
static enum ssl_hs_wait_t do_process_server_hello(SSL *ssl, SSL_HANDSHAKE *hs) { if (!tls13_check_message_type(ssl, SSL3_MT_SERVER_HELLO)) { return ssl_hs_error; } CBS cbs, server_random, extensions; uint16_t server_wire_version; uint16_t cipher_suite; CBS_init(&cbs, ssl->init_msg, ssl->init_num); if (!CBS_get_u16(&cbs, &server_wire_version) || !CBS_get_bytes(&cbs, &server_random, SSL3_RANDOM_SIZE) || !CBS_get_u16(&cbs, &cipher_suite) || !CBS_get_u16_length_prefixed(&cbs, &extensions) || CBS_len(&cbs) != 0) { ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); return ssl_hs_error; } if (server_wire_version != ssl->version) { ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER); return ssl_hs_error; } /* Parse out the extensions. */ int have_key_share = 0; CBS key_share; while (CBS_len(&extensions) != 0) { uint16_t type; CBS extension; if (!CBS_get_u16(&extensions, &type) || !CBS_get_u16_length_prefixed(&extensions, &extension)) { OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT); ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); return ssl_hs_error; } switch (type) { case TLSEXT_TYPE_key_share: if (have_key_share) { OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION); ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); return ssl_hs_error; } key_share = extension; have_key_share = 1; break; default: OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION); ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION); return ssl_hs_error; } } assert(ssl->s3->have_version); memcpy(ssl->s3->server_random, CBS_data(&server_random), SSL3_RANDOM_SIZE); ssl->hit = 0; if (!ssl_get_new_session(ssl, 0)) { ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); return ssl_hs_error; } const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite); if (cipher == NULL) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED); ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); return ssl_hs_error; } /* Check if the cipher is disabled. */ if ((cipher->algorithm_mkey & ssl->cert->mask_k) || (cipher->algorithm_auth & ssl->cert->mask_a) || SSL_CIPHER_get_min_version(cipher) > ssl3_protocol_version(ssl) || SSL_CIPHER_get_max_version(cipher) < ssl3_protocol_version(ssl) || !sk_SSL_CIPHER_find(ssl_get_ciphers_by_id(ssl), NULL, cipher)) { OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED); ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); return ssl_hs_error; } ssl->session->cipher = cipher; ssl->s3->tmp.new_cipher = cipher; /* The PRF hash is now known. Set up the key schedule. */ static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0}; size_t hash_len = EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl))); if (!tls13_init_key_schedule(ssl, kZeroes, hash_len)) { return ssl_hs_error; } /* Resolve PSK and incorporate it into the secret. */ if (cipher->algorithm_auth == SSL_aPSK) { /* TODO(davidben): Support PSK. */ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return ssl_hs_error; } else if (!tls13_advance_key_schedule(ssl, kZeroes, hash_len)) { return ssl_hs_error; } /* Resolve ECDHE and incorporate it into the secret. */ if (cipher->algorithm_mkey == SSL_kECDHE) { if (!have_key_share) { OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE); ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION); return ssl_hs_error; } uint8_t *dhe_secret; size_t dhe_secret_len; uint8_t alert = SSL_AD_DECODE_ERROR; if (!ext_key_share_parse_serverhello(ssl, &dhe_secret, &dhe_secret_len, &alert, &key_share)) { ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); return ssl_hs_error; } int ok = tls13_advance_key_schedule(ssl, dhe_secret, dhe_secret_len); OPENSSL_free(dhe_secret); if (!ok) { return ssl_hs_error; } } else { if (have_key_share) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION); ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION); return ssl_hs_error; } if (!tls13_advance_key_schedule(ssl, kZeroes, hash_len)) { return ssl_hs_error; } } /* If there was no HelloRetryRequest, the version negotiation logic has * already hashed the message. */ if (ssl->s3->hs->retry_group != 0 && !ssl->method->hash_current_message(ssl)) { return ssl_hs_error; } if (!tls13_set_handshake_traffic(ssl)) { return ssl_hs_error; } hs->state = state_process_encrypted_extensions; return ssl_hs_read_message; }
SSL_SESSION *SSL_SESSION_parse(CBS *cbs, const SSL_X509_METHOD *x509_method, CRYPTO_BUFFER_POOL *pool) { SSL_SESSION *ret = ssl_session_new(x509_method); if (ret == NULL) { goto err; } CBS session; uint64_t version, ssl_version; if (!CBS_get_asn1(cbs, &session, CBS_ASN1_SEQUENCE) || !CBS_get_asn1_uint64(&session, &version) || version != kVersion || !CBS_get_asn1_uint64(&session, &ssl_version)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } ret->ssl_version = ssl_version; CBS cipher; uint16_t cipher_value; if (!CBS_get_asn1(&session, &cipher, CBS_ASN1_OCTETSTRING) || !CBS_get_u16(&cipher, &cipher_value) || CBS_len(&cipher) != 0) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } ret->cipher = SSL_get_cipher_by_value(cipher_value); if (ret->cipher == NULL) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_CIPHER); goto err; } CBS session_id, master_key; if (!CBS_get_asn1(&session, &session_id, CBS_ASN1_OCTETSTRING) || CBS_len(&session_id) > SSL3_MAX_SSL_SESSION_ID_LENGTH || !CBS_get_asn1(&session, &master_key, CBS_ASN1_OCTETSTRING) || CBS_len(&master_key) > SSL_MAX_MASTER_KEY_LENGTH) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } OPENSSL_memcpy(ret->session_id, CBS_data(&session_id), CBS_len(&session_id)); ret->session_id_length = CBS_len(&session_id); OPENSSL_memcpy(ret->master_key, CBS_data(&master_key), CBS_len(&master_key)); ret->master_key_length = CBS_len(&master_key); CBS child; uint64_t timeout; if (!CBS_get_asn1(&session, &child, kTimeTag) || !CBS_get_asn1_uint64(&child, &ret->time) || !CBS_get_asn1(&session, &child, kTimeoutTag) || !CBS_get_asn1_uint64(&child, &timeout) || timeout > UINT32_MAX) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } ret->timeout = (uint32_t)timeout; CBS peer; int has_peer; if (!CBS_get_optional_asn1(&session, &peer, &has_peer, kPeerTag) || (has_peer && CBS_len(&peer) == 0)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } /* |peer| is processed with the certificate chain. */ if (!SSL_SESSION_parse_bounded_octet_string( &session, ret->sid_ctx, &ret->sid_ctx_length, sizeof(ret->sid_ctx), kSessionIDContextTag) || !SSL_SESSION_parse_long(&session, &ret->verify_result, kVerifyResultTag, X509_V_OK) || !SSL_SESSION_parse_string(&session, &ret->tlsext_hostname, kHostNameTag) || !SSL_SESSION_parse_string(&session, &ret->psk_identity, kPSKIdentityTag) || !SSL_SESSION_parse_u32(&session, &ret->tlsext_tick_lifetime_hint, kTicketLifetimeHintTag, 0) || !SSL_SESSION_parse_octet_string(&session, &ret->tlsext_tick, &ret->tlsext_ticklen, kTicketTag)) { goto err; } if (CBS_peek_asn1_tag(&session, kPeerSHA256Tag)) { CBS peer_sha256; if (!CBS_get_asn1(&session, &child, kPeerSHA256Tag) || !CBS_get_asn1(&child, &peer_sha256, CBS_ASN1_OCTETSTRING) || CBS_len(&peer_sha256) != sizeof(ret->peer_sha256) || CBS_len(&child) != 0) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } OPENSSL_memcpy(ret->peer_sha256, CBS_data(&peer_sha256), sizeof(ret->peer_sha256)); ret->peer_sha256_valid = 1; } else { ret->peer_sha256_valid = 0; } if (!SSL_SESSION_parse_bounded_octet_string( &session, ret->original_handshake_hash, &ret->original_handshake_hash_len, sizeof(ret->original_handshake_hash), kOriginalHandshakeHashTag) || !SSL_SESSION_parse_octet_string( &session, &ret->tlsext_signed_cert_timestamp_list, &ret->tlsext_signed_cert_timestamp_list_length, kSignedCertTimestampListTag) || !SSL_SESSION_parse_octet_string( &session, &ret->ocsp_response, &ret->ocsp_response_length, kOCSPResponseTag)) { goto err; } int extended_master_secret; if (!CBS_get_optional_asn1_bool(&session, &extended_master_secret, kExtendedMasterSecretTag, 0 /* default to false */)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } ret->extended_master_secret = !!extended_master_secret; if (!SSL_SESSION_parse_u16(&session, &ret->group_id, kGroupIDTag, 0)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } CBS cert_chain; CBS_init(&cert_chain, NULL, 0); int has_cert_chain; if (!CBS_get_optional_asn1(&session, &cert_chain, &has_cert_chain, kCertChainTag) || (has_cert_chain && CBS_len(&cert_chain) == 0)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } if (has_cert_chain && !has_peer) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } if (has_peer || has_cert_chain) { ret->certs = sk_CRYPTO_BUFFER_new_null(); if (ret->certs == NULL) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } if (has_peer) { /* TODO(agl): this should use the |SSL_CTX|'s pool. */ CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&peer, pool); if (buffer == NULL || !sk_CRYPTO_BUFFER_push(ret->certs, buffer)) { CRYPTO_BUFFER_free(buffer); OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } while (CBS_len(&cert_chain) > 0) { CBS cert; if (!CBS_get_any_asn1_element(&cert_chain, &cert, NULL, NULL) || CBS_len(&cert) == 0) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } /* TODO(agl): this should use the |SSL_CTX|'s pool. */ CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&cert, pool); if (buffer == NULL || !sk_CRYPTO_BUFFER_push(ret->certs, buffer)) { CRYPTO_BUFFER_free(buffer); OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } } if (!x509_method->session_cache_objects(ret)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } CBS age_add; int age_add_present; if (!CBS_get_optional_asn1_octet_string(&session, &age_add, &age_add_present, kTicketAgeAddTag) || (age_add_present && !CBS_get_u32(&age_add, &ret->ticket_age_add)) || CBS_len(&age_add) != 0) { goto err; } ret->ticket_age_add_valid = age_add_present; int is_server; if (!CBS_get_optional_asn1_bool(&session, &is_server, kIsServerTag, 1 /* default to true */)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } /* TODO: in time we can include |is_server| for servers too, then we can enforce that client and server sessions are never mixed up. */ ret->is_server = is_server; if (!SSL_SESSION_parse_u16(&session, &ret->peer_signature_algorithm, kPeerSignatureAlgorithmTag, 0) || !SSL_SESSION_parse_u32(&session, &ret->ticket_max_early_data, kTicketMaxEarlyDataTag, 0) || !SSL_SESSION_parse_u32(&session, &ret->auth_timeout, kAuthTimeoutTag, ret->timeout) || !SSL_SESSION_parse_octet_string(&session, &ret->early_alpn, &ret->early_alpn_len, kEarlyALPNTag) || CBS_len(&session) != 0) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } return ret; err: SSL_SESSION_free(ret); return NULL; }
MONO_API int mono_btls_ssl_ctx_set_ciphers (MonoBtlsSslCtx *ctx, int count, const uint16_t *data, int allow_unsupported) { STACK_OF(SSL_CIPHER) *ciphers = NULL; struct ssl_cipher_preference_list_st *pref_list = NULL; uint8_t *in_group_flags = NULL; int i; ciphers = sk_SSL_CIPHER_new_null (); if (!ciphers) goto err; for (i = 0; i < count; i++) { const SSL_CIPHER *cipher = SSL_get_cipher_by_value (data [i]); if (!cipher) { debug_printf (ctx, "mono_btls_ssl_ctx_set_ciphers(): unknown cipher %02x", data [i]); if (!allow_unsupported) goto err; continue; } if (!sk_SSL_CIPHER_push (ciphers, cipher)) goto err; } pref_list = OPENSSL_malloc (sizeof (struct ssl_cipher_preference_list_st)); if (!pref_list) goto err; memset (pref_list, 0, sizeof (struct ssl_cipher_preference_list_st)); pref_list->ciphers = sk_SSL_CIPHER_dup (ciphers); if (!pref_list->ciphers) goto err; pref_list->in_group_flags = OPENSSL_malloc (sk_SSL_CIPHER_num (ciphers)); if (!pref_list->in_group_flags) goto err; if (ctx->ctx->cipher_list) ssl_cipher_preference_list_free (ctx->ctx->cipher_list); if (ctx->ctx->cipher_list_by_id) sk_SSL_CIPHER_free (ctx->ctx->cipher_list_by_id); if (ctx->ctx->cipher_list_tls10) { ssl_cipher_preference_list_free (ctx->ctx->cipher_list_tls10); ctx->ctx->cipher_list_tls10 = NULL; } if (ctx->ctx->cipher_list_tls11) { ssl_cipher_preference_list_free (ctx->ctx->cipher_list_tls11); ctx->ctx->cipher_list_tls11 = NULL; } ctx->ctx->cipher_list = pref_list; ctx->ctx->cipher_list_by_id = ciphers; return (int)sk_SSL_CIPHER_num (ciphers); err: sk_SSL_CIPHER_free (ciphers); OPENSSL_free (pref_list); OPENSSL_free (in_group_flags); return 0; }
static enum ssl_hs_wait_t do_select_parameters(SSL *ssl, SSL_HANDSHAKE *hs) { if (!ssl->s3->session_reused) { /* Call |cert_cb| to update server certificates if required. */ if (ssl->cert->cert_cb != NULL) { int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg); if (rv == 0) { OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR); ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); return ssl_hs_error; } if (rv < 0) { hs->state = state_select_parameters; return ssl_hs_x509_lookup; } } } struct ssl_early_callback_ctx client_hello; if (!ssl_early_callback_init(ssl, &client_hello, ssl->init_msg, ssl->init_num)) { OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED); ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); return ssl_hs_error; } if (!ssl->s3->session_reused) { const SSL_CIPHER *cipher = ssl3_choose_cipher(ssl, &client_hello, ssl_get_cipher_preferences(ssl)); if (cipher == NULL) { OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER); ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); return ssl_hs_error; } ssl->s3->new_session->cipher = cipher; ssl->s3->tmp.new_cipher = cipher; } else { uint16_t resumption_cipher; if (!ssl_cipher_get_ecdhe_psk_cipher(ssl->s3->new_session->cipher, &resumption_cipher)) { OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER); ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); return ssl_hs_error; } ssl->s3->tmp.new_cipher = SSL_get_cipher_by_value(resumption_cipher); } ssl->method->received_flight(ssl); /* The PRF hash is now known. Set up the key schedule and hash the * ClientHello. */ size_t resumption_ctx_len = EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl))); if (ssl->s3->session_reused) { uint8_t resumption_ctx[EVP_MAX_MD_SIZE]; if (!tls13_resumption_context(ssl, resumption_ctx, resumption_ctx_len, ssl->s3->new_session) || !tls13_init_key_schedule(ssl, resumption_ctx, resumption_ctx_len)) { return ssl_hs_error; } } else { if (!tls13_init_key_schedule(ssl, kZeroes, resumption_ctx_len)) { return ssl_hs_error; } } /* Resolve PSK and incorporate it into the secret. */ if (!resolve_psk_secret(ssl)) { return ssl_hs_error; } /* Resolve ECDHE and incorporate it into the secret. */ int need_retry; if (!resolve_ecdhe_secret(ssl, &need_retry, &client_hello)) { if (need_retry) { hs->state = state_send_hello_retry_request; return ssl_hs_ok; } return ssl_hs_error; } hs->state = state_send_server_hello; return ssl_hs_ok; }