static enum ssl_hs_wait_t do_send_server_hello(SSL *ssl, SSL_HANDSHAKE *hs) { CBB cbb, body, extensions; if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO) || !CBB_add_u16(&body, ssl->version) || !RAND_bytes(ssl->s3->server_random, sizeof(ssl->s3->server_random)) || !CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) || !CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) || !CBB_add_u16_length_prefixed(&body, &extensions) || !ssl_ext_pre_shared_key_add_serverhello(ssl, &extensions) || !ssl_ext_key_share_add_serverhello(ssl, &extensions) || !ssl->method->finish_message(ssl, &cbb)) { CBB_cleanup(&cbb); return ssl_hs_error; } hs->state = state_send_encrypted_extensions; return ssl_hs_write_message; }
static int rsa_pub_encode(CBB *out, const EVP_PKEY *key) { // See RFC 3279, section 2.3.1. CBB spki, algorithm, oid, null, key_bitstring; if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) || !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) || !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) || !CBB_add_bytes(&oid, rsa_asn1_meth.oid, rsa_asn1_meth.oid_len) || !CBB_add_asn1(&algorithm, &null, CBS_ASN1_NULL) || !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) || !CBB_add_u8(&key_bitstring, 0 /* padding */) || !RSA_marshal_public_key(&key_bitstring, key->pkey.rsa) || !CBB_flush(out)) { OPENSSL_PUT_ERROR(EVP, EVP_R_ENCODE_ERROR); return 0; } return 1; }
int CBS_get_asn1_implicit_string(CBS *in, CBS *out, uint8_t **out_storage, unsigned outer_tag, unsigned inner_tag) { assert(!(outer_tag & CBS_ASN1_CONSTRUCTED)); assert(!(inner_tag & CBS_ASN1_CONSTRUCTED)); assert(is_string_type(inner_tag)); if (CBS_peek_asn1_tag(in, outer_tag)) { /* Normal implicitly-tagged string. */ *out_storage = NULL; return CBS_get_asn1(in, out, outer_tag); } /* Otherwise, try to parse an implicitly-tagged constructed string. * |CBS_asn1_ber_to_der| is assumed to have run, so only allow one level deep * of nesting. */ CBB result; CBS child; if (!CBB_init(&result, CBS_len(in)) || !CBS_get_asn1(in, &child, outer_tag | CBS_ASN1_CONSTRUCTED)) { goto err; } while (CBS_len(&child) > 0) { CBS chunk; if (!CBS_get_asn1(&child, &chunk, inner_tag) || !CBB_add_bytes(&result, CBS_data(&chunk), CBS_len(&chunk))) { goto err; } } uint8_t *data; size_t len; if (!CBB_finish(&result, &data, &len)) { goto err; } CBS_init(out, data, len); *out_storage = data; return 1; err: CBB_cleanup(&result); return 0; }
int tls13_prepare_finished(SSL *ssl) { size_t verify_data_len; uint8_t verify_data[EVP_MAX_MD_SIZE]; if (!tls13_finished_mac(ssl, verify_data, &verify_data_len, ssl->server)) { ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED); return 0; } CBB cbb, body; if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_FINISHED) || !CBB_add_bytes(&body, verify_data, verify_data_len) || !ssl->method->finish_message(ssl, &cbb)) { CBB_cleanup(&cbb); return 0; } return 1; }
static int eckey_pub_encode(CBB *out, const EVP_PKEY *key) { const EC_KEY *ec_key = key->pkey.ec; const EC_GROUP *group = EC_KEY_get0_group(ec_key); const EC_POINT *public_key = EC_KEY_get0_public_key(ec_key); // See RFC 5480, section 2. CBB spki, algorithm, oid, key_bitstring; if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) || !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) || !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) || !CBB_add_bytes(&oid, ec_asn1_meth.oid, ec_asn1_meth.oid_len) || !EC_KEY_marshal_curve_name(&algorithm, group) || !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) || !CBB_add_u8(&key_bitstring, 0 /* padding */) || !EC_POINT_point2cbb(&key_bitstring, group, public_key, POINT_CONVERSION_UNCOMPRESSED, NULL) || !CBB_flush(out)) { OPENSSL_PUT_ERROR(EVP, EVP_R_ENCODE_ERROR); return 0; } return 1; }
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; }
/* cbs_convert_ber reads BER data from |in| and writes DER data to |out|. If * |string_tag| is non-zero, then all elements must match |string_tag| up to the * constructed bit and primitive element bodies are written to |out| without * element headers. This is used when concatenating the fragments of a * constructed string. If |looking_for_eoc| is set then any EOC elements found * will cause the function to return after consuming it. It returns one on * success and zero on error. */ static int cbs_convert_ber(CBS *in, CBB *out, unsigned string_tag, char looking_for_eoc, unsigned depth) { assert(!(string_tag & CBS_ASN1_CONSTRUCTED)); if (depth > kMaxDepth) { return 0; } while (CBS_len(in) > 0) { CBS contents; unsigned tag, child_string_tag = string_tag; size_t header_len; CBB *out_contents, out_contents_storage; if (!CBS_get_any_ber_asn1_element(in, &contents, &tag, &header_len)) { return 0; } if (is_eoc(header_len, &contents)) { return looking_for_eoc; } if (string_tag != 0) { /* This is part of a constructed string. All elements must match * |string_tag| up to the constructed bit and get appended to |out| * without a child element. */ if ((tag & ~CBS_ASN1_CONSTRUCTED) != string_tag) { return 0; } out_contents = out; } else { unsigned out_tag = tag; if ((tag & CBS_ASN1_CONSTRUCTED) && is_string_type(tag)) { /* If a constructed string, clear the constructed bit and inform * children to concatenate bodies. */ out_tag &= ~CBS_ASN1_CONSTRUCTED; child_string_tag = out_tag; } if (!CBB_add_asn1(out, &out_contents_storage, out_tag)) { return 0; } out_contents = &out_contents_storage; } if (CBS_len(&contents) == header_len && header_len > 0 && CBS_data(&contents)[header_len - 1] == 0x80) { /* This is an indefinite length element. */ if (!cbs_convert_ber(in, out_contents, child_string_tag, 1 /* looking for eoc */, depth + 1) || !CBB_flush(out)) { return 0; } continue; } if (!CBS_skip(&contents, header_len)) { return 0; } if (tag & CBS_ASN1_CONSTRUCTED) { /* Recurse into children. */ if (!cbs_convert_ber(&contents, out_contents, child_string_tag, 0 /* not looking for eoc */, depth + 1)) { return 0; } } else { /* Copy primitive contents as-is. */ if (!CBB_add_bytes(out_contents, CBS_data(&contents), CBS_len(&contents))) { return 0; } } if (!CBB_flush(out)) { return 0; } } return looking_for_eoc == 0; }
int tls13_prepare_certificate(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; CBB cbb, body, certificate_list; if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) || /* The request context is always empty in the handshake. */ !CBB_add_u8(&body, 0) || !CBB_add_u24_length_prefixed(&body, &certificate_list)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } if (!ssl_has_certificate(ssl)) { if (!ssl_complete_message(ssl, &cbb)) { goto err; } return 1; } CERT *cert = ssl->cert; CBB leaf, extensions; if (!CBB_add_u24_length_prefixed(&certificate_list, &leaf) || !ssl_add_cert_to_cbb(&leaf, cert->x509_leaf) || !CBB_add_u16_length_prefixed(&certificate_list, &extensions)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } if (hs->scts_requested && ssl->ctx->signed_cert_timestamp_list_length != 0) { CBB contents; if (!CBB_add_u16(&extensions, TLSEXT_TYPE_certificate_timestamp) || !CBB_add_u16_length_prefixed(&extensions, &contents) || !CBB_add_bytes(&contents, ssl->ctx->signed_cert_timestamp_list, ssl->ctx->signed_cert_timestamp_list_length) || !CBB_flush(&extensions)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } } if (hs->ocsp_stapling_requested && ssl->ocsp_response != NULL) { CBB contents, ocsp_response; if (!CBB_add_u16(&extensions, TLSEXT_TYPE_status_request) || !CBB_add_u16_length_prefixed(&extensions, &contents) || !CBB_add_u8(&contents, TLSEXT_STATUSTYPE_ocsp) || !CBB_add_u24_length_prefixed(&contents, &ocsp_response) || !CBB_add_bytes(&ocsp_response, CRYPTO_BUFFER_data(ssl->ocsp_response), CRYPTO_BUFFER_len(ssl->ocsp_response)) || !CBB_flush(&extensions)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } } for (size_t i = 0; i < sk_X509_num(cert->x509_chain); i++) { CBB child; if (!CBB_add_u24_length_prefixed(&certificate_list, &child) || !ssl_add_cert_to_cbb(&child, sk_X509_value(cert->x509_chain, i)) || !CBB_add_u16(&certificate_list, 0 /* no extensions */)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } } if (!ssl_complete_message(ssl, &cbb)) { goto err; } return 1; err: CBB_cleanup(&cbb); return 0; }
static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data, size_t *out_len, int for_ticket) { CBB cbb, session, child, child2; if (in == NULL || in->cipher == NULL) { return 0; } CBB_zero(&cbb); if (!CBB_init(&cbb, 0) || !CBB_add_asn1(&cbb, &session, CBS_ASN1_SEQUENCE) || !CBB_add_asn1_uint64(&session, kVersion) || !CBB_add_asn1_uint64(&session, in->ssl_version) || !CBB_add_asn1(&session, &child, CBS_ASN1_OCTETSTRING) || !CBB_add_u16(&child, (uint16_t)(in->cipher->id & 0xffff)) || !CBB_add_asn1(&session, &child, CBS_ASN1_OCTETSTRING) || /* The session ID is irrelevant for a session ticket. */ !CBB_add_bytes(&child, in->session_id, for_ticket ? 0 : in->session_id_length) || !CBB_add_asn1(&session, &child, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child, in->master_key, in->master_key_length) || !CBB_add_asn1(&session, &child, kTimeTag) || !CBB_add_asn1_uint64(&child, in->time) || !CBB_add_asn1(&session, &child, kTimeoutTag) || !CBB_add_asn1_uint64(&child, in->timeout)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } /* The peer certificate is only serialized if the SHA-256 isn't * serialized instead. */ if (sk_CRYPTO_BUFFER_num(in->certs) > 0 && !in->peer_sha256_valid) { const CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(in->certs, 0); if (!CBB_add_asn1(&session, &child, kPeerTag) || !CBB_add_bytes(&child, CRYPTO_BUFFER_data(buffer), CRYPTO_BUFFER_len(buffer))) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } /* Although it is OPTIONAL and usually empty, OpenSSL has * historically always encoded the sid_ctx. */ if (!CBB_add_asn1(&session, &child, kSessionIDContextTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, in->sid_ctx, in->sid_ctx_length)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } if (in->verify_result != X509_V_OK) { if (!CBB_add_asn1(&session, &child, kVerifyResultTag) || !CBB_add_asn1_uint64(&child, in->verify_result)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } if (in->tlsext_hostname) { if (!CBB_add_asn1(&session, &child, kHostNameTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, (const uint8_t *)in->tlsext_hostname, strlen(in->tlsext_hostname))) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } if (in->psk_identity) { if (!CBB_add_asn1(&session, &child, kPSKIdentityTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, (const uint8_t *)in->psk_identity, strlen(in->psk_identity))) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } if (in->tlsext_tick_lifetime_hint > 0) { if (!CBB_add_asn1(&session, &child, kTicketLifetimeHintTag) || !CBB_add_asn1_uint64(&child, in->tlsext_tick_lifetime_hint)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } if (in->tlsext_tick && !for_ticket) { if (!CBB_add_asn1(&session, &child, kTicketTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, in->tlsext_tick, in->tlsext_ticklen)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } if (in->peer_sha256_valid) { if (!CBB_add_asn1(&session, &child, kPeerSHA256Tag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, in->peer_sha256, sizeof(in->peer_sha256))) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } if (in->original_handshake_hash_len > 0) { if (!CBB_add_asn1(&session, &child, kOriginalHandshakeHashTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, in->original_handshake_hash, in->original_handshake_hash_len)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } if (in->tlsext_signed_cert_timestamp_list_length > 0) { if (!CBB_add_asn1(&session, &child, kSignedCertTimestampListTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, in->tlsext_signed_cert_timestamp_list, in->tlsext_signed_cert_timestamp_list_length)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } if (in->ocsp_response_length > 0) { if (!CBB_add_asn1(&session, &child, kOCSPResponseTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, in->ocsp_response, in->ocsp_response_length)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } if (in->extended_master_secret) { if (!CBB_add_asn1(&session, &child, kExtendedMasterSecretTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_BOOLEAN) || !CBB_add_u8(&child2, 0xff)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } if (in->group_id > 0 && (!CBB_add_asn1(&session, &child, kGroupIDTag) || !CBB_add_asn1_uint64(&child, in->group_id))) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } /* The certificate chain is only serialized if the leaf's SHA-256 isn't * serialized instead. */ if (in->certs != NULL && !in->peer_sha256_valid && sk_CRYPTO_BUFFER_num(in->certs) >= 2) { if (!CBB_add_asn1(&session, &child, kCertChainTag)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(in->certs); i++) { const CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(in->certs, i); if (!CBB_add_bytes(&child, CRYPTO_BUFFER_data(buffer), CRYPTO_BUFFER_len(buffer))) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } } if (in->ticket_age_add_valid) { if (!CBB_add_asn1(&session, &child, kTicketAgeAddTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_u32(&child2, in->ticket_age_add)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } if (!in->is_server) { if (!CBB_add_asn1(&session, &child, kIsServerTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_BOOLEAN) || !CBB_add_u8(&child2, 0x00)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } if (in->peer_signature_algorithm != 0 && (!CBB_add_asn1(&session, &child, kPeerSignatureAlgorithmTag) || !CBB_add_asn1_uint64(&child, in->peer_signature_algorithm))) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } if (in->ticket_max_early_data != 0 && (!CBB_add_asn1(&session, &child, kTicketMaxEarlyDataTag) || !CBB_add_asn1_uint64(&child, in->ticket_max_early_data))) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } if (in->timeout != in->auth_timeout && (!CBB_add_asn1(&session, &child, kAuthTimeoutTag) || !CBB_add_asn1_uint64(&child, in->auth_timeout))) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } if (in->early_alpn) { if (!CBB_add_asn1(&session, &child, kEarlyALPNTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, (const uint8_t *)in->early_alpn, in->early_alpn_len)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } if (!CBB_finish(&cbb, out_data, out_len)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } return 1; err: CBB_cleanup(&cbb); return 0; }
int i2d_SSL_SESSION(SSL_SESSION *s, unsigned char **pp) { CBB cbb, session, cipher_suite, session_id, master_key, time, timeout; CBB peer_cert, sidctx, verify_result, hostname, lifetime, ticket; CBB value; unsigned char *data = NULL, *peer_cert_bytes = NULL; size_t data_len = 0; int len, rv = -1; uint16_t cid; if (s == NULL) return (0); if (s->cipher == NULL && s->cipher_id == 0) return (0); if (!CBB_init(&cbb, 0)) goto err; if (!CBB_add_asn1(&cbb, &session, CBS_ASN1_SEQUENCE)) goto err; /* Session ASN1 version. */ if (!CBB_add_asn1_uint64(&session, SSL_SESSION_ASN1_VERSION)) goto err; /* TLS/SSL protocol version. */ if (s->ssl_version < 0) goto err; if (!CBB_add_asn1_uint64(&session, s->ssl_version)) goto err; /* Cipher suite ID. */ /* XXX - require cipher to be non-NULL or always/only use cipher_id. */ cid = (uint16_t)(s->cipher_id & 0xffff); if (s->cipher != NULL) cid = ssl3_cipher_get_value(s->cipher); if (!CBB_add_asn1(&session, &cipher_suite, CBS_ASN1_OCTETSTRING)) goto err; if (!CBB_add_u16(&cipher_suite, cid)) goto err; /* Session ID. */ if (!CBB_add_asn1(&session, &session_id, CBS_ASN1_OCTETSTRING)) goto err; if (!CBB_add_bytes(&session_id, s->session_id, s->session_id_length)) goto err; /* Master key. */ if (!CBB_add_asn1(&session, &master_key, CBS_ASN1_OCTETSTRING)) goto err; if (!CBB_add_bytes(&master_key, s->master_key, s->master_key_length)) goto err; /* Time [1]. */ if (s->time != 0) { if (s->time < 0) goto err; if (!CBB_add_asn1(&session, &time, SSLASN1_TIME_TAG)) goto err; if (!CBB_add_asn1_uint64(&time, s->time)) goto err; } /* Timeout [2]. */ if (s->timeout != 0) { if (s->timeout < 0) goto err; if (!CBB_add_asn1(&session, &timeout, SSLASN1_TIMEOUT_TAG)) goto err; if (!CBB_add_asn1_uint64(&timeout, s->timeout)) goto err; } /* Peer certificate [3]. */ if (s->peer != NULL) { if ((len = i2d_X509(s->peer, &peer_cert_bytes)) <= 0) goto err; if (!CBB_add_asn1(&session, &peer_cert, SSLASN1_PEER_CERT_TAG)) goto err; if (!CBB_add_bytes(&peer_cert, peer_cert_bytes, len)) goto err; } /* Session ID context [4]. */ /* XXX - Actually handle this as optional? */ if (!CBB_add_asn1(&session, &sidctx, SSLASN1_SESSION_ID_CTX_TAG)) goto err; if (!CBB_add_asn1(&sidctx, &value, CBS_ASN1_OCTETSTRING)) goto err; if (!CBB_add_bytes(&value, s->sid_ctx, s->sid_ctx_length)) goto err; /* Verify result [5]. */ if (s->verify_result != X509_V_OK) { if (s->verify_result < 0) goto err; if (!CBB_add_asn1(&session, &verify_result, SSLASN1_VERIFY_RESULT_TAG)) goto err; if (!CBB_add_asn1_uint64(&verify_result, s->verify_result)) goto err; } /* Hostname [6]. */ if (s->tlsext_hostname != NULL) { if (!CBB_add_asn1(&session, &hostname, SSLASN1_HOSTNAME_TAG)) goto err; if (!CBB_add_asn1(&hostname, &value, CBS_ASN1_OCTETSTRING)) goto err; if (!CBB_add_bytes(&value, (const uint8_t *)s->tlsext_hostname, strlen(s->tlsext_hostname))) goto err; } /* PSK identity hint [7]. */ /* PSK identity [8]. */ /* Ticket lifetime hint [9]. */ if (s->tlsext_tick_lifetime_hint > 0) { if (!CBB_add_asn1(&session, &lifetime, SSLASN1_LIFETIME_TAG)) goto err; if (!CBB_add_asn1_uint64(&lifetime, s->tlsext_tick_lifetime_hint)) goto err; } /* Ticket [10]. */ if (s->tlsext_tick) { if (!CBB_add_asn1(&session, &ticket, SSLASN1_TICKET_TAG)) goto err; if (!CBB_add_asn1(&ticket, &value, CBS_ASN1_OCTETSTRING)) goto err; if (!CBB_add_bytes(&value, s->tlsext_tick, s->tlsext_ticklen)) goto err; } /* Compression method [11]. */ /* SRP username [12]. */ if (!CBB_finish(&cbb, &data, &data_len)) goto err; if (data_len > INT_MAX) goto err; if (pp != NULL) { if (*pp == NULL) { *pp = data; data = NULL; } else { memcpy(*pp, data, data_len); *pp += data_len; } } rv = (int)data_len; err: CBB_cleanup(&cbb); freezero(data, data_len); free(peer_cert_bytes); return rv; }
static int ssl_encrypt_ticket_with_cipher_ctx(SSL *ssl, CBB *out, const uint8_t *session_buf, size_t session_len) { int ret = 0; EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX_init(&ctx); HMAC_CTX hctx; HMAC_CTX_init(&hctx); /* If the session is too long, emit a dummy value rather than abort the * connection. */ static const size_t kMaxTicketOverhead = 16 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE; if (session_len > 0xffff - kMaxTicketOverhead) { static const char kTicketPlaceholder[] = "TICKET TOO LARGE"; if (CBB_add_bytes(out, (const uint8_t *)kTicketPlaceholder, strlen(kTicketPlaceholder))) { ret = 1; } goto err; } /* Initialize HMAC and cipher contexts. If callback present it does all the * work otherwise use generated values from parent ctx. */ SSL_CTX *tctx = ssl->session_ctx; uint8_t iv[EVP_MAX_IV_LENGTH]; uint8_t key_name[16]; if (tctx->tlsext_ticket_key_cb != NULL) { if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, &ctx, &hctx, 1 /* encrypt */) < 0) { goto err; } } else { if (!RAND_bytes(iv, 16) || !EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, tctx->tlsext_tick_aes_key, iv) || !HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16, tlsext_tick_md(), NULL)) { goto err; } OPENSSL_memcpy(key_name, tctx->tlsext_tick_key_name, 16); } uint8_t *ptr; if (!CBB_add_bytes(out, key_name, 16) || !CBB_add_bytes(out, iv, EVP_CIPHER_CTX_iv_length(&ctx)) || !CBB_reserve(out, &ptr, session_len + EVP_MAX_BLOCK_LENGTH)) { goto err; } size_t total = 0; #if defined(BORINGSSL_UNSAFE_FUZZER_MODE) OPENSSL_memcpy(ptr, session_buf, session_len); total = session_len; #else int len; if (!EVP_EncryptUpdate(&ctx, ptr + total, &len, session_buf, session_len)) { goto err; } total += len; if (!EVP_EncryptFinal_ex(&ctx, ptr + total, &len)) { goto err; } total += len; #endif if (!CBB_did_write(out, total)) { goto err; } unsigned hlen; if (!HMAC_Update(&hctx, CBB_data(out), CBB_len(out)) || !CBB_reserve(out, &ptr, EVP_MAX_MD_SIZE) || !HMAC_Final(&hctx, ptr, &hlen) || !CBB_did_write(out, hlen)) { goto err; } ret = 1; err: EVP_CIPHER_CTX_cleanup(&ctx); HMAC_CTX_cleanup(&hctx); return ret; }
/* cbs_convert_ber reads BER data from |in| and writes DER data to |out|. If * |squash_header| is set then the top-level of elements from |in| will not * have their headers written. This is used when concatenating the fragments of * an indefinite length, primitive value. If |looking_for_eoc| is set then any * EOC elements found will cause the function to return after consuming it. * It returns one on success and zero on error. */ static int cbs_convert_ber(CBS *in, CBB *out, char squash_header, char looking_for_eoc, unsigned depth) { if (depth > kMaxDepth) { return 0; } while (CBS_len(in) > 0) { CBS contents; unsigned tag; size_t header_len; CBB *out_contents, out_contents_storage; if (!CBS_get_any_ber_asn1_element(in, &contents, &tag, &header_len)) { return 0; } out_contents = out; if (CBS_len(&contents) == header_len) { if (is_eoc(header_len, &contents)) { return looking_for_eoc; } if (header_len > 0 && CBS_data(&contents)[header_len - 1] == 0x80) { /* This is an indefinite length element. If it's a SEQUENCE or SET then * we just need to write the out the contents as normal, but with a * concrete length prefix. * * If it's a something else then the contents will be a series of BER * elements of the same type which need to be concatenated. */ const char context_specific = (tag & 0xc0) == 0x80; char squash_child_headers = is_primitive_type(tag); /* This is a hack, but it sufficies to handle NSS's output. If we find * an indefinite length, context-specific tag with a definite, primitive * tag inside it, then we assume that the context-specific tag is * implicit and the tags within are fragments of a primitive type that * need to be concatenated. */ if (context_specific && (tag & CBS_ASN1_CONSTRUCTED)) { CBS in_copy, inner_contents; unsigned inner_tag; size_t inner_header_len; CBS_init(&in_copy, CBS_data(in), CBS_len(in)); if (!CBS_get_any_ber_asn1_element(&in_copy, &inner_contents, &inner_tag, &inner_header_len)) { return 0; } if (CBS_len(&inner_contents) > inner_header_len && is_primitive_type(inner_tag)) { squash_child_headers = 1; } } if (!squash_header) { unsigned out_tag = tag; if (squash_child_headers) { out_tag &= ~CBS_ASN1_CONSTRUCTED; } if (!CBB_add_asn1(out, &out_contents_storage, out_tag)) { return 0; } out_contents = &out_contents_storage; } if (!cbs_convert_ber(in, out_contents, squash_child_headers, 1 /* looking for eoc */, depth + 1)) { return 0; } if (out_contents != out && !CBB_flush(out)) { return 0; } continue; } } if (!squash_header) { if (!CBB_add_asn1(out, &out_contents_storage, tag)) { return 0; } out_contents = &out_contents_storage; } if (!CBS_skip(&contents, header_len)) { return 0; } if (tag & CBS_ASN1_CONSTRUCTED) { if (!cbs_convert_ber(&contents, out_contents, 0 /* don't squash header */, 0 /* not looking for eoc */, depth + 1)) { return 0; } } else { if (!CBB_add_bytes(out_contents, CBS_data(&contents), CBS_len(&contents))) { return 0; } } if (out_contents != out && !CBB_flush(out)) { return 0; } } return looking_for_eoc == 0; }
static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; /* Send a ServerHello. */ CBB cbb, body, extensions; if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO) || !CBB_add_u16(&body, ssl->version) || !RAND_bytes(ssl->s3->server_random, sizeof(ssl->s3->server_random)) || !CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) || !CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) || !CBB_add_u16_length_prefixed(&body, &extensions) || !ssl_ext_pre_shared_key_add_serverhello(hs, &extensions) || !ssl_ext_key_share_add_serverhello(hs, &extensions)) { goto err; } if (ssl->s3->short_header) { if (!CBB_add_u16(&extensions, TLSEXT_TYPE_short_header) || !CBB_add_u16(&extensions, 0 /* empty extension */)) { goto err; } } if (!ssl_add_message_cbb(ssl, &cbb)) { goto err; } /* Derive and enable the handshake traffic secrets. */ if (!tls13_derive_handshake_secrets(hs) || !tls13_set_traffic_key(ssl, evp_aead_open, hs->client_handshake_secret, hs->hash_len) || !tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_handshake_secret, hs->hash_len)) { goto err; } /* Send EncryptedExtensions. */ if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_ENCRYPTED_EXTENSIONS) || !ssl_add_serverhello_tlsext(hs, &body) || !ssl_add_message_cbb(ssl, &cbb)) { goto err; } /* Determine whether to request a client certificate. */ hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER); /* CertificateRequest may only be sent in non-resumption handshakes. */ if (ssl->s3->session_reused) { hs->cert_request = 0; } /* Send a CertificateRequest, if necessary. */ if (hs->cert_request) { CBB sigalgs_cbb; if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE_REQUEST) || !CBB_add_u8(&body, 0 /* no certificate_request_context. */)) { goto err; } const uint16_t *sigalgs; size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs); if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb)) { goto err; } for (size_t i = 0; i < num_sigalgs; i++) { if (!CBB_add_u16(&sigalgs_cbb, sigalgs[i])) { goto err; } } if (!ssl_add_client_CA_list(ssl, &body) || !CBB_add_u16(&body, 0 /* empty certificate_extensions. */) || !ssl_add_message_cbb(ssl, &cbb)) { goto err; } } /* Send the server Certificate message, if necessary. */ if (!ssl->s3->session_reused) { if (!ssl_has_certificate(ssl)) { OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET); goto err; } if (!tls13_add_certificate(hs)) { goto err; } hs->tls13_state = state_send_server_certificate_verify; return ssl_hs_ok; } hs->tls13_state = state_send_server_finished; return ssl_hs_ok; err: CBB_cleanup(&cbb); return ssl_hs_error; }
static int test_cbb_asn1(void) { static const uint8_t kExpected[] = {0x30, 3, 1, 2, 3}; uint8_t *buf, *test_data; size_t buf_len; CBB cbb, contents, inner_contents; if (!CBB_init(&cbb, 0) || !CBB_add_asn1(&cbb, &contents, 0x30) || !CBB_add_bytes(&contents, (const uint8_t*) "\x01\x02\x03", 3) || !CBB_finish(&cbb, &buf, &buf_len)) { return 0; } if (buf_len != sizeof(kExpected) || memcmp(buf, kExpected, buf_len) != 0) { return 0; } free(buf); test_data = malloc(100000); memset(test_data, 0x42, 100000); if (!CBB_init(&cbb, 0) || !CBB_add_asn1(&cbb, &contents, 0x30) || !CBB_add_bytes(&contents, test_data, 130) || !CBB_finish(&cbb, &buf, &buf_len)) { return 0; } if (buf_len != 3 + 130 || memcmp(buf, "\x30\x81\x82", 3) != 0 || memcmp(buf + 3, test_data, 130) != 0) { return 0; } free(buf); if (!CBB_init(&cbb, 0) || !CBB_add_asn1(&cbb, &contents, 0x30) || !CBB_add_bytes(&contents, test_data, 1000) || !CBB_finish(&cbb, &buf, &buf_len)) { return 0; } if (buf_len != 4 + 1000 || memcmp(buf, "\x30\x82\x03\xe8", 4) != 0 || memcmp(buf + 4, test_data, 1000)) { return 0; } free(buf); if (!CBB_init(&cbb, 0) || !CBB_add_asn1(&cbb, &contents, 0x30) || !CBB_add_asn1(&contents, &inner_contents, 0x30) || !CBB_add_bytes(&inner_contents, test_data, 100000) || !CBB_finish(&cbb, &buf, &buf_len)) { return 0; } if (buf_len != 5 + 5 + 100000 || memcmp(buf, "\x30\x83\x01\x86\xa5\x30\x83\x01\x86\xa0", 10) != 0 || memcmp(buf + 10, test_data, 100000)) { return 0; } free(buf); free(test_data); return 1; }