mirror of https://github.com/openssl/openssl.git
				
				
				
			
		
			
				
	
	
		
			328 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			328 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright 2022 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 <openssl/evp.h>
 | |
| #include <openssl/core_names.h>
 | |
| #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;
 | |
|     }
 | |
| 
 | |
|     if (EVP_CIPHER_get0_provider(ciph) != 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);
 | |
| 
 | |
|     /* 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
 | |
| };
 |