コード例 #1
0
ファイル: cipher_int.c プロジェクト: attilamolnar/gnutls
/* The caller must make sure that textlen+pad_size+tag_size is divided by the block size of the cipher */
int _gnutls_auth_cipher_encrypt2_tag(auth_cipher_hd_st * handle,
				     const uint8_t * text, int textlen,
				     void *_ciphertext, int ciphertextlen,
				     int pad_size)
{
	int ret;
	uint8_t *ciphertext = _ciphertext;
	unsigned blocksize =
	    _gnutls_cipher_get_block_size(handle->cipher.e);
	unsigned l;

	if (handle->is_mac) { /* cipher + mac */
		if (handle->non_null == 0) { /* NULL cipher + MAC */
			MAC(handle, text, textlen);

			if (unlikely(textlen + pad_size + handle->tag_size) >
			    ciphertextlen)
				return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

			if (text != ciphertext)
				memcpy(ciphertext, text, textlen);
			ret =
			    _gnutls_auth_cipher_tag(handle,
						    ciphertext + textlen,
						    handle->tag_size);
			if (ret < 0)
				return gnutls_assert_val(ret);

		} else {
			uint8_t *orig_ciphertext = ciphertext;

			if (handle->etm == 0 || handle->cipher.e->type != CIPHER_BLOCK) {
				MAC(handle, text, textlen);
			}

			if (unlikely(textlen + pad_size + handle->tag_size) >
			    ciphertextlen)
				return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

			l = (textlen / blocksize) * blocksize;
			if (l > 0) {
				ret =
			    	_gnutls_cipher_encrypt2(&handle->cipher, text,
						    l, ciphertext,
						    ciphertextlen);
				if (ret < 0)
					return gnutls_assert_val(ret);

				textlen -= l;
				text += l;
				ciphertext += l;
				ciphertextlen -= l;
			}

			if (ciphertext != text && textlen > 0)
				memcpy(ciphertext, text, textlen);

			if (handle->etm == 0 || handle->cipher.e->type != CIPHER_BLOCK) {
				ret =
				    _gnutls_auth_cipher_tag(handle,
							    ciphertext + textlen,
							    handle->tag_size);
				if (ret < 0)
					return gnutls_assert_val(ret);
				textlen += handle->tag_size;
			}

			/* TLS 1.0 style padding */
			if (pad_size > 0) {
				memset(ciphertext + textlen, pad_size - 1,
				       pad_size);
				textlen += pad_size;
			}

			ret =
			    _gnutls_cipher_encrypt2(&handle->cipher,
						    ciphertext, textlen,
						    ciphertext,
						    ciphertextlen);
			if (ret < 0)
				return gnutls_assert_val(ret);

			if (handle->etm != 0 && handle->cipher.e->type == CIPHER_BLOCK) {
				MAC(handle, orig_ciphertext, l);
				MAC(handle, ciphertext, textlen);

				ret =
				    _gnutls_auth_cipher_tag(handle,
						    	    ciphertext + textlen,
						    	    handle->tag_size);
				if (ret < 0)
					return gnutls_assert_val(ret);
			}
		}
	} else if (_gnutls_cipher_is_aead(&handle->cipher)) {
		ret =
		    _gnutls_cipher_encrypt2(&handle->cipher, text, textlen,
					    ciphertext, ciphertextlen);
		if (unlikely(ret < 0))
			return gnutls_assert_val(ret);

		ret =
		    _gnutls_auth_cipher_tag(handle, ciphertext + textlen,
					    handle->tag_size);
		if (unlikely(ret < 0))
			return gnutls_assert_val(ret);
	} else if (handle->non_null == 0 && text != ciphertext) /* NULL cipher - no MAC */
		memcpy(ciphertext, text, textlen);

	return 0;
}
コード例 #2
0
/* 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 (&params->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(&params->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(&params->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 (&params->read.cipher_state, preamble, preamble_size);

      if ((ret =
           _gnutls_auth_cipher_decrypt (&params->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(&params->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 (&params->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)
        length = 0;

      /* Pass the type, version, length and compressed through
       * MAC.
       */
      preamble_size =
        make_preamble (UINT64DATA(*sequence), type,
                       length, ver, preamble);
      _gnutls_auth_cipher_add_auth (&params->read.cipher_state, preamble, preamble_size);
      _gnutls_auth_cipher_add_auth (&params->read.cipher_state, ciphertext->data, length);

      break;
    default:
      return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
    }

  ret = _gnutls_auth_cipher_tag(&params->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;
}
コード例 #3
0
ファイル: gnutls_cipher.c プロジェクト: Distrotech/gnutls
/* 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,
			 gnutls_datum_t * compressed,
			 uint8_t type, record_parameters_st * params,
			 uint64 * sequence)
{
	uint8_t tag[MAX_HASH_SIZE];
	uint8_t nonce[MAX_CIPHER_BLOCK_SIZE];
	const uint8_t *tag_ptr = NULL;
	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 = 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);
	bool etm = 0;

	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);
	blocksize = _gnutls_cipher_get_block_size(params->cipher);

	if (params->etm !=0 && cipher_type == CIPHER_BLOCK)
		etm = 1;

	/* if EtM mode and not AEAD */
	if (etm) {
		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(gnutls_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 (cipher_type) {
	case CIPHER_AEAD:
		/* The way AEAD ciphers are defined in RFC5246, it allows
		 * only stream ciphers.
		 */
		if (unlikely(_gnutls_auth_cipher_is_aead(&params->read.
						   cipher_state) == 0))
			return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

		/* 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);


		ciphertext->data += exp_iv_size;
		ciphertext->size -= exp_iv_size;

		length =
		    ciphertext->size - tag_size;

		length_to_decrypt = ciphertext->size;

		/* Pass the type, version, length and compressed through
		 * MAC.
		 */
		preamble_size =
		    make_preamble(UINT64DATA(*sequence), type,
				  length, ver, preamble);


		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_aead_cipher_decrypt(&params->read.cipher_state.cipher,
						  nonce, exp_iv_size + imp_iv_size,
						  preamble, preamble_size,
						  tag_size,
						  ciphertext->data, length_to_decrypt,
						  compressed->data, compressed->size);
		if (unlikely(ret < 0))
			return gnutls_assert_val(ret);

		return length;

		break;
	case CIPHER_STREAM:
		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);

		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)) {
			_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(&params->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))
			return
			    gnutls_assert_val
			    (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

		if (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) {
			_gnutls_auth_cipher_setiv(&params->read.
						  cipher_state,
						  ciphertext->data,
						  blocksize);

			memcpy(nonce, 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);

		if (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).
			 */
			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);

			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);
	}

	/* STREAM or BLOCK arrive here */
	if (etm == 0) {
		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
		    (gnutls_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;
}
コード例 #4
0
ファイル: cipher.c プロジェクト: gnutls/gnutls
/* Deciphers the ciphertext packet, and puts the result to plain.
 * Returns the actual plaintext packet size.
 */
static int
decrypt_packet(gnutls_session_t session,
		gnutls_datum_t * ciphertext,
		gnutls_datum_t * plain,
		content_type_t type, record_parameters_st * params,
		gnutls_uint64 * sequence)
{
	uint8_t tag[MAX_HASH_SIZE];
	uint8_t nonce[MAX_CIPHER_IV_SIZE];
	const uint8_t *tag_ptr = NULL;
	unsigned int pad = 0;
	int length, length_to_decrypt;
	uint16_t blocksize;
	int ret;
	uint8_t preamble[MAX_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.ctx.tls12);
	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);
	bool etm = 0;

	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);
	blocksize = _gnutls_cipher_get_block_size(params->cipher);

	if (params->etm !=0 && cipher_type == CIPHER_BLOCK)
		etm = 1;

	/* if EtM mode and not AEAD */
	if (etm) {
		if (unlikely(ciphertext->size < tag_size))
			return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

		preamble_size = _gnutls_make_preamble(UINT64DATA(*sequence),
						      type, ciphertext->size-tag_size,
						      ver, preamble);

		ret = _gnutls_auth_cipher_add_auth(&params->read.
						   ctx.tls12, preamble,
						   preamble_size);
		if (unlikely(ret < 0))
			return gnutls_assert_val(ret);

		ret = _gnutls_auth_cipher_add_auth(&params->read.
						   ctx.tls12,
						   ciphertext->data,
						   ciphertext->size-tag_size);
		if (unlikely(ret < 0))
			return gnutls_assert_val(ret);

		ret = _gnutls_auth_cipher_tag(&params->read.ctx.tls12, tag, tag_size);
		if (unlikely(ret < 0))
			return gnutls_assert_val(ret);
		
		if (unlikely(gnutls_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 (cipher_type) {
	case CIPHER_AEAD:
		/* The way AEAD ciphers are defined in RFC5246, it allows
		 * only stream ciphers.
		 */
		if (unlikely(_gnutls_auth_cipher_is_aead(&params->read.
						   ctx.tls12) == 0))
			return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);


		if (unlikely(ciphertext->size < (tag_size + exp_iv_size)))
			return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);

		if (params->cipher->xor_nonce == 0) {
			/* Values in AEAD are pretty fixed in TLS 1.2 for 128-bit block
			 */
			 if (unlikely(params->read.iv_size != 4))
				return
				    gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);

			memcpy(nonce, params->read.iv,
			       imp_iv_size);

			memcpy(&nonce[imp_iv_size],
			       ciphertext->data, exp_iv_size);

			ciphertext->data += exp_iv_size;
			ciphertext->size -= exp_iv_size;
		} else { /* XOR nonce with IV */
			if (unlikely(params->read.iv_size != 12 || imp_iv_size != 12 || exp_iv_size != 0))
				return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);

			memset(nonce, 0, 4);
			memcpy(&nonce[4], UINT64DATA(*sequence), 8);

			memxor(nonce, params->read.iv, 12);
		}

		length =
		    ciphertext->size - tag_size;

		length_to_decrypt = ciphertext->size;

		/* Pass the type, version, length and plain through
		 * MAC.
		 */
		preamble_size =
		    _gnutls_make_preamble(UINT64DATA(*sequence), type,
					  length, ver, preamble);


		if (unlikely
		    ((unsigned) length_to_decrypt > plain->size)) {
			_gnutls_audit_log(session,
					  "Received %u bytes, while expecting less than %u\n",
					  (unsigned int) length_to_decrypt,
					  (unsigned int) plain->size);
			return
			    gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
		}

		ret = _gnutls_aead_cipher_decrypt(&params->read.ctx.tls12.cipher,
						  nonce, exp_iv_size + imp_iv_size,
						  preamble, preamble_size,
						  tag_size,
						  ciphertext->data, length_to_decrypt,
						  plain->data, plain->size);
		if (unlikely(ret < 0))
			return gnutls_assert_val(ret);

		return length;

		break;
	case CIPHER_STREAM:
		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 = plain->data + length;

		/* Pass the type, version, length and plain through
		 * MAC.
		 */
		preamble_size =
		    _gnutls_make_preamble(UINT64DATA(*sequence), type,
					  length, ver, preamble);

		ret =
		    _gnutls_auth_cipher_add_auth(&params->read.
						 ctx.tls12, preamble,
						 preamble_size);
		if (unlikely(ret < 0))
			return gnutls_assert_val(ret);

		if (unlikely
		    ((unsigned) length_to_decrypt > plain->size)) {
			_gnutls_audit_log(session,
					  "Received %u bytes, while expecting less than %u\n",
					  (unsigned int) length_to_decrypt,
					  (unsigned int) plain->size);
			return
			    gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
		}

		ret =
		    _gnutls_auth_cipher_decrypt2(&params->read.
						 ctx.tls12,
						 ciphertext->data,
						 length_to_decrypt,
						 plain->data,
						 plain->size);

		if (unlikely(ret < 0))
			return gnutls_assert_val(ret);

		ret =
		    _gnutls_auth_cipher_tag(&params->read.ctx.tls12, tag,
					    tag_size);
		if (unlikely(ret < 0))
			return gnutls_assert_val(ret);

		if (unlikely
		    (gnutls_memcmp(tag, tag_ptr, tag_size) != 0)) {
			return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
		}

		break;
	case CIPHER_BLOCK:
		if (unlikely(ciphertext->size < blocksize))
			return
			    gnutls_assert_val
			    (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

		if (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) {
			_gnutls_auth_cipher_setiv(&params->read.
						  ctx.tls12,
						  ciphertext->data,
						  blocksize);

			memcpy(nonce, 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 > plain->size))
			return
			    gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);

		if (etm == 0) {
			ret =
			    _gnutls_cipher_decrypt2(&params->read.ctx.tls12.
						    cipher, ciphertext->data,
						    ciphertext->size,
						    plain->data,
						    plain->size);
			if (unlikely(ret < 0))
				return gnutls_assert_val(ret);

			ret = cbc_mac_verify(session, params, preamble, type,
					     sequence, plain->data, ciphertext->size,
					     tag_size);
			if (unlikely(ret < 0))
				return gnutls_assert_val(ret);

			length = ret;
		} else { /* EtM */
			ret =
			    _gnutls_cipher_decrypt2(&params->read.ctx.tls12.
						    cipher, ciphertext->data,
						    ciphertext->size - tag_size,
						    plain->data,
						    plain->size);
			if (unlikely(ret < 0))
				return gnutls_assert_val(ret);

			pad = plain->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_DECRYPTION_FAILED);
	}


	return length;
}