/*
- * Copyright (C) 2000-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2000-2013 Free Software Foundation, Inc.
+ * Copyright (C) 2013 Nikos Mavrogiannopoulos
*
* Author: Nikos Mavrogiannopoulos
*
*
* The GnuTLS is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 3 of
+ * as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
#include "gnutls_kx.h"
#include "gnutls_record.h"
#include "gnutls_constate.h"
+#include "gnutls_mbuffers.h"
#include <gnutls_state.h>
#include <random.h>
-static int compressed_to_ciphertext (gnutls_session_t session,
- uint8_t * cipher_data, int cipher_size,
- gnutls_datum_t *compressed,
- content_type_t _type,
- record_parameters_st * params);
-static int ciphertext_to_compressed (gnutls_session_t session,
- gnutls_datum_t *ciphertext,
- uint8_t * compress_data,
- int compress_size,
- uint8_t type,
- record_parameters_st * params, uint64* sequence);
-
-inline static int
-is_write_comp_null (record_parameters_st * record_params)
+static int compressed_to_ciphertext(gnutls_session_t session,
+ uint8_t * cipher_data, int cipher_size,
+ gnutls_datum_t * compressed,
+ size_t min_pad,
+ content_type_t _type,
+ record_parameters_st * params);
+static int ciphertext_to_compressed(gnutls_session_t session,
+ gnutls_datum_t * ciphertext,
+ gnutls_datum_t * compressed,
+ uint8_t type,
+ record_parameters_st * params,
+ uint64 * sequence);
+
+inline static int is_write_comp_null(record_parameters_st * record_params)
{
- if (record_params->compression_algorithm == GNUTLS_COMP_NULL)
- return 0;
+ if (record_params->compression_algorithm == GNUTLS_COMP_NULL)
+ return 0;
- return 1;
+ return 1;
}
-inline static int
-is_read_comp_null (record_parameters_st * record_params)
+inline static int is_read_comp_null(record_parameters_st * record_params)
{
- if (record_params->compression_algorithm == GNUTLS_COMP_NULL)
- return 0;
+ if (record_params->compression_algorithm == GNUTLS_COMP_NULL)
+ return 0;
- return 1;
+ return 1;
}
/* returns ciphertext which contains the headers too. This also
* calculates the size in the header field.
*
- * If random pad != 0 then the random pad data will be appended.
*/
int
-_gnutls_encrypt (gnutls_session_t session, const uint8_t * headers,
- size_t headers_size, const uint8_t * data,
- size_t data_size, uint8_t * ciphertext,
- size_t ciphertext_size, content_type_t type,
- record_parameters_st * params)
+_gnutls_encrypt(gnutls_session_t session,
+ const uint8_t * data, size_t data_size,
+ size_t min_pad,
+ mbuffer_st * bufel,
+ content_type_t type, record_parameters_st * params)
{
- gnutls_datum_t comp;
- int free_comp = 0;
- int ret;
-
- if (data_size == 0 || is_write_comp_null (params) == 0)
- {
- comp.data = (uint8_t*)data;
- comp.size = data_size;
- }
- else
- {
- /* Here comp is allocated and must be
- * freed.
- */
- free_comp = 1;
-
- comp.size = ciphertext_size - headers_size;
- comp.data = gnutls_malloc(comp.size);
- if (comp.data == NULL)
- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-
- ret = _gnutls_compress( ¶ms->write.compression_state, data, data_size, comp.data, comp.size);
- if (ret < 0)
- {
- gnutls_free(comp.data);
- return gnutls_assert_val(ret);
- }
-
- comp.size = ret;
- }
-
- ret = compressed_to_ciphertext (session, &ciphertext[headers_size],
- ciphertext_size - headers_size,
- &comp, type, params);
-
- if (free_comp)
- gnutls_free(comp.data);
-
- if (ret < 0)
- return gnutls_assert_val(ret);
-
- /* copy the headers */
- memcpy (ciphertext, headers, headers_size);
-
- if(IS_DTLS(session))
- _gnutls_write_uint16 (ret, &ciphertext[11]);
- else
- _gnutls_write_uint16 (ret, &ciphertext[3]);
-
- return ret + headers_size;
+ gnutls_datum_t comp;
+ int free_comp = 0;
+ int ret;
+
+ if (data_size == 0 || is_write_comp_null(params) == 0) {
+ comp.data = (uint8_t *) data;
+ comp.size = data_size;
+ } else {
+ /* Here comp is allocated and must be
+ * freed.
+ */
+ free_comp = 1;
+
+ comp.size = _mbuffer_get_udata_size(bufel);
+ comp.data = gnutls_malloc(comp.size);
+ if (comp.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ ret =
+ _gnutls_compress(¶ms->write.compression_state,
+ data, data_size, comp.data, comp.size,
+ session->internals.priorities.
+ stateless_compression);
+ if (ret < 0) {
+ gnutls_free(comp.data);
+ return gnutls_assert_val(ret);
+ }
+
+ comp.size = ret;
+ }
+
+ ret =
+ compressed_to_ciphertext(session,
+ _mbuffer_get_udata_ptr(bufel),
+ _mbuffer_get_udata_size
+ (bufel), &comp, min_pad, type,
+ params);
+ if (free_comp)
+ gnutls_free(comp.data);
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (IS_DTLS(session))
+ _gnutls_write_uint16(ret,
+ ((uint8_t *)
+ _mbuffer_get_uhead_ptr(bufel)) + 11);
+ else
+ _gnutls_write_uint16(ret,
+ ((uint8_t *)
+ _mbuffer_get_uhead_ptr(bufel)) + 3);
+
+ _mbuffer_set_udata_size(bufel, ret);
+ _mbuffer_set_uhead_size(bufel, 0);
+
+ return _mbuffer_get_udata_size(bufel);
}
/* Decrypts the given data.
* Returns the decrypted data length.
+ *
+ * The output is preallocated with the maximum allowed data size.
*/
int
-_gnutls_decrypt (gnutls_session_t session, uint8_t * ciphertext,
- size_t ciphertext_size, uint8_t * data,
- size_t max_data_size, content_type_t type,
- record_parameters_st * params, uint64 *sequence)
+_gnutls_decrypt(gnutls_session_t session,
+ gnutls_datum_t * ciphertext,
+ gnutls_datum_t * output,
+ content_type_t type,
+ record_parameters_st * params, uint64 * sequence)
{
- gnutls_datum_t gcipher;
- int ret, data_size;
-
- if (ciphertext_size == 0)
- return 0;
-
- gcipher.size = ciphertext_size;
- gcipher.data = ciphertext;
-
- if (is_read_comp_null (params) == 0)
- {
- ret =
- ciphertext_to_compressed (session, &gcipher, data, max_data_size,
- type, params, sequence);
- if (ret < 0)
- return gnutls_assert_val(ret);
-
- return ret;
- }
- else
- {
- uint8_t* tmp_data;
-
- tmp_data = gnutls_malloc(max_data_size);
- if (tmp_data == NULL)
- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-
- ret =
- ciphertext_to_compressed (session, &gcipher, tmp_data, max_data_size,
- type, params, sequence);
- if (ret < 0)
- goto leave;
-
- data_size = ret;
-
- if (ret != 0)
- {
- ret = _gnutls_decompress( ¶ms->read.compression_state, tmp_data, data_size, data, max_data_size);
- if (ret < 0)
- goto leave;
- }
-
-leave:
- gnutls_free(tmp_data);
- return ret;
- }
+ int ret;
+
+ if (ciphertext->size == 0)
+ return 0;
+
+ if (is_read_comp_null(params) == 0) {
+ ret =
+ ciphertext_to_compressed(session, ciphertext,
+ output, type, params,
+ sequence);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return ret;
+ } else {
+ gnutls_datum_t tmp;
+
+ tmp.size = output->size;
+ tmp.data = gnutls_malloc(tmp.size);
+ if (tmp.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ ret =
+ ciphertext_to_compressed(session, ciphertext,
+ &tmp, type, params,
+ sequence);
+ if (ret < 0)
+ goto leave;
+
+ tmp.size = ret;
+
+ if (ret != 0) {
+ ret =
+ _gnutls_decompress(¶ms->read.
+ compression_state, tmp.data,
+ tmp.size, output->data,
+ output->size);
+ if (ret < 0)
+ goto leave;
+ }
+
+ leave:
+ gnutls_free(tmp.data);
+ return ret;
+ }
}
inline static int
-calc_enc_length (gnutls_session_t session, int data_size,
- int hash_size, uint8_t * pad, int random_pad,
- unsigned block_algo, unsigned auth_cipher, uint16_t blocksize)
+calc_enc_length_block(gnutls_session_t session,
+ const version_entry_st * ver,
+ int data_size,
+ int hash_size, uint8_t * pad,
+ unsigned auth_cipher, uint16_t blocksize)
{
- uint8_t rnd;
- unsigned int length;
- int ret;
-
- *pad = 0;
-
- switch (block_algo)
- {
- case CIPHER_STREAM:
- length = data_size + hash_size;
- if (auth_cipher)
- length += AEAD_EXPLICIT_DATA_SIZE;
-
- break;
- case CIPHER_BLOCK:
- ret = _gnutls_rnd (GNUTLS_RND_NONCE, &rnd, 1);
- if (ret < 0)
- return gnutls_assert_val(ret);
-
- /* 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_t) (blocksize - (length % blocksize)) + rnd;
-
- length += *pad;
- if (_gnutls_version_has_explicit_iv
- (session->security_parameters.version))
- length += blocksize; /* for the IV */
-
- break;
- default:
- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
- }
-
- return length;
+ /* pad is the LH pad the user wants us to add. Besides
+ * this LH pad, we only add minimal padding
+ */
+ unsigned int pre_length = data_size + hash_size + *pad;
+ unsigned int length, new_pad;
+
+ new_pad = (uint8_t) (blocksize - (pre_length % blocksize)) + *pad;
+
+ if (new_pad > 255)
+ new_pad -= blocksize;
+ *pad = new_pad;
+
+ length = data_size + hash_size + *pad;
+
+ if (_gnutls_version_has_explicit_iv(ver))
+ length += blocksize; /* for the IV */
+
+ return length;
+}
+
+inline static int
+calc_enc_length_stream(gnutls_session_t session, int data_size,
+ int hash_size, unsigned auth_cipher,
+ unsigned exp_iv_size)
+{
+ unsigned int length;
+
+ length = data_size + hash_size;
+ if (auth_cipher)
+ length += exp_iv_size;
+
+ return length;
}
#define MAX_PREAMBLE_SIZE 16
* and are not to be sent). Returns their size.
*/
static inline int
-make_preamble (uint8_t * uint64_data, uint8_t type, unsigned int length,
- uint8_t ver, uint8_t * preamble)
+make_preamble(uint8_t * uint64_data, uint8_t type, unsigned int length,
+ const version_entry_st * ver, uint8_t * preamble)
{
- uint8_t minor = _gnutls_version_get_minor (ver);
- uint8_t major = _gnutls_version_get_major (ver);
- uint8_t *p = preamble;
- uint16_t c_length;
-
- c_length = _gnutls_conv_uint16 (length);
-
- memcpy (p, uint64_data, 8);
- p += 8;
- *p = type;
- p++;
- if (ver != GNUTLS_SSL3)
- { /* TLS protocols */
- *p = major;
- p++;
- *p = minor;
- p++;
- }
- memcpy (p, &c_length, 2);
- p += 2;
- return p - preamble;
+ uint8_t *p = preamble;
+ uint16_t c_length;
+
+ c_length = _gnutls_conv_uint16(length);
+
+ memcpy(p, uint64_data, 8);
+ p += 8;
+ *p = type;
+ p++;
+ if (ver->id != GNUTLS_SSL3) { /* TLS protocols */
+ *p = ver->major;
+ p++;
+ *p = ver->minor;
+ p++;
+ }
+ memcpy(p, &c_length, 2);
+ p += 2;
+ return p - preamble;
}
/* This is the actual encryption
* return the actual encrypted data length.
*/
static int
-compressed_to_ciphertext (gnutls_session_t session,
- uint8_t * cipher_data, int cipher_size,
- gnutls_datum_t *compressed,
- content_type_t type,
- record_parameters_st * params)
+compressed_to_ciphertext(gnutls_session_t session,
+ uint8_t * cipher_data, int cipher_size,
+ gnutls_datum_t * compressed,
+ size_t min_pad,
+ content_type_t type,
+ record_parameters_st * params)
{
- uint8_t * tag_ptr = NULL;
- uint8_t pad;
- int length, length_to_encrypt, ret;
- uint8_t preamble[MAX_PREAMBLE_SIZE];
- int preamble_size;
- int tag_size = _gnutls_auth_cipher_tag_len (¶ms->write.cipher_state);
- int blocksize = gnutls_cipher_get_block_size (params->cipher_algorithm);
- unsigned block_algo =
- _gnutls_cipher_is_block (params->cipher_algorithm);
- uint8_t *data_ptr;
- int ver = gnutls_protocol_get_version (session);
- int explicit_iv = _gnutls_version_has_explicit_iv (session->security_parameters.version);
- int auth_cipher = _gnutls_auth_cipher_is_aead(¶ms->write.cipher_state);
- int random_pad;
-
- /* We don't use long padding if requested or if we are in DTLS.
- */
- if (session->internals.priorities.no_padding == 0 && (!IS_DTLS(session)))
- random_pad = 1;
- else
- random_pad = 0;
-
- _gnutls_hard_log("ENC[%p]: cipher: %s, MAC: %s, Epoch: %u\n",
- session, gnutls_cipher_get_name(params->cipher_algorithm), gnutls_mac_get_name(params->mac_algorithm),
- (unsigned int)params->epoch);
-
- preamble_size =
- make_preamble (UINT64DATA
- (params->write.sequence_number),
- type, compressed->size, ver, preamble);
-
- /* Calculate the encrypted length (padding etc.)
- */
- length_to_encrypt = length =
- calc_enc_length (session, compressed->size, tag_size, &pad,
- random_pad, block_algo, auth_cipher, blocksize);
- if (length < 0)
- {
- return gnutls_assert_val(length);
- }
-
- /* copy the encrypted data to cipher_data.
- */
- if (cipher_size < length)
- {
- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
- }
-
- data_ptr = cipher_data;
-
- if (explicit_iv)
- {
-
- if (block_algo == CIPHER_BLOCK)
- {
- /* copy the random IV.
- */
- ret = _gnutls_rnd (GNUTLS_RND_NONCE, data_ptr, blocksize);
- if (ret < 0)
- return gnutls_assert_val(ret);
-
- _gnutls_auth_cipher_setiv(¶ms->write.cipher_state, data_ptr, blocksize);
-
- data_ptr += blocksize;
- cipher_data += blocksize;
- length_to_encrypt -= blocksize;
- }
- else if (auth_cipher)
- {
- uint8_t nonce[blocksize];
-
- /* Values in AEAD are pretty fixed in TLS 1.2 for 128-bit block
- */
- if (params->write.IV.data == NULL || params->write.IV.size != AEAD_IMPLICIT_DATA_SIZE)
- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
-
- /* Instead of generating a new nonce on every packet, we use the
- * write.sequence_number (It is a MAY on RFC 5288).
- */
- memcpy(nonce, params->write.IV.data, params->write.IV.size);
- memcpy(&nonce[AEAD_IMPLICIT_DATA_SIZE], UINT64DATA(params->write.sequence_number), 8);
-
- _gnutls_auth_cipher_setiv(¶ms->write.cipher_state, nonce, AEAD_IMPLICIT_DATA_SIZE+AEAD_EXPLICIT_DATA_SIZE);
-
- /* copy the explicit part */
- memcpy(data_ptr, &nonce[AEAD_IMPLICIT_DATA_SIZE], AEAD_EXPLICIT_DATA_SIZE);
-
- data_ptr += AEAD_EXPLICIT_DATA_SIZE;
- cipher_data += AEAD_EXPLICIT_DATA_SIZE;
- /* In AEAD ciphers we don't encrypt the tag
- */
- length_to_encrypt -= AEAD_EXPLICIT_DATA_SIZE + tag_size;
- }
- }
- else
- {
- /* AEAD ciphers have an explicit IV. Shouldn't be used otherwise.
- */
- if (auth_cipher) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
- }
-
- memcpy (data_ptr, compressed->data, compressed->size);
- data_ptr += compressed->size;
-
- if (tag_size > 0)
- {
- tag_ptr = data_ptr;
- data_ptr += tag_size;
- }
- if (block_algo == CIPHER_BLOCK && pad > 0)
- {
- memset (data_ptr, pad - 1, pad);
- }
-
- /* add the authenticate data */
- ret = _gnutls_auth_cipher_add_auth(¶ms->write.cipher_state, preamble, preamble_size);
- if (ret < 0)
- return gnutls_assert_val(ret);
-
- /* Actual encryption (inplace).
- */
- ret =
- _gnutls_auth_cipher_encrypt2_tag (¶ms->write.cipher_state,
- cipher_data, length_to_encrypt,
- cipher_data, cipher_size,
- tag_ptr, tag_size, compressed->size);
- if (ret < 0)
- return gnutls_assert_val(ret);
-
- return length;
+ uint8_t pad;
+ int length, ret;
+ uint8_t preamble[MAX_PREAMBLE_SIZE];
+ int preamble_size;
+ int tag_size =
+ _gnutls_auth_cipher_tag_len(¶ms->write.cipher_state);
+ int blocksize = _gnutls_cipher_get_block_size(params->cipher);
+ unsigned block_algo = _gnutls_cipher_is_block(params->cipher);
+ uint8_t *data_ptr;
+ const version_entry_st *ver = get_version(session);
+ int explicit_iv = _gnutls_version_has_explicit_iv(ver);
+ int auth_cipher =
+ _gnutls_auth_cipher_is_aead(¶ms->write.cipher_state);
+ uint8_t nonce[MAX_CIPHER_BLOCK_SIZE];
+ unsigned iv_size, imp_iv_size, exp_iv_size;
+
+ if (unlikely(ver == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ iv_size = _gnutls_cipher_get_iv_size(params->cipher);
+ imp_iv_size = _gnutls_cipher_get_implicit_iv_size(params->cipher);
+ exp_iv_size = _gnutls_cipher_get_explicit_iv_size(params->cipher);
+
+ _gnutls_hard_log("ENC[%p]: cipher: %s, MAC: %s, Epoch: %u\n",
+ session, _gnutls_cipher_get_name(params->cipher),
+ _gnutls_mac_get_name(params->mac),
+ (unsigned int) params->epoch);
+
+ preamble_size =
+ make_preamble(UINT64DATA
+ (params->write.sequence_number),
+ type, compressed->size, ver, preamble);
+
+ /* Calculate the encrypted length (padding etc.)
+ */
+ if (block_algo == CIPHER_BLOCK) {
+ /* Call _gnutls_rnd() once. Get data used for the IV
+ */
+ ret = _gnutls_rnd(GNUTLS_RND_NONCE, nonce, blocksize);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ pad = min_pad;
+
+ length =
+ calc_enc_length_block(session, ver, compressed->size,
+ tag_size, &pad, auth_cipher,
+ blocksize);
+ } else {
+ pad = 0;
+ length =
+ calc_enc_length_stream(session, compressed->size,
+ tag_size, auth_cipher, exp_iv_size);
+ }
+
+ if (length < 0)
+ return gnutls_assert_val(length);
+
+ /* copy the encrypted data to cipher_data.
+ */
+ if (cipher_size < length)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ data_ptr = cipher_data;
+
+ if (explicit_iv) { /* TLS 1.1 or later */
+
+ if (block_algo == CIPHER_BLOCK) {
+ /* copy the random IV.
+ */
+ memcpy(data_ptr, nonce, blocksize);
+ _gnutls_auth_cipher_setiv(¶ms->write.
+ cipher_state, data_ptr,
+ blocksize);
+
+ data_ptr += blocksize;
+ cipher_data += blocksize;
+ } else if (auth_cipher) {
+ /* Values in AEAD are pretty fixed in TLS 1.2 for 128-bit block
+ */
+ if (params->write.IV.data == NULL
+ || params->write.IV.size !=
+ imp_iv_size)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_INTERNAL_ERROR);
+
+ /* Instead of generating a new nonce on every packet, we use the
+ * write.sequence_number (It is a MAY on RFC 5288).
+ */
+ memcpy(nonce, params->write.IV.data,
+ params->write.IV.size);
+ memcpy(&nonce[imp_iv_size],
+ UINT64DATA(params->write.sequence_number),
+ 8);
+
+ _gnutls_auth_cipher_setiv(¶ms->write.
+ cipher_state, nonce,
+ imp_iv_size +
+ exp_iv_size);
+
+ /* copy the explicit part */
+ memcpy(data_ptr, &nonce[imp_iv_size],
+ exp_iv_size);
+
+ data_ptr += exp_iv_size;
+ cipher_data += exp_iv_size;
+ } else if (iv_size > 0)
+ _gnutls_auth_cipher_setiv(¶ms->write.
+ cipher_state,
+ UINT64DATA(params->write.
+ sequence_number),
+ 8);
+ } else {
+ /* AEAD ciphers have an explicit IV. Shouldn't be used otherwise.
+ */
+ if (auth_cipher)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ else if (block_algo == CIPHER_STREAM && iv_size > 0)
+ _gnutls_auth_cipher_setiv(¶ms->write.
+ cipher_state,
+ UINT64DATA(params->write.
+ sequence_number),
+ 8);
+ }
+
+ _gnutls_auth_cipher_set_mac_nonce(¶ms->write.cipher_state,
+ UINT64DATA(params->write.
+ sequence_number), 8);
+
+ /* add the authenticate data */
+ ret =
+ _gnutls_auth_cipher_add_auth(¶ms->write.cipher_state,
+ preamble, preamble_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* Actual encryption.
+ */
+ ret =
+ _gnutls_auth_cipher_encrypt2_tag(¶ms->write.cipher_state,
+ compressed->data,
+ compressed->size, cipher_data,
+ cipher_size, pad);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return length;
}
+static void dummy_wait(record_parameters_st * params,
+ gnutls_datum_t * plaintext, unsigned pad_failed,
+ unsigned int pad, unsigned total)
+{
+ /* this hack is only needed on CBC ciphers */
+ if (_gnutls_cipher_is_block(params->cipher) == CIPHER_BLOCK) {
+ unsigned len;
+
+ /* force an additional hash compression function evaluation to prevent timing
+ * attacks that distinguish between wrong-mac + correct pad, from wrong-mac + incorrect pad.
+ */
+ if (pad_failed == 0 && pad > 0) {
+ len = _gnutls_mac_block_size(params->mac);
+ if (len > 0) {
+ /* This is really specific to the current hash functions.
+ * It should be removed once a protocol fix is in place.
+ */
+ if ((pad + total) % len > len - 9
+ && total % len <= len - 9) {
+ if (len < plaintext->size)
+ _gnutls_auth_cipher_add_auth
+ (¶ms->read.
+ cipher_state,
+ plaintext->data, len);
+ else
+ _gnutls_auth_cipher_add_auth
+ (¶ms->read.
+ cipher_state,
+ plaintext->data,
+ plaintext->size);
+ }
+ }
+ }
+ }
+}
/* Deciphers the ciphertext packet, and puts the result to compress_data, of compress_size.
* Returns the actual compressed packet size.
*/
static int
-ciphertext_to_compressed (gnutls_session_t session,
- gnutls_datum_t *ciphertext,
- uint8_t * compress_data,
- int compress_size,
- uint8_t type, record_parameters_st * params,
- uint64* sequence)
+ciphertext_to_compressed(gnutls_session_t session,
+ gnutls_datum_t * ciphertext,
+ gnutls_datum_t * compressed,
+ uint8_t type, record_parameters_st * params,
+ uint64 * sequence)
{
- uint8_t tag[MAX_HASH_SIZE];
- uint8_t pad;
- int length, length_to_decrypt;
- uint16_t blocksize;
- int ret, i, pad_failed = 0;
- uint8_t preamble[MAX_PREAMBLE_SIZE];
- unsigned int preamble_size;
- unsigned int ver = gnutls_protocol_get_version (session);
- unsigned int tag_size = _gnutls_auth_cipher_tag_len (¶ms->read.cipher_state);
- unsigned int explicit_iv = _gnutls_version_has_explicit_iv (session->security_parameters.version);
-
- blocksize = gnutls_cipher_get_block_size (params->cipher_algorithm);
-
- /* actual decryption (inplace)
- */
- switch (_gnutls_cipher_is_block (params->cipher_algorithm))
- {
- case CIPHER_STREAM:
- /* The way AEAD ciphers are defined in RFC5246, it allows
- * only stream ciphers.
- */
- if (explicit_iv && _gnutls_auth_cipher_is_aead(¶ms->read.cipher_state))
- {
- uint8_t nonce[blocksize];
- /* Values in AEAD are pretty fixed in TLS 1.2 for 128-bit block
- */
- if (params->read.IV.data == NULL || params->read.IV.size != 4)
- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
-
- if (ciphertext->size < tag_size+AEAD_EXPLICIT_DATA_SIZE)
- return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
-
- memcpy(nonce, params->read.IV.data, AEAD_IMPLICIT_DATA_SIZE);
- memcpy(&nonce[AEAD_IMPLICIT_DATA_SIZE], ciphertext->data, AEAD_EXPLICIT_DATA_SIZE);
-
- _gnutls_auth_cipher_setiv(¶ms->read.cipher_state, nonce, AEAD_EXPLICIT_DATA_SIZE+AEAD_IMPLICIT_DATA_SIZE);
-
- ciphertext->data += AEAD_EXPLICIT_DATA_SIZE;
- ciphertext->size -= AEAD_EXPLICIT_DATA_SIZE;
-
- length_to_decrypt = ciphertext->size - tag_size;
- }
- else
- {
- if (ciphertext->size < tag_size)
- return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
-
- length_to_decrypt = ciphertext->size;
- }
-
- length = ciphertext->size - tag_size;
-
- /* Pass the type, version, length and compressed through
- * MAC.
- */
- preamble_size =
- make_preamble (UINT64DATA(*sequence), type,
- length, ver, preamble);
-
- ret = _gnutls_auth_cipher_add_auth (¶ms->read.cipher_state, preamble, preamble_size);
- if (ret < 0)
- return gnutls_assert_val(ret);
-
- if ((ret =
- _gnutls_auth_cipher_decrypt2 (¶ms->read.cipher_state,
- ciphertext->data, length_to_decrypt,
- ciphertext->data, ciphertext->size)) < 0)
- return gnutls_assert_val(ret);
-
- break;
- case CIPHER_BLOCK:
- if (ciphertext->size < blocksize || (ciphertext->size % blocksize != 0))
- return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
-
- /* ignore the IV in TLS 1.1+
- */
- if (explicit_iv)
- {
- _gnutls_auth_cipher_setiv(¶ms->read.cipher_state,
- ciphertext->data, blocksize);
-
- ciphertext->size -= blocksize;
- ciphertext->data += blocksize;
- }
-
- if (ciphertext->size < tag_size)
- return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
-
- /* we don't use the auth_cipher interface here, since
- * TLS with block ciphers is impossible to be used under such
- * an API. (the length of plaintext is required to calculate
- * auth_data, but it is not available before decryption).
- */
- if ((ret =
- _gnutls_cipher_decrypt (¶ms->read.cipher_state.cipher,
- ciphertext->data, ciphertext->size)) < 0)
- return gnutls_assert_val(ret);
-
- pad = ciphertext->data[ciphertext->size - 1] + 1; /* pad */
-
-
- if ((int) pad > (int) ciphertext->size - tag_size)
- {
- gnutls_assert ();
- _gnutls_record_log
- ("REC[%p]: Short record length %d > %d - %d (under attack?)\n",
- session, pad, ciphertext->size, tag_size);
- /* We do not fail here. We check below for the
- * the pad_failed. If zero means success.
- */
- pad_failed = GNUTLS_E_DECRYPTION_FAILED;
- pad %= blocksize;
- }
-
- length = ciphertext->size - tag_size - pad;
-
- /* Check the pading bytes (TLS 1.x)
- */
- if (ver != GNUTLS_SSL3)
- for (i = 2; i < pad; i++)
- {
- if (ciphertext->data[ciphertext->size - i] !=
- ciphertext->data[ciphertext->size - 1])
- pad_failed = GNUTLS_E_DECRYPTION_FAILED;
- }
-
- if (length < 0)
- {
- /* Setting a proper length to prevent timing differences in
- * processing of records with invalid encryption.
- */
- length = ciphertext->size - tag_size;
- }
-
- /* Pass the type, version, length and compressed through
- * MAC.
- */
- preamble_size =
- make_preamble (UINT64DATA(*sequence), type,
- length, ver, preamble);
- ret = _gnutls_auth_cipher_add_auth (¶ms->read.cipher_state, preamble, preamble_size);
- if (ret < 0)
- return gnutls_assert_val(ret);
-
- ret = _gnutls_auth_cipher_add_auth (¶ms->read.cipher_state, ciphertext->data, length);
- if (ret < 0)
- return gnutls_assert_val(ret);
-
- break;
- default:
- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
- }
-
- ret = _gnutls_auth_cipher_tag(¶ms->read.cipher_state, tag, tag_size);
- if (ret < 0)
- return gnutls_assert_val(ret);
-
- /* This one was introduced to avoid a timing attack against the TLS
- * 1.0 protocol.
- */
- /* HMAC was not the same.
- */
- if (memcmp (tag, &ciphertext->data[length], tag_size) != 0 || pad_failed != 0)
- return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
-
- /* copy the decrypted stuff to compress_data.
- */
- if (compress_size < length)
- return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED);
-
- if (compress_data != ciphertext->data)
- memcpy (compress_data, ciphertext->data, length);
-
- return length;
+ uint8_t tag[MAX_HASH_SIZE];
+ const uint8_t *tag_ptr;
+ unsigned int pad = 0, i;
+ int length, length_to_decrypt;
+ uint16_t blocksize;
+ int ret;
+ unsigned int tmp_pad_failed = 0;
+ unsigned int pad_failed = 0;
+ uint8_t preamble[MAX_PREAMBLE_SIZE];
+ unsigned int preamble_size;
+ const version_entry_st *ver = get_version(session);
+ unsigned int tag_size =
+ _gnutls_auth_cipher_tag_len(¶ms->read.cipher_state);
+ unsigned int explicit_iv = _gnutls_version_has_explicit_iv(ver);
+ unsigned imp_iv_size, iv_size, exp_iv_size;
+
+ if (unlikely(ver == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ imp_iv_size = _gnutls_cipher_get_implicit_iv_size(params->cipher);
+ exp_iv_size = _gnutls_cipher_get_explicit_iv_size(params->cipher);
+ iv_size = _gnutls_cipher_get_iv_size(params->cipher);
+ blocksize = _gnutls_cipher_get_block_size(params->cipher);
+
+ /* actual decryption (inplace)
+ */
+ switch (_gnutls_cipher_is_block(params->cipher)) {
+ case CIPHER_STREAM:
+ /* The way AEAD ciphers are defined in RFC5246, it allows
+ * only stream ciphers.
+ */
+ if (explicit_iv
+ && _gnutls_auth_cipher_is_aead(¶ms->read.
+ cipher_state)) {
+ uint8_t nonce[blocksize];
+ /* Values in AEAD are pretty fixed in TLS 1.2 for 128-bit block
+ */
+ if (unlikely
+ (params->read.IV.data == NULL
+ || params->read.IV.size != 4))
+ return
+ gnutls_assert_val
+ (GNUTLS_E_INTERNAL_ERROR);
+
+ if (unlikely
+ (ciphertext->size <
+ tag_size + exp_iv_size))
+ return
+ gnutls_assert_val
+ (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ memcpy(nonce, params->read.IV.data,
+ imp_iv_size);
+ memcpy(&nonce[imp_iv_size],
+ ciphertext->data, exp_iv_size);
+
+ _gnutls_auth_cipher_setiv(¶ms->read.
+ cipher_state, nonce,
+ exp_iv_size +
+ imp_iv_size);
+
+ ciphertext->data += exp_iv_size;
+ ciphertext->size -= exp_iv_size;
+
+ length = length_to_decrypt =
+ ciphertext->size - tag_size;
+ tag_ptr = ciphertext->data + length_to_decrypt;
+ } else if (iv_size > 0) { /* a stream cipher with explicit IV */
+ _gnutls_auth_cipher_setiv(¶ms->read.
+ cipher_state,
+ UINT64DATA(*sequence),
+ 8);
+ length_to_decrypt = ciphertext->size;
+ length = ciphertext->size - tag_size;
+ tag_ptr = compressed->data + length;
+ } else {
+ if (unlikely(ciphertext->size < tag_size))
+ return
+ gnutls_assert_val
+ (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ length_to_decrypt = ciphertext->size;
+ length = ciphertext->size - tag_size;
+ tag_ptr = compressed->data + length;
+ }
+
+
+ /* Pass the type, version, length and compressed through
+ * MAC.
+ */
+ preamble_size =
+ make_preamble(UINT64DATA(*sequence), type,
+ length, ver, preamble);
+
+ _gnutls_auth_cipher_set_mac_nonce(¶ms->read.
+ cipher_state,
+ UINT64DATA(*sequence),
+ 8);
+ ret =
+ _gnutls_auth_cipher_add_auth(¶ms->read.
+ cipher_state, preamble,
+ preamble_size);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+
+ if (unlikely
+ ((unsigned) length_to_decrypt > compressed->size)) {
+ _gnutls_audit_log(session,
+ "Received %u bytes, while expecting less than %u\n",
+ (unsigned int) length_to_decrypt,
+ (unsigned int) compressed->size);
+ return
+ gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ }
+
+ ret =
+ _gnutls_auth_cipher_decrypt2(¶ms->read.
+ cipher_state,
+ ciphertext->data,
+ length_to_decrypt,
+ compressed->data,
+ compressed->size);
+
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+
+ break;
+ case CIPHER_BLOCK:
+ if (unlikely
+ (ciphertext->size < blocksize
+ || (ciphertext->size % blocksize != 0)))
+ return
+ gnutls_assert_val
+ (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ /* ignore the IV in TLS 1.1+
+ */
+ if (explicit_iv) {
+ _gnutls_auth_cipher_setiv(¶ms->read.
+ cipher_state,
+ ciphertext->data,
+ blocksize);
+
+ ciphertext->size -= blocksize;
+ ciphertext->data += blocksize;
+ }
+
+ if (unlikely(ciphertext->size < tag_size + 1))
+ return
+ gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+
+ /* we don't use the auth_cipher interface here, since
+ * TLS with block ciphers is impossible to be used under such
+ * an API. (the length of plaintext is required to calculate
+ * auth_data, but it is not available before decryption).
+ */
+ if (unlikely(ciphertext->size > compressed->size))
+ return
+ gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+
+ ret =
+ _gnutls_cipher_decrypt2(¶ms->read.cipher_state.
+ cipher, ciphertext->data,
+ ciphertext->size,
+ compressed->data,
+ compressed->size);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+
+ pad = compressed->data[ciphertext->size - 1]; /* pad */
+
+ /* Check the pading bytes (TLS 1.x).
+ * Note that we access all 256 bytes of ciphertext for padding check
+ * because there is a timing channel in that memory access (in certain CPUs).
+ */
+ if (ver->id != GNUTLS_SSL3)
+ for (i = 2; i <= MIN(256, ciphertext->size); i++) {
+ tmp_pad_failed |=
+ (compressed->
+ data[ciphertext->size - i] != pad);
+ pad_failed |=
+ ((i <= (1 + pad)) & (tmp_pad_failed));
+ }
+
+ if (unlikely
+ (pad_failed != 0
+ || (1 + pad > ((int) ciphertext->size - tag_size)))) {
+ /* We do not fail here. We check below for the
+ * the pad_failed. If zero means success.
+ */
+ pad_failed = 1;
+ pad = 0;
+ }
+
+ length = ciphertext->size - tag_size - pad - 1;
+ tag_ptr = &compressed->data[length];
+
+ /* Pass the type, version, length and compressed through
+ * MAC.
+ */
+ preamble_size =
+ make_preamble(UINT64DATA(*sequence), type,
+ length, ver, preamble);
+
+ _gnutls_auth_cipher_set_mac_nonce(¶ms->read.
+ cipher_state,
+ UINT64DATA(*sequence),
+ 8);
+ ret =
+ _gnutls_auth_cipher_add_auth(¶ms->read.
+ cipher_state, preamble,
+ preamble_size);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+
+ ret =
+ _gnutls_auth_cipher_add_auth(¶ms->read.
+ cipher_state,
+ compressed->data, length);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
+ ret =
+ _gnutls_auth_cipher_tag(¶ms->read.cipher_state, tag,
+ tag_size);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+
+ /* Here there could be a timing leakage in CBC ciphersuites that
+ * could be exploited if the cost of a successful memcmp is high.
+ * A constant time memcmp would help there, but it is not easy to maintain
+ * against compiler optimizations. Currently we rely on the fact that
+ * a memcmp comparison is negligible over the crypto operations.
+ */
+ if (unlikely
+ (memcmp(tag, tag_ptr, tag_size) != 0 || pad_failed != 0)) {
+ /* HMAC was not the same. */
+ dummy_wait(params, compressed, pad_failed, pad,
+ length + preamble_size);
+
+ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ }
+
+ return length;
}