/* * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include #include "internal/ssl3_cbc.h" #include "../../ssl_local.h" #include "../record_local.h" #include "recmethod_local.h" static int ssl3_set_crypto_state(OSSL_RECORD_LAYER *rl, int level, unsigned char *key, size_t keylen, unsigned char *iv, size_t ivlen, unsigned char *mackey, size_t mackeylen, const EVP_CIPHER *ciph, size_t taglen, int mactype, const EVP_MD *md, COMP_METHOD *comp) { EVP_CIPHER_CTX *ciph_ctx; int enc = (rl->direction == OSSL_RECORD_DIRECTION_WRITE) ? 1 : 0; if (md == NULL) { ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); return OSSL_RECORD_RETURN_FATAL; } if ((rl->enc_ctx = EVP_CIPHER_CTX_new()) == NULL) { ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); return OSSL_RECORD_RETURN_FATAL; } ciph_ctx = rl->enc_ctx; rl->md_ctx = EVP_MD_CTX_new(); if (rl->md_ctx == NULL) { ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); return OSSL_RECORD_RETURN_FATAL; } if ((md != NULL && EVP_DigestInit_ex(rl->md_ctx, md, NULL) <= 0)) { ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); return OSSL_RECORD_RETURN_FATAL; } #ifndef OPENSSL_NO_COMP if (comp != NULL) { rl->compctx = COMP_CTX_new(comp); if (rl->compctx == NULL) { ERR_raise(ERR_LIB_SSL, SSL_R_COMPRESSION_LIBRARY_ERROR); return OSSL_RECORD_RETURN_FATAL; } } #endif if (!EVP_CipherInit_ex(ciph_ctx, ciph, NULL, key, iv, enc)) { ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); return OSSL_RECORD_RETURN_FATAL; } /* * The cipher we actually ended up using in the EVP_CIPHER_CTX may be * different to that in ciph if we have an ENGINE in use */ if (EVP_CIPHER_get0_provider(EVP_CIPHER_CTX_get0_cipher(ciph_ctx)) != NULL && !ossl_set_tls_provider_parameters(rl, ciph_ctx, ciph, md)) { /* ERR_raise already called */ return OSSL_RECORD_RETURN_FATAL; } if (mackeylen > sizeof(rl->mac_secret)) { ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); return OSSL_RECORD_RETURN_FATAL; } memcpy(rl->mac_secret, mackey, mackeylen); return OSSL_RECORD_RETURN_SUCCESS; } /* * ssl3_cipher encrypts/decrypts |n_recs| records in |inrecs|. Calls RLAYERfatal * on internal error, but not otherwise. It is the responsibility of the caller * to report a bad_record_mac * * Returns: * 0: if the record is publicly invalid, or an internal error * 1: Success or Mac-then-encrypt decryption failed (MAC will be randomised) */ static int ssl3_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *inrecs, size_t n_recs, int sending, SSL_MAC_BUF *mac, size_t macsize) { TLS_RL_RECORD *rec; EVP_CIPHER_CTX *ds; size_t l, i; size_t bs; const EVP_CIPHER *enc; int provided; rec = inrecs; /* * We shouldn't ever be called with more than one record in the SSLv3 case */ if (n_recs != 1) return 0; ds = rl->enc_ctx; if (ds == NULL || (enc = EVP_CIPHER_CTX_get0_cipher(ds)) == NULL) return 0; provided = (EVP_CIPHER_get0_provider(enc) != NULL); l = rec->length; bs = EVP_CIPHER_CTX_get_block_size(ds); if (bs == 0) return 0; /* COMPRESS */ if ((bs != 1) && sending && !provided) { /* * We only do this for legacy ciphers. Provided ciphers add the * padding on the provider side. */ i = bs - (l % bs); /* we need to add 'i-1' padding bytes */ l += i; /* * the last of these zero bytes will be overwritten with the * padding length. */ memset(&rec->input[rec->length], 0, i); rec->length += i; rec->input[l - 1] = (unsigned char)(i - 1); } if (!sending) { if (l == 0 || l % bs != 0) { /* Publicly invalid */ return 0; } /* otherwise, rec->length >= bs */ } if (provided) { int outlen; if (!EVP_CipherUpdate(ds, rec->data, &outlen, rec->input, (unsigned int)l)) return 0; rec->length = outlen; if (!sending && mac != NULL) { /* Now get a pointer to the MAC */ OSSL_PARAM params[2], *p = params; /* Get the MAC */ mac->alloced = 0; *p++ = OSSL_PARAM_construct_octet_ptr(OSSL_CIPHER_PARAM_TLS_MAC, (void **)&mac->mac, macsize); *p = OSSL_PARAM_construct_end(); if (!EVP_CIPHER_CTX_get_params(ds, params)) { /* Shouldn't normally happen */ RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } } } else { if (EVP_Cipher(ds, rec->data, rec->input, (unsigned int)l) < 1) { /* Shouldn't happen */ RLAYERfatal(rl, SSL_AD_BAD_RECORD_MAC, ERR_R_INTERNAL_ERROR); return 0; } if (!sending) return ssl3_cbc_remove_padding_and_mac(&rec->length, rec->orig_len, rec->data, (mac != NULL) ? &mac->mac : NULL, (mac != NULL) ? &mac->alloced : NULL, bs, macsize, rl->libctx); } return 1; } static const unsigned char ssl3_pad_1[48] = { 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 }; static const unsigned char ssl3_pad_2[48] = { 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c }; static int ssl3_mac(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *rec, unsigned char *md, int sending) { unsigned char *mac_sec, *seq = rl->sequence; const EVP_MD_CTX *hash; unsigned char *p, rec_char; size_t md_size; size_t npad; int t; mac_sec = &(rl->mac_secret[0]); hash = rl->md_ctx; t = EVP_MD_CTX_get_size(hash); if (t <= 0) return 0; md_size = t; npad = (48 / md_size) * md_size; if (!sending && EVP_CIPHER_CTX_get_mode(rl->enc_ctx) == EVP_CIPH_CBC_MODE && ssl3_cbc_record_digest_supported(hash)) { #ifdef OPENSSL_NO_DEPRECATED_3_0 return 0; #else /* * This is a CBC-encrypted record. We must avoid leaking any * timing-side channel information about how many blocks of data we * are hashing because that gives an attacker a timing-oracle. */ /*- * npad is, at most, 48 bytes and that's with MD5: * 16 + 48 + 8 (sequence bytes) + 1 + 2 = 75. * * With SHA-1 (the largest hash speced for SSLv3) the hash size * goes up 4, but npad goes down by 8, resulting in a smaller * total size. */ unsigned char header[75]; size_t j = 0; memcpy(header + j, mac_sec, md_size); j += md_size; memcpy(header + j, ssl3_pad_1, npad); j += npad; memcpy(header + j, seq, 8); j += 8; header[j++] = rec->type; header[j++] = (unsigned char)(rec->length >> 8); header[j++] = (unsigned char)(rec->length & 0xff); /* Final param == is SSLv3 */ if (ssl3_cbc_digest_record(EVP_MD_CTX_get0_md(hash), md, &md_size, header, rec->input, rec->length, rec->orig_len, mac_sec, md_size, 1) <= 0) return 0; #endif } else { unsigned int md_size_u; /* Chop the digest off the end :-) */ EVP_MD_CTX *md_ctx = EVP_MD_CTX_new(); if (md_ctx == NULL) return 0; rec_char = rec->type; p = md; s2n(rec->length, p); if (EVP_MD_CTX_copy_ex(md_ctx, hash) <= 0 || EVP_DigestUpdate(md_ctx, mac_sec, md_size) <= 0 || EVP_DigestUpdate(md_ctx, ssl3_pad_1, npad) <= 0 || EVP_DigestUpdate(md_ctx, seq, 8) <= 0 || EVP_DigestUpdate(md_ctx, &rec_char, 1) <= 0 || EVP_DigestUpdate(md_ctx, md, 2) <= 0 || EVP_DigestUpdate(md_ctx, rec->input, rec->length) <= 0 || EVP_DigestFinal_ex(md_ctx, md, NULL) <= 0 || EVP_MD_CTX_copy_ex(md_ctx, hash) <= 0 || EVP_DigestUpdate(md_ctx, mac_sec, md_size) <= 0 || EVP_DigestUpdate(md_ctx, ssl3_pad_2, npad) <= 0 || EVP_DigestUpdate(md_ctx, md, md_size) <= 0 || EVP_DigestFinal_ex(md_ctx, md, &md_size_u) <= 0) { EVP_MD_CTX_free(md_ctx); return 0; } EVP_MD_CTX_free(md_ctx); } if (!tls_increment_sequence_ctr(rl)) return 0; return 1; } struct record_functions_st ssl_3_0_funcs = { ssl3_set_crypto_state, ssl3_cipher, ssl3_mac, tls_default_set_protocol_version, tls_default_read_n, tls_get_more_records, tls_default_validate_record_header, tls_default_post_process_record, tls_get_max_records_default, tls_write_records_default, /* These 2 functions are defined in tls1_meth.c */ tls1_allocate_write_buffers, tls1_initialise_write_packets, NULL, tls_prepare_record_header_default, NULL, tls_prepare_for_encryption_default, tls_post_encryption_processing_default, NULL };