summaryrefslogtreecommitdiff
path: root/lib/gnutls_cipher.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gnutls_cipher.c')
-rw-r--r--lib/gnutls_cipher.c294
1 files changed, 189 insertions, 105 deletions
diff --git a/lib/gnutls_cipher.c b/lib/gnutls_cipher.c
index e9f76cf39e..82abe270e3 100644
--- a/lib/gnutls_cipher.c
+++ b/lib/gnutls_cipher.c
@@ -206,14 +206,19 @@ 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)
+ unsigned auth_cipher,
+ uint16_t blocksize,
+ unsigned etm)
{
/* 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 pre_length = data_size + *pad;
unsigned int length, new_pad;
+ if (etm == 0)
+ pre_length += hash_size;
+
new_pad = (uint8_t) (blocksize - (pre_length % blocksize)) + *pad;
if (new_pad > 255)
@@ -292,7 +297,7 @@ compressed_to_ciphertext(gnutls_session_t session,
_gnutls_auth_cipher_tag_len(&params->write.cipher_state);
int blocksize = _gnutls_cipher_get_block_size(params->cipher);
unsigned algo_type = _gnutls_cipher_type(params->cipher);
- uint8_t *data_ptr;
+ uint8_t *data_ptr, *full_cipher_ptr;
const version_entry_st *ver = get_version(session);
int explicit_iv = _gnutls_version_has_explicit_iv(ver);
int auth_cipher =
@@ -303,19 +308,12 @@ compressed_to_ciphertext(gnutls_session_t session,
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);
_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 (algo_type == CIPHER_BLOCK) {
@@ -330,8 +328,11 @@ compressed_to_ciphertext(gnutls_session_t session,
length =
calc_enc_length_block(session, ver, compressed->size,
tag_size, &pad, auth_cipher,
- blocksize);
+ blocksize, params->etm);
} else { /* AEAD + STREAM */
+ imp_iv_size = _gnutls_cipher_get_implicit_iv_size(params->cipher);
+ exp_iv_size = _gnutls_cipher_get_explicit_iv_size(params->cipher);
+
pad = 0;
length =
calc_enc_length_stream(session, compressed->size,
@@ -347,18 +348,22 @@ compressed_to_ciphertext(gnutls_session_t session,
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
data_ptr = cipher_data;
+ full_cipher_ptr = data_ptr;
- if (algo_type == CIPHER_BLOCK && explicit_iv != 0) {
- /* copy the random IV.
- */
- memcpy(data_ptr, nonce, blocksize);
- _gnutls_auth_cipher_setiv(&params->write.
- cipher_state, data_ptr,
- blocksize);
+ if (algo_type == CIPHER_BLOCK || algo_type == CIPHER_STREAM) {
+ if (algo_type == CIPHER_BLOCK && explicit_iv != 0) {
+ /* copy the random IV.
+ */
+ memcpy(data_ptr, nonce, blocksize);
+ _gnutls_auth_cipher_setiv(&params->write.
+ cipher_state, data_ptr,
+ blocksize);
+
+ data_ptr += blocksize;
+ cipher_data += blocksize;
+ }
- data_ptr += blocksize;
- cipher_data += blocksize;
- } else if (algo_type == CIPHER_AEAD) {
+ } else { /* AEAD */
/* Values in AEAD are pretty fixed in TLS 1.2 for 128-bit block
*/
if (params->write.IV.data == NULL
@@ -391,6 +396,15 @@ compressed_to_ciphertext(gnutls_session_t session,
cipher_data += exp_iv_size;
}
+ if (params->etm && algo_type != CIPHER_AEAD)
+ ret = length-tag_size;
+ else
+ ret = compressed->size;
+
+ preamble_size =
+ make_preamble(UINT64DATA(params->write.sequence_number),
+ type, ret, ver, preamble);
+
/* add the authenticate data */
ret =
_gnutls_auth_cipher_add_auth(&params->write.cipher_state,
@@ -398,6 +412,15 @@ compressed_to_ciphertext(gnutls_session_t session,
if (ret < 0)
return gnutls_assert_val(ret);
+ if (params->etm && explicit_iv && algo_type == CIPHER_BLOCK) {
+ /* In EtM we need to hash the IV as well */
+ ret =
+ _gnutls_auth_cipher_add_auth(&params->write.cipher_state,
+ full_cipher_ptr, blocksize);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
/* Actual encryption.
*/
ret =
@@ -459,7 +482,7 @@ ciphertext_to_compressed(gnutls_session_t session,
{
uint8_t tag[MAX_HASH_SIZE];
uint8_t nonce[MAX_CIPHER_BLOCK_SIZE];
- const uint8_t *tag_ptr;
+ const uint8_t *tag_ptr = NULL;
unsigned int pad = 0, i;
int length, length_to_decrypt;
uint16_t blocksize;
@@ -467,12 +490,13 @@ ciphertext_to_compressed(gnutls_session_t session,
unsigned int tmp_pad_failed = 0;
unsigned int pad_failed = 0;
uint8_t preamble[MAX_PREAMBLE_SIZE];
- unsigned int preamble_size;
+ unsigned int preamble_size = 0;
const version_entry_st *ver = get_version(session);
unsigned int tag_size =
_gnutls_auth_cipher_tag_len(&params->read.cipher_state);
unsigned int explicit_iv = _gnutls_version_has_explicit_iv(ver);
unsigned imp_iv_size, exp_iv_size;
+ unsigned cipher_type = _gnutls_cipher_type(params->cipher);
if (unlikely(ver == NULL))
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
@@ -481,9 +505,42 @@ ciphertext_to_compressed(gnutls_session_t session,
exp_iv_size = _gnutls_cipher_get_explicit_iv_size(params->cipher);
blocksize = _gnutls_cipher_get_block_size(params->cipher);
+ /* if EtM mode and not AEAD */
+ if (params->etm !=0 && cipher_type != CIPHER_AEAD) {
+ if (unlikely(ciphertext->size < tag_size))
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ preamble_size = make_preamble(UINT64DATA(*sequence),
+ type, ciphertext->size-tag_size,
+ ver, preamble);
+
+ ret = _gnutls_auth_cipher_add_auth(&params->read.
+ cipher_state, preamble,
+ preamble_size);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_auth_cipher_add_auth(&params->read.
+ cipher_state,
+ ciphertext->data,
+ ciphertext->size-tag_size);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_auth_cipher_tag(&params->read.cipher_state, tag, tag_size);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+
+ if (unlikely(memcmp(tag, &ciphertext->data[ciphertext->size-tag_size], tag_size) != 0)) {
+ /* HMAC was not the same. */
+ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ }
+
+ }
+
/* actual decryption (inplace)
*/
- switch (_gnutls_cipher_type(params->cipher)) {
+ switch (cipher_type) {
case CIPHER_AEAD:
/* The way AEAD ciphers are defined in RFC5246, it allows
* only stream ciphers.
@@ -569,16 +626,18 @@ ciphertext_to_compressed(gnutls_session_t session,
/* Pass the type, version, length and compressed through
* MAC.
*/
- preamble_size =
- make_preamble(UINT64DATA(*sequence), type,
- length, ver, preamble);
+ if (params->etm == 0) {
+ preamble_size =
+ make_preamble(UINT64DATA(*sequence), type,
+ length, ver, preamble);
- ret =
- _gnutls_auth_cipher_add_auth(&params->read.
- cipher_state, preamble,
- preamble_size);
- if (unlikely(ret < 0))
- return gnutls_assert_val(ret);
+ ret =
+ _gnutls_auth_cipher_add_auth(&params->read.
+ cipher_state, preamble,
+ preamble_size);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ }
if (unlikely
((unsigned) length_to_decrypt > compressed->size)) {
@@ -603,13 +662,19 @@ ciphertext_to_compressed(gnutls_session_t session,
break;
case CIPHER_BLOCK:
- if (unlikely
- (ciphertext->size < blocksize
- || (ciphertext->size % blocksize != 0)))
+ if (unlikely(ciphertext->size < blocksize))
return
gnutls_assert_val
(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+ if (params->etm == 0) {
+ if (unlikely(ciphertext->size % blocksize != 0))
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+ } else {
+ if (unlikely((ciphertext->size - tag_size) % blocksize != 0))
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+ }
+
/* ignore the IV in TLS 1.1+
*/
if (explicit_iv) {
@@ -618,6 +683,7 @@ ciphertext_to_compressed(gnutls_session_t session,
ciphertext->data,
blocksize);
+ memcpy(nonce, ciphertext->data, blocksize);
ciphertext->size -= blocksize;
ciphertext->data += blocksize;
}
@@ -635,88 +701,106 @@ ciphertext_to_compressed(gnutls_session_t session,
return
gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
- ret =
- _gnutls_cipher_decrypt2(&params->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.
+ if (params->etm == 0) {
+ ret =
+ _gnutls_cipher_decrypt2(&params->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).
*/
- pad_failed = 1;
- pad = 0;
- }
+ 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));
+ }
- length = ciphertext->size - tag_size - pad - 1;
- tag_ptr = &compressed->data[length];
+ 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;
+ }
- /* Pass the type, version, length and compressed through
- * MAC.
- */
- preamble_size =
- make_preamble(UINT64DATA(*sequence), type,
- length, ver, preamble);
+ length = ciphertext->size - tag_size - pad - 1;
+ tag_ptr = &compressed->data[length];
- ret =
- _gnutls_auth_cipher_add_auth(&params->read.
- cipher_state, preamble,
- preamble_size);
- if (unlikely(ret < 0))
- return gnutls_assert_val(ret);
+ /* 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(&params->read.
- cipher_state,
- compressed->data, length);
- if (unlikely(ret < 0))
- return gnutls_assert_val(ret);
+ ret =
+ _gnutls_auth_cipher_add_auth(&params->read.
+ cipher_state, preamble,
+ preamble_size);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ ret =
+ _gnutls_auth_cipher_add_auth(&params->read.
+ cipher_state,
+ compressed->data, length);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ } else { /* EtM */
+ ret =
+ _gnutls_cipher_decrypt2(&params->read.cipher_state.
+ cipher, ciphertext->data,
+ ciphertext->size - tag_size,
+ compressed->data,
+ compressed->size);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+
+ pad = compressed->data[ciphertext->size - tag_size - 1]; /* pad */
+ length = ciphertext->size - tag_size - pad - 1;
+
+ if (unlikely(length < 0))
+ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ }
break;
default:
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
}
- ret =
- _gnutls_auth_cipher_tag(&params->read.cipher_state, tag,
- tag_size);
- if (unlikely(ret < 0))
- return gnutls_assert_val(ret);
+ if (params->etm ==0 && cipher_type != CIPHER_AEAD) {
+ ret =
+ _gnutls_auth_cipher_tag(&params->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);
+ /* 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 gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ }
}
return length;