Commit message: added an extra check while checking the padding.
CVE: None
Vulnerability description: None

Project: gnutls
File: gnutls_cipher.c

int _gnutls_ciphertext2compressed(gnutls_session_t session,
				  opaque * compress_data,
				  int compress_size,
				  gnutls_datum_t ciphertext, uint8 type)
{
    uint8 MAC[MAX_HASH_SIZE];
    uint16 c_length;
    uint8 pad;
    int length;
    mac_hd_t td;
    uint16 blocksize;
    int ret, i, pad_failed = 0;
    uint8 major, minor;
    gnutls_protocol_t ver;
    int hash_size =
	_gnutls_hash_get_algo_len(session->security_parameters.
				  read_mac_algorithm);

    ver = gnutls_protocol_get_version(session);
    minor = _gnutls_version_get_minor(ver);
    major = _gnutls_version_get_major(ver);

    blocksize = _gnutls_cipher_get_block_size(session->security_parameters.
					      read_bulk_cipher_algorithm);

    /* initialize MAC 
     */
    td = mac_init(session->security_parameters.read_mac_algorithm,
		  session->connection_state.read_mac_secret.data,
		  session->connection_state.read_mac_secret.size, ver);

    if (td == GNUTLS_MAC_FAILED
	&& session->security_parameters.read_mac_algorithm !=
	GNUTLS_MAC_NULL) {
	gnutls_assert();
	return GNUTLS_E_INTERNAL_ERROR;
    }


    /* actual decryption (inplace)
     */
    switch (_gnutls_cipher_is_block
	    (session->security_parameters.read_bulk_cipher_algorithm)) {
    case CIPHER_STREAM:
	if ((ret = _gnutls_cipher_decrypt(session->connection_state.
					  read_cipher_state,
					  ciphertext.data,
					  ciphertext.size)) < 0) {
	    gnutls_assert();
	    return ret;
	}

	length = ciphertext.size - hash_size;

	break;
    case CIPHER_BLOCK:
	if ((ciphertext.size < blocksize)
	    || (ciphertext.size % blocksize != 0)) {
	    gnutls_assert();
	    return GNUTLS_E_DECRYPTION_FAILED;
	}

	if ((ret = _gnutls_cipher_decrypt(session->connection_state.
					  read_cipher_state,
					  ciphertext.data,
					  ciphertext.size)) < 0) {
	    gnutls_assert();
	    return ret;
	}

	/* ignore the IV in TLS 1.1.
	 */
	if (session->security_parameters.version >= GNUTLS_TLS1_1) {
	    ciphertext.size -= blocksize;
	    ciphertext.data += blocksize;

	    if (ciphertext.size == 0) {
		gnutls_assert();
		return GNUTLS_E_DECRYPTION_FAILED;
	    }
	}

	pad = ciphertext.data[ciphertext.size - 1] + 1;	/* pad */

	length = ciphertext.size - hash_size - pad;

	if (pad > ciphertext.size - hash_size) {
	    gnutls_assert();
	    /* We do not fail here. We check below for the
	     * the pad_failed. If zero means success.
	     */
	    pad_failed = GNUTLS_E_DECRYPTION_FAILED;
	}

	/* Check the pading bytes (TLS 1.x)
	 */
	if (ver >= GNUTLS_TLS1 && pad_failed==0)
	    for (i = 2; i < pad; i++) {
		if (ciphertext.data[ciphertext.size - i] !=
		    ciphertext.data[ciphertext.size - 1])
		    pad_failed = GNUTLS_E_DECRYPTION_FAILED;
	    }
	break;
    default:
	gnutls_assert();
	return GNUTLS_E_INTERNAL_ERROR;
    }

    if (length < 0)
	length = 0;
    c_length = _gnutls_conv_uint16((uint16) length);

    /* Pass the type, version, length and compressed through
     * MAC.
     */
    if (td != GNUTLS_MAC_FAILED) {
	_gnutls_hmac(td,
		     UINT64DATA(session->connection_state.
				read_sequence_number), 8);

	_gnutls_hmac(td, &type, 1);
	if (ver >= GNUTLS_TLS1) {	/* TLS 1.x */
	    _gnutls_hmac(td, &major, 1);
	    _gnutls_hmac(td, &minor, 1);
	}
	_gnutls_hmac(td, &c_length, 2);

	if (length > 0)
	    _gnutls_hmac(td, ciphertext.data, length);

	mac_deinit(td, MAC, ver);
    }

    /* This one was introduced to avoid a timing attack against the TLS
     * 1.0 protocol.
     */
    if (pad_failed != 0)
	return pad_failed;

    /* HMAC was not the same. 
     */
    if (memcmp(MAC, &ciphertext.data[length], hash_size) != 0) {
	gnutls_assert();
	return GNUTLS_E_DECRYPTION_FAILED;
    }

    /* copy the decrypted stuff to compress_data.
     */
    if (compress_size < length) {
	gnutls_assert();
	return GNUTLS_E_INTERNAL_ERROR;
    }
    memcpy(compress_data, ciphertext.data, length);

    return length;
}

Project: gnutls
File: gnutls_cipher.c

int _gnutls_decrypt(gnutls_session_t session, opaque * ciphertext,
		    size_t ciphertext_size, uint8 * data,
		    size_t max_data_size, content_type_t type)
{
    gnutls_datum_t gtxt;
    gnutls_datum_t gcipher;
    int ret;

    if (ciphertext_size == 0)
	return 0;

    gcipher.size = ciphertext_size;
    gcipher.data = ciphertext;

    ret =
	_gnutls_ciphertext2compressed(session, data, max_data_size,
				      gcipher, type);
    if (ret < 0) {
	return ret;
    }

    if (ret == 0 || is_read_comp_null(session) == 0) {
	/* ret == ret */

    } else {
	gnutls_datum_t gcomp;

	/* compression has this malloc overhead.
	 */

	gcomp.data = data;
	gcomp.size = ret;
	ret = _gnutls_m_compressed2plaintext(session, &gtxt, gcomp);
	if (ret < 0) {
	    return ret;
	}

	if (gtxt.size > max_data_size) {
	    gnutls_assert();
	    _gnutls_free_datum(&gtxt);
	    /* This shouldn't have happen and
	     * is a TLS fatal error.
	     */
	    return GNUTLS_E_INTERNAL_ERROR;
	}

	memcpy(data, gtxt.data, gtxt.size);
	ret = gtxt.size;

	_gnutls_free_datum(&gtxt);
    }

    return ret;
}

Project: gnutls
File: gnutls_cipher.c

int _gnutls_encrypt(gnutls_session_t session, const opaque * headers,
		    size_t headers_size, const opaque * data,
		    size_t data_size, opaque * ciphertext,
		    size_t ciphertext_size, content_type_t type,
		    int random_pad)
{
    gnutls_datum_t plain;
    gnutls_datum_t comp;
    int ret;
    int free_comp = 1;

    plain.data = (opaque *) data;
    plain.size = data_size;

    if (plain.size == 0 || is_write_comp_null(session) == 0) {
	comp = plain;
	free_comp = 0;
    } else {
	/* Here comp is allocated and must be 
	 * freed.
	 */
	ret = _gnutls_m_plaintext2compressed(session, &comp, plain);
	if (ret < 0) {
	    gnutls_assert();
	    return ret;
	}
    }

    ret = _gnutls_compressed2ciphertext(session, &ciphertext[headers_size],
					ciphertext_size - headers_size,
					comp, type, random_pad);

    if (free_comp)
	_gnutls_free_datum(&comp);

    if (ret < 0) {
	gnutls_assert();
	return ret;
    }


    /* copy the headers */
    memcpy(ciphertext, headers, headers_size);
    _gnutls_write_uint16(ret, &ciphertext[3]);

    return ret + headers_size;
}

Project: gnutls
File: gnutls_cipher.c

int _gnutls_compressed2ciphertext(gnutls_session_t session,
				  opaque * cipher_data, int cipher_size,
				  gnutls_datum_t compressed,
				  content_type_t _type, int random_pad)
{
    uint8 MAC[MAX_HASH_SIZE];
    uint16 c_length;
    uint8 pad;
    int length, ret;
    mac_hd_t td;
    uint8 type = _type;
    uint8 major, minor;
    int hash_size =
	_gnutls_hash_get_algo_len(session->security_parameters.
				  write_mac_algorithm);
    gnutls_protocol_t ver;
    int blocksize =
	_gnutls_cipher_get_block_size(session->security_parameters.
				      write_bulk_cipher_algorithm);
    cipher_type_t block_algo =
	_gnutls_cipher_is_block(session->security_parameters.
				write_bulk_cipher_algorithm);
    opaque *data_ptr;


    ver = gnutls_protocol_get_version(session);
    minor = _gnutls_version_get_minor(ver);
    major = _gnutls_version_get_major(ver);


    /* Initialize MAC */
    td = mac_init(session->security_parameters.write_mac_algorithm,
		  session->connection_state.write_mac_secret.data,
		  session->connection_state.write_mac_secret.size, ver);

    if (td == GNUTLS_MAC_FAILED
	&& session->security_parameters.write_mac_algorithm !=
	GNUTLS_MAC_NULL) {
	gnutls_assert();
	return GNUTLS_E_INTERNAL_ERROR;
    }

    c_length = _gnutls_conv_uint16(compressed.size);

    if (td != GNUTLS_MAC_FAILED) {	/* actually when the algorithm in not the NULL one */
	_gnutls_hmac(td,
		     UINT64DATA(session->connection_state.
				write_sequence_number), 8);

	_gnutls_hmac(td, &type, 1);
	if (ver >= GNUTLS_TLS1) {	/* TLS 1.0 or higher */
	    _gnutls_hmac(td, &major, 1);
	    _gnutls_hmac(td, &minor, 1);
	}
	_gnutls_hmac(td, &c_length, 2);
	_gnutls_hmac(td, compressed.data, compressed.size);
	mac_deinit(td, MAC, ver);
    }


    /* Calculate the encrypted length (padding etc.)
     */
    length =
	calc_enc_length(session, compressed.size, hash_size, &pad,
			random_pad, block_algo, blocksize);
    if (length < 0) {
	gnutls_assert();
	return length;
    }

    /* copy the encrypted data to cipher_data.
     */
    if (cipher_size < length) {
	gnutls_assert();
	return GNUTLS_E_MEMORY_ERROR;
    }

    data_ptr = cipher_data;
    if (block_algo == CIPHER_BLOCK &&
	session->security_parameters.version >= GNUTLS_TLS1_1) {
	/* copy the random IV.
	 */
	if (_gnutls_get_random(data_ptr, blocksize, GNUTLS_WEAK_RANDOM) <
	    0) {
	    gnutls_assert();
	    return GNUTLS_E_MEMORY_ERROR;
	}
	data_ptr += blocksize;
    }

    memcpy(data_ptr, compressed.data, compressed.size);
    data_ptr += compressed.size;

    if (hash_size > 0) {
	memcpy(data_ptr, MAC, hash_size);
	data_ptr += hash_size;
    }
    if (block_algo == CIPHER_BLOCK && pad > 0) {
	memset(data_ptr, pad - 1, pad);
    }


    /* Actual encryption (inplace).
     */
    ret = _gnutls_cipher_encrypt(session->connection_state.
				 write_cipher_state, cipher_data, length);
    if (ret < 0) {
	gnutls_assert();
	return ret;
    }

    return length;
}

Project: gnutls
File: gnutls_cipher.c

inline static int is_read_comp_null(gnutls_session_t session)
{
    if (session->security_parameters.read_compression_algorithm ==
	GNUTLS_COMP_NULL)
	return 0;

    return 1;
}

Project: gnutls
File: gnutls_cipher.c

inline static int is_write_comp_null(gnutls_session_t session)
{
    if (session->security_parameters.write_compression_algorithm ==
	GNUTLS_COMP_NULL)
	return 0;

    return 1;
}

Project: gnutls
File: gnutls_cipher.c

mac_init(gnutls_mac_algorithm_t mac, opaque * secret, int secret_size,
	 int ver)
{
    mac_hd_t td;

    if (mac == GNUTLS_MAC_NULL)
	return GNUTLS_MAC_FAILED;

    if (ver == GNUTLS_SSL3) {	/* SSL 3.0 */
	td = _gnutls_mac_init_ssl3(mac, secret, secret_size);
    } else {			/* TLS 1.x */
	td = _gnutls_hmac_init(mac, secret, secret_size);
    }

    return td;
}

Project: gnutls
File: gnutls_cipher.c

inline static void mac_deinit(mac_hd_t td, opaque * res, int ver)
{
    if (ver == GNUTLS_SSL3) {	/* SSL 3.0 */
	_gnutls_mac_deinit_ssl3(td, res);
    } else {
	_gnutls_hmac_deinit(td, res);
    }
}

Project: gnutls
File: gnutls_cipher.c

calc_enc_length(gnutls_session_t session, int data_size,
		int hash_size, uint8 * pad, int random_pad,
		cipher_type_t block_algo, uint16 blocksize)
{
    uint8 rnd;
    int length;

    *pad = 0;

    switch (block_algo) {
    case CIPHER_STREAM:
	length = data_size + hash_size;

	break;
    case CIPHER_BLOCK:
	if (_gnutls_get_random(&rnd, 1, GNUTLS_WEAK_RANDOM) < 0) {
	    gnutls_assert();
	    return GNUTLS_E_MEMORY_ERROR;
	}

	/* make rnd a multiple of blocksize */
	if (session->security_parameters.version == GNUTLS_SSL3 ||
	    random_pad == 0) {
	    rnd = 0;
	} else {
	    rnd = (rnd / blocksize) * blocksize;
	    /* added to avoid the case of pad calculated 0
	     * seen below for pad calculation.
	     */
	    if (rnd > blocksize)
		rnd -= blocksize;
	}

	length = data_size + hash_size;

	*pad = (uint8) (blocksize - (length % blocksize)) + rnd;

	length += *pad;
	if (session->security_parameters.version >= GNUTLS_TLS1_1)
	    length += blocksize;	/* for the IV */

	break;
    default:
	gnutls_assert();
	return GNUTLS_E_INTERNAL_ERROR;
    }

    return length;
}