diff options
Diffstat (limited to 'lib/libssl/tls12_record_layer.c')
-rw-r--r-- | lib/libssl/tls12_record_layer.c | 348 |
1 files changed, 343 insertions, 5 deletions
diff --git a/lib/libssl/tls12_record_layer.c b/lib/libssl/tls12_record_layer.c index 10d0f1194b4..56ff94d95c9 100644 --- a/lib/libssl/tls12_record_layer.c +++ b/lib/libssl/tls12_record_layer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tls12_record_layer.c,v 1.4 2020/09/16 17:15:01 jsing Exp $ */ +/* $OpenBSD: tls12_record_layer.c,v 1.5 2020/10/03 17:35:17 jsing Exp $ */ /* * Copyright (c) 2020 Joel Sing <jsing@openbsd.org> * @@ -25,6 +25,8 @@ struct tls12_record_layer { uint16_t version; int dtls; + uint8_t alert_desc; + uint16_t read_epoch; uint16_t write_epoch; @@ -43,6 +45,9 @@ struct tls12_record_layer { EVP_CIPHER_CTX *write_cipher_ctx; EVP_MD_CTX *write_hash_ctx; + const uint8_t *read_mac_key; + size_t read_mac_key_len; + uint8_t *read_seq_num; uint8_t *write_seq_num; }; @@ -65,6 +70,12 @@ tls12_record_layer_free(struct tls12_record_layer *rl) } void +tls12_record_layer_alert(struct tls12_record_layer *rl, uint8_t *alert_desc) +{ + *alert_desc = rl->alert_desc; +} + +void tls12_record_layer_set_version(struct tls12_record_layer *rl, uint16_t version) { rl->version = version; @@ -111,6 +122,7 @@ void tls12_record_layer_clear_read_state(struct tls12_record_layer *rl) { tls12_record_layer_set_read_state(rl, NULL, NULL, NULL, 0); + tls12_record_layer_set_read_mac_key(rl, NULL, 0); rl->read_seq_num = NULL; } @@ -173,6 +185,16 @@ tls12_record_layer_set_write_cipher_hash(struct tls12_record_layer *rl, return 1; } +int +tls12_record_layer_set_read_mac_key(struct tls12_record_layer *rl, + const uint8_t *mac_key, size_t mac_key_len) +{ + rl->read_mac_key = mac_key; + rl->read_mac_key_len = mac_key_len; + + return 1; +} + static int tls12_record_layer_build_seq_num(struct tls12_record_layer *rl, CBB *cbb, uint16_t epoch, uint8_t *seq_num, size_t seq_num_len) @@ -234,7 +256,7 @@ tls12_record_layer_mac(struct tls12_record_layer *rl, CBB *cbb, { EVP_MD_CTX *mac_ctx = NULL; uint8_t *header = NULL; - size_t header_len; + size_t header_len = 0; size_t mac_len; uint8_t *mac; int ret = 0; @@ -258,6 +280,8 @@ tls12_record_layer_mac(struct tls12_record_layer *rl, CBB *cbb, goto err; if (EVP_DigestSignFinal(mac_ctx, mac, &mac_len) <= 0) goto err; + if (mac_len == 0) + goto err; if (stream_mac) { if (!EVP_MD_CTX_copy(hash_ctx, mac_ctx)) @@ -269,12 +293,67 @@ tls12_record_layer_mac(struct tls12_record_layer *rl, CBB *cbb, err: EVP_MD_CTX_free(mac_ctx); - free(header); + freezero(header, header_len); return ret; } static int +tls12_record_layer_read_mac_cbc(struct tls12_record_layer *rl, CBB *cbb, + uint8_t content_type, const uint8_t *content, size_t content_len, + size_t mac_len, size_t padding_len) +{ + uint8_t *header = NULL; + size_t header_len = 0; + uint8_t *mac = NULL; + size_t out_mac_len = 0; + int ret = 0; + + /* + * Must be constant time to avoid leaking details about CBC padding. + */ + + if (!ssl3_cbc_record_digest_supported(rl->read_hash_ctx)) + goto err; + + if (!tls12_record_layer_pseudo_header(rl, content_type, content_len, + rl->read_epoch, rl->read_seq_num, SSL3_SEQUENCE_SIZE, + &header, &header_len)) + goto err; + + if (!CBB_add_space(cbb, &mac, mac_len)) + goto err; + if (!ssl3_cbc_digest_record(rl->read_hash_ctx, mac, &out_mac_len, header, + content, content_len + mac_len, content_len + mac_len + padding_len, + rl->read_mac_key, rl->read_mac_key_len)) + goto err; + if (mac_len != out_mac_len) + goto err; + + ret = 1; + + err: + freezero(header, header_len); + + return ret; +} + +static int +tls12_record_layer_read_mac(struct tls12_record_layer *rl, CBB *cbb, + uint8_t content_type, const uint8_t *content, size_t content_len) +{ + EVP_CIPHER_CTX *enc = rl->read_cipher_ctx; + size_t out_len; + + if (EVP_CIPHER_CTX_mode(enc) == EVP_CIPH_CBC_MODE) + return 0; + + return tls12_record_layer_mac(rl, cbb, rl->read_hash_ctx, + rl->read_stream_mac, rl->read_epoch, rl->read_seq_num, + SSL3_SEQUENCE_SIZE, content_type, content, content_len, &out_len); +} + +static int tls12_record_layer_write_mac(struct tls12_record_layer *rl, CBB *cbb, uint8_t content_type, const uint8_t *content, size_t content_len, size_t *out_len) @@ -286,7 +365,8 @@ tls12_record_layer_write_mac(struct tls12_record_layer *rl, CBB *cbb, static int tls12_record_layer_aead_concat_nonce(struct tls12_record_layer *rl, - const SSL_AEAD_CTX *aead, uint8_t *seq_num, uint8_t **out, size_t *out_len) + const SSL_AEAD_CTX *aead, const uint8_t *seq_num, + uint8_t **out, size_t *out_len) { CBB cbb; @@ -314,7 +394,8 @@ tls12_record_layer_aead_concat_nonce(struct tls12_record_layer *rl, static int tls12_record_layer_aead_xored_nonce(struct tls12_record_layer *rl, - const SSL_AEAD_CTX *aead, uint8_t *seq_num, uint8_t **out, size_t *out_len) + const SSL_AEAD_CTX *aead, const uint8_t *seq_num, + uint8_t **out, size_t *out_len) { uint8_t *nonce = NULL; size_t nonce_len = 0; @@ -357,6 +438,263 @@ tls12_record_layer_aead_xored_nonce(struct tls12_record_layer *rl, } static int +tls12_record_layer_open_record_plaintext(struct tls12_record_layer *rl, + uint8_t content_type, CBS *fragment, uint8_t **out, size_t *out_len) +{ + if (rl->read_aead_ctx != NULL || rl->read_cipher_ctx != NULL) + return 0; + + /* XXX - decrypt/process in place for now. */ + *out = (uint8_t *)CBS_data(fragment); + *out_len = CBS_len(fragment); + + return 1; +} + +static int +tls12_record_layer_open_record_protected_aead(struct tls12_record_layer *rl, + uint8_t content_type, CBS *fragment, uint8_t **out, size_t *out_len) +{ + const SSL_AEAD_CTX *aead = rl->read_aead_ctx; + uint8_t *header = NULL, *nonce = NULL; + size_t header_len = 0, nonce_len = 0; + uint8_t *plain; + size_t plain_len; + uint16_t epoch = 0; + CBS var_nonce; + int ret = 0; + + /* XXX - move to nonce allocated in record layer, matching TLSv1.3 */ + if (aead->xor_fixed_nonce) { + if (!tls12_record_layer_aead_xored_nonce(rl, aead, + rl->read_seq_num, &nonce, &nonce_len)) + goto err; + } else if (aead->variable_nonce_in_record) { + if (!CBS_get_bytes(fragment, &var_nonce, + aead->variable_nonce_len)) + goto err; + if (!tls12_record_layer_aead_concat_nonce(rl, aead, + CBS_data(&var_nonce), &nonce, &nonce_len)) + goto err; + } else { + if (!tls12_record_layer_aead_concat_nonce(rl, aead, + rl->read_seq_num, &nonce, &nonce_len)) + goto err; + } + + /* XXX EVP_AEAD_max_tag_len vs EVP_AEAD_CTX_tag_len. */ + if (CBS_len(fragment) < aead->tag_len) { + rl->alert_desc = SSL_AD_BAD_RECORD_MAC; + goto err; + } + if (CBS_len(fragment) > SSL3_RT_MAX_ENCRYPTED_LENGTH) { + rl->alert_desc = SSL_AD_RECORD_OVERFLOW; + goto err; + } + + /* XXX - decrypt/process in place for now. */ + plain = (uint8_t *)CBS_data(fragment); + plain_len = CBS_len(fragment) - aead->tag_len; + + if (!tls12_record_layer_pseudo_header(rl, content_type, plain_len, + epoch, rl->read_seq_num, SSL3_SEQUENCE_SIZE, &header, &header_len)) + goto err; + + if (!EVP_AEAD_CTX_open(&aead->ctx, plain, out_len, plain_len, + nonce, nonce_len, CBS_data(fragment), CBS_len(fragment), + header, header_len)) { + rl->alert_desc = SSL_AD_BAD_RECORD_MAC; + goto err; + } + + if (*out_len > SSL3_RT_MAX_PLAIN_LENGTH) { + rl->alert_desc = SSL_AD_RECORD_OVERFLOW; + goto err; + } + + if (*out_len != plain_len) + goto err; + + *out = plain; + + ret = 1; + + err: + freezero(header, header_len); + freezero(nonce, nonce_len); + + return ret; +} + +static int +tls12_record_layer_open_record_protected_cipher(struct tls12_record_layer *rl, + uint8_t content_type, CBS *fragment, uint8_t **out, size_t *out_len) +{ + EVP_CIPHER_CTX *enc = rl->read_cipher_ctx; + SSL3_RECORD_INTERNAL rrec; + int block_size, eiv_len; + uint8_t *mac = NULL; + int mac_len = 0; + uint8_t *out_mac = NULL; + size_t out_mac_len = 0; + uint8_t *plain; + size_t plain_len; + size_t min_len; + CBB cbb_mac; + int ret = 0; + + memset(&cbb_mac, 0, sizeof(cbb_mac)); + + block_size = EVP_CIPHER_CTX_block_size(enc); + if (block_size < 0 || block_size > EVP_MAX_BLOCK_LENGTH) + goto err; + + /* Determine explicit IV length. */ + eiv_len = 0; + if (rl->version != TLS1_VERSION && + EVP_CIPHER_CTX_mode(enc) == EVP_CIPH_CBC_MODE) + eiv_len = EVP_CIPHER_CTX_iv_length(enc); + if (eiv_len < 0 || eiv_len > EVP_MAX_IV_LENGTH) + goto err; + + mac_len = 0; + if (rl->read_hash_ctx != NULL) { + mac_len = EVP_MD_CTX_size(rl->read_hash_ctx); + if (mac_len <= 0 || mac_len > EVP_MAX_MD_SIZE) + goto err; + } + + /* CBC has at least one padding byte. */ + min_len = eiv_len + mac_len; + if (EVP_CIPHER_CTX_mode(enc) == EVP_CIPH_CBC_MODE) + min_len += 1; + + if (CBS_len(fragment) < min_len) { + rl->alert_desc = SSL_AD_BAD_RECORD_MAC; + goto err; + } + if (CBS_len(fragment) > SSL3_RT_MAX_ENCRYPTED_LENGTH) { + rl->alert_desc = SSL_AD_RECORD_OVERFLOW; + goto err; + } + if (CBS_len(fragment) % block_size != 0) { + rl->alert_desc = SSL_AD_BAD_RECORD_MAC; + goto err; + } + + /* XXX - decrypt/process in place for now. */ + plain = (uint8_t *)CBS_data(fragment); + plain_len = CBS_len(fragment); + + if (!EVP_Cipher(enc, plain, CBS_data(fragment), plain_len)) + goto err; + + rrec.data = plain; + rrec.input = plain; + rrec.length = plain_len; + + /* + * We now have to remove padding, extract MAC, calculate MAC + * and compare MAC in constant time. + */ + if (block_size > 1) + ssl3_cbc_remove_padding(&rrec, eiv_len, mac_len); + + if ((mac = calloc(1, mac_len)) == NULL) + goto err; + + if (!CBB_init(&cbb_mac, EVP_MAX_MD_SIZE)) + goto err; + if (EVP_CIPHER_CTX_mode(enc) == EVP_CIPH_CBC_MODE) { + ssl3_cbc_copy_mac(mac, &rrec, mac_len, rrec.length + + rrec.padding_length); + rrec.length -= mac_len; + if (!tls12_record_layer_read_mac_cbc(rl, &cbb_mac, content_type, + rrec.input, rrec.length, mac_len, rrec.padding_length)) + goto err; + } else { + rrec.length -= mac_len; + memcpy(mac, rrec.data + rrec.length, mac_len); + if (!tls12_record_layer_read_mac(rl, &cbb_mac, content_type, + rrec.input, rrec.length)) + goto err; + } + if (!CBB_finish(&cbb_mac, &out_mac, &out_mac_len)) + goto err; + if (mac_len != out_mac_len) + goto err; + + if (timingsafe_memcmp(mac, out_mac, mac_len) != 0) { + rl->alert_desc = SSL_AD_BAD_RECORD_MAC; + goto err; + } + + if (rrec.length > SSL3_RT_MAX_COMPRESSED_LENGTH + mac_len) { + rl->alert_desc = SSL_AD_BAD_RECORD_MAC; + goto err; + } + if (rrec.length > SSL3_RT_MAX_PLAIN_LENGTH) { + rl->alert_desc = SSL_AD_RECORD_OVERFLOW; + goto err; + } + + *out = rrec.data; + *out_len = rrec.length; + + ret = 1; + + err: + CBB_cleanup(&cbb_mac); + freezero(mac, mac_len); + freezero(out_mac, out_mac_len); + + return ret; +} + +int +tls12_record_layer_open_record(struct tls12_record_layer *rl, uint8_t *buf, + size_t buf_len, uint8_t **out, size_t *out_len) +{ + CBS cbs, fragment, seq_no; + uint16_t epoch, version; + uint8_t content_type; + + CBS_init(&cbs, buf, buf_len); + + if (!CBS_get_u8(&cbs, &content_type)) + return 0; + if (!CBS_get_u16(&cbs, &version)) + return 0; + if (rl->dtls) { + if (!CBS_get_u16(&cbs, &epoch)) + return 0; + if (!CBS_get_bytes(&cbs, &seq_no, 6)) + return 0; + } + if (!CBS_get_u16_length_prefixed(&cbs, &fragment)) + return 0; + + if (rl->read_aead_ctx != NULL) { + if (!tls12_record_layer_open_record_protected_aead(rl, + content_type, &fragment, out, out_len)) + return 0; + } else if (rl->read_cipher_ctx != NULL) { + if (!tls12_record_layer_open_record_protected_cipher(rl, + content_type, &fragment, out, out_len)) + return 0; + } else { + if (!tls12_record_layer_open_record_plaintext(rl, + content_type, &fragment, out, out_len)) + return 0; + } + + if (!rl->dtls) + tls1_record_sequence_increment(rl->read_seq_num); + + return 1; +} + +static int tls12_record_layer_seal_record_plaintext(struct tls12_record_layer *rl, uint8_t content_type, const uint8_t *content, size_t content_len, CBB *out) { |