/*
* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2010
* Free Software Foundation, Inc.
*
* Author: Nikos Mavrogiannopoulos
*
* This file is part of GnuTLS.
*
* 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
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see
*
*/
/* Some high level functions to be used in the record encryption are
* included here.
*/
#include "gnutls_int.h"
#include "gnutls_errors.h"
#include "gnutls_compress.h"
#include "gnutls_cipher.h"
#include "algorithms.h"
#include "gnutls_hash_int.h"
#include "gnutls_cipher_int.h"
#include "debug.h"
#include "gnutls_num.h"
#include "gnutls_datum.h"
#include "gnutls_kx.h"
#include "gnutls_record.h"
#include "gnutls_constate.h"
#include
#include
static int compressed_to_ciphertext (gnutls_session_t session,
opaque * 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,
opaque * 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)
{
if (record_params->compression_algorithm == GNUTLS_COMP_NULL)
return 0;
return 1;
}
inline static int
is_read_comp_null (record_parameters_st * record_params)
{
if (record_params->compression_algorithm == GNUTLS_COMP_NULL)
return 0;
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 opaque * headers,
size_t headers_size, const opaque * data,
size_t data_size, opaque * ciphertext,
size_t ciphertext_size, 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 = (opaque*)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;
}
/* Decrypts the given data.
* Returns the decrypted data length.
*/
int
_gnutls_decrypt (gnutls_session_t session, opaque * ciphertext,
size_t ciphertext_size, uint8_t * data,
size_t max_data_size, 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
{
opaque* 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;
}
}
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)
{
uint8_t rnd;
int length, 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;
}
#define MAX_PREAMBLE_SIZE 16
/* generates the authentication data (data to be hashed only
* and are not to be sent). Returns their size.
*/
static inline int
make_preamble (opaque * uint64_data, opaque type, int length,
opaque ver, opaque * preamble)
{
opaque minor = _gnutls_version_get_minor (ver);
opaque major = _gnutls_version_get_major (ver);
opaque *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;
}
/* This is the actual encryption
* Encrypts the given compressed datum, and puts the result to cipher_data,
* which has cipher_size size.
* return the actual encrypted data length.
*/
static int
compressed_to_ciphertext (gnutls_session_t session,
opaque * cipher_data, int cipher_size,
gnutls_datum_t *compressed,
content_type_t type,
record_parameters_st * params)
{
uint8_t * tag_ptr = NULL;
uint8_t pad;
int length, length_to_encrypt, ret;
opaque 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);
opaque *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 */
_gnutls_auth_cipher_add_auth(¶ms->write.cipher_state, preamble, preamble_size);
/* Actual encryption (inplace).
*/
ret =
_gnutls_auth_cipher_encrypt_tag (¶ms->write.cipher_state,
cipher_data, length_to_encrypt, tag_ptr, tag_size, compressed->size);
if (ret < 0)
return gnutls_assert_val(ret);
return length;
}
/* 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,
opaque * compress_data,
int compress_size,
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;
opaque preamble[MAX_PREAMBLE_SIZE];
int preamble_size;
int ver = gnutls_protocol_get_version (session);
int tag_size = _gnutls_auth_cipher_tag_len (¶ms->read.cipher_state);
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);
_gnutls_auth_cipher_add_auth (¶ms->read.cipher_state, preamble, preamble_size);
if ((ret =
_gnutls_auth_cipher_decrypt (¶ms->read.cipher_state,
ciphertext->data, length_to_decrypt)) < 0)
return gnutls_assert_val(ret);
break;
case CIPHER_BLOCK:
if (ciphertext->size < MAX(blocksize, tag_size) || (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 == 0)
{
gnutls_assert ();
return 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);
_gnutls_auth_cipher_add_auth (¶ms->read.cipher_state, preamble, preamble_size);
_gnutls_auth_cipher_add_auth (¶ms->read.cipher_state, ciphertext->data, length);
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;
}