/* 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; }
static cdk_error_t read_secret_key (cdk_stream_t inp, size_t pktlen, cdk_pkt_seckey_t sk) { size_t p1, p2, nread; int i, nskey; int rc; if (!inp || !sk || !sk->pk) return CDK_Inv_Value; if (DEBUG_PKT) _cdk_log_debug ("read_secret_key: %d octets\n", pktlen); p1 = cdk_stream_tell (inp); rc = read_public_key (inp, pktlen, sk->pk); if (rc) return rc; sk->s2k_usage = cdk_stream_getc (inp); sk->protect.sha1chk = 0; if (sk->s2k_usage == 254 || sk->s2k_usage == 255) { sk->protect.sha1chk = (sk->s2k_usage == 254); sk->protect.algo = _pgp_cipher_to_gnutls (cdk_stream_getc (inp)); sk->protect.s2k = cdk_calloc (1, sizeof *sk->protect.s2k); if (!sk->protect.s2k) return CDK_Out_Of_Core; rc = read_s2k (inp, sk->protect.s2k); if (rc) return rc; /* refer to --export-secret-subkeys in gpg(1) */ if (sk->protect.s2k->mode == CDK_S2K_GNU_EXT) sk->protect.ivlen = 0; else { sk->protect.ivlen = _gnutls_cipher_get_block_size (sk->protect.algo); if (!sk->protect.ivlen) return CDK_Inv_Packet; rc = stream_read (inp, sk->protect.iv, sk->protect.ivlen, &nread); if (rc) return rc; if (nread != sk->protect.ivlen) return CDK_Inv_Packet; } } else sk->protect.algo = _pgp_cipher_to_gnutls (sk->s2k_usage); if (sk->protect.algo == GNUTLS_CIPHER_NULL) { sk->csum = 0; nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo); if (!nskey) { gnutls_assert (); return CDK_Inv_Algo; } for (i = 0; i < nskey; i++) { rc = read_mpi (inp, &sk->mpi[i], 1); if (rc) return rc; } sk->csum = read_16 (inp); sk->is_protected = 0; } else if (sk->pk->version < 4) { /* The length of each multiprecision integer is stored in plaintext. */ nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo); if (!nskey) { gnutls_assert (); return CDK_Inv_Algo; } for (i = 0; i < nskey; i++) { rc = read_mpi (inp, &sk->mpi[i], 1); if (rc) return rc; } sk->csum = read_16 (inp); sk->is_protected = 1; } else { /* We need to read the rest of the packet because we do not have any information how long the encrypted mpi's are */ p2 = cdk_stream_tell (inp); p2 -= p1; sk->enclen = pktlen - p2; if (sk->enclen < 2) return CDK_Inv_Packet; /* at least 16 bits for the checksum! */ sk->encdata = cdk_calloc (1, sk->enclen + 1); if (!sk->encdata) return CDK_Out_Of_Core; if (stream_read (inp, sk->encdata, sk->enclen, &nread)) return CDK_Inv_Packet; /* Handle the GNU S2K extensions we know (just gnu-dummy right now): */ if (sk->protect.s2k->mode == CDK_S2K_GNU_EXT) { unsigned char gnumode; if ((sk->enclen < strlen ("GNU") + 1) || (0 != memcmp ("GNU", sk->encdata, strlen ("GNU")))) return CDK_Inv_Packet; gnumode = sk->encdata[strlen ("GNU")]; /* we only handle gnu-dummy (mode 1). mode 2 should refer to external smart cards. */ if (gnumode != 1) return CDK_Inv_Packet; /* gnu-dummy should have no more data */ if (sk->enclen != strlen ("GNU") + 1) return CDK_Inv_Packet; } nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo); if (!nskey) { gnutls_assert (); return CDK_Inv_Algo; } /* We mark each MPI entry with NULL to indicate a protected key. */ for (i = 0; i < nskey; i++) sk->mpi[i] = NULL; sk->is_protected = 1; } sk->is_primary = 1; _cdk_copy_pk_to_sk (sk->pk, sk); return 0; }
/* 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. */ 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_t MAC[MAX_HASH_SIZE]; uint16_t c_length; uint8_t pad; int length, ret; digest_hd_st td; uint8_t type = _type; uint8_t 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 */ ret = mac_init (&td, session->security_parameters.write_mac_algorithm, session->connection_state.write_mac_secret.data, session->connection_state.write_mac_secret.size, ver); if (ret < 0 && session->security_parameters.write_mac_algorithm != GNUTLS_MAC_NULL) { gnutls_assert (); return ret; } c_length = _gnutls_conv_uint16 (compressed.size); if (session->security_parameters.write_mac_algorithm != GNUTLS_MAC_NULL) { /* 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. */ ret = _gnutls_rnd (GNUTLS_RND_NONCE, data_ptr, blocksize); if (ret < 0) { gnutls_assert (); return ret; } 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; }
/* Deciphers the ciphertext packet, and puts the result to compress_data, of compress_size. * Returns the actual compressed packet size. */ int _gnutls_ciphertext2compressed (gnutls_session_t session, opaque * compress_data, int compress_size, gnutls_datum_t ciphertext, uint8_t type) { uint8_t MAC[MAX_HASH_SIZE]; uint16_t c_length; uint8_t pad; int length; digest_hd_st td; uint16_t blocksize; int ret, i, pad_failed = 0; uint8_t 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 */ ret = mac_init (&td, session->security_parameters.read_mac_algorithm, session->connection_state.read_mac_secret.data, session->connection_state.read_mac_secret.size, ver); if (ret < 0 && 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 */ if ((int) pad > (int) ciphertext.size - hash_size) { gnutls_assert (); _gnutls_record_log ("REC[%x]: Short record length %d > %d - %d (under attack?)\n", session, pad, ciphertext.size, hash_size); /* We do not fail here. We check below for the * the pad_failed. If zero means success. */ pad_failed = GNUTLS_E_DECRYPTION_FAILED; } length = ciphertext.size - hash_size - pad; /* 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_t) length); /* Pass the type, version, length and compressed through * MAC. */ if (session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL) { _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_DECOMPRESSION_FAILED; } memcpy (compress_data, ciphertext.data, length); 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, 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(¶ms->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(¶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, ciphertext->data, ciphertext->size-tag_size); if (unlikely(ret < 0)) return gnutls_assert_val(ret); ret = _gnutls_auth_cipher_tag(¶ms->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(¶ms->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(¶ms->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(¶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)) 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(¶ms->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(¶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); 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); } else { /* EtM */ ret = _gnutls_cipher_decrypt2(¶ms->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(¶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 (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; }
/* 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, 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 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 algo_type = _gnutls_cipher_type(params->cipher); 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 = _gnutls_auth_cipher_is_aead(¶ms->write.cipher_state); uint8_t nonce[MAX_CIPHER_BLOCK_SIZE]; unsigned imp_iv_size = 0, exp_iv_size = 0; bool etm = 0; if (unlikely(ver == NULL)) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); if (algo_type == CIPHER_BLOCK && params->etm != 0) etm = 1; _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); /* Calculate the encrypted length (padding etc.) */ if (algo_type == 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, 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, 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; full_cipher_ptr = data_ptr; 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(¶ms->write. cipher_state, data_ptr, blocksize); data_ptr += blocksize; cipher_data += blocksize; } } else { /* AEAD */ /* 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), and safer * as it will never reuse a value. */ memcpy(nonce, params->write.IV.data, params->write.IV.size); memcpy(&nonce[imp_iv_size], UINT64DATA(params->write.sequence_number), 8); /* copy the explicit part */ memcpy(data_ptr, &nonce[imp_iv_size], exp_iv_size); data_ptr += exp_iv_size; cipher_data += exp_iv_size; } if (etm) ret = length-tag_size; else ret = compressed->size; preamble_size = make_preamble(UINT64DATA(params->write.sequence_number), type, ret, ver, preamble); if (algo_type == CIPHER_BLOCK || algo_type == CIPHER_STREAM) { /* add the authenticated data */ ret = _gnutls_auth_cipher_add_auth(¶ms->write.cipher_state, preamble, preamble_size); if (ret < 0) return gnutls_assert_val(ret); if (etm && explicit_iv) { /* In EtM we need to hash the IV as well */ ret = _gnutls_auth_cipher_add_auth(¶ms->write.cipher_state, full_cipher_ptr, blocksize); 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); } else { /* AEAD */ ret = _gnutls_aead_cipher_encrypt(¶ms->write.cipher_state.cipher, nonce, imp_iv_size + exp_iv_size, preamble, preamble_size, tag_size, compressed->data, compressed->size, cipher_data, cipher_size); if (ret < 0) return gnutls_assert_val(ret); } return length; }
static int compressed_to_ciphertext_new(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) { uint16_t pad = min_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); 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, final_cipher_size; if (unlikely(ver == NULL)) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); iv_size = _gnutls_cipher_get_implicit_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); /* 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); /* cipher_data points to the start of data to be encrypted */ data_ptr = cipher_data; length_to_encrypt = length = 0; if (explicit_iv) { if (block_algo == CIPHER_BLOCK) { /* copy the random IV. */ DECR_LEN(cipher_size, blocksize); memcpy(data_ptr, nonce, blocksize); _gnutls_auth_cipher_setiv(¶ms->write. cipher_state, data_ptr, blocksize); data_ptr += blocksize; cipher_data += blocksize; length += 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 != 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 */ DECR_LEN(cipher_size, AEAD_EXPLICIT_DATA_SIZE); memcpy(data_ptr, &nonce[AEAD_IMPLICIT_DATA_SIZE], AEAD_EXPLICIT_DATA_SIZE); data_ptr += AEAD_EXPLICIT_DATA_SIZE; cipher_data += AEAD_EXPLICIT_DATA_SIZE; length += AEAD_EXPLICIT_DATA_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); } DECR_LEN(cipher_size, 2); if (block_algo == CIPHER_BLOCK) { /* make pad a multiple of blocksize */ unsigned t = (2 + pad + compressed->size + tag_size) % blocksize; if (t > 0) { pad += blocksize - t; } } _gnutls_write_uint16(pad, data_ptr); data_ptr += 2; length_to_encrypt += 2; length += 2; final_cipher_size = cipher_size; if (pad > 0) { unsigned t; t = cipher_size - compressed->size; if (pad > t) { if (block_algo == CIPHER_BLOCK) { if (pad <= blocksize) return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST); pad -= blocksize * ((pad - t) / blocksize); } else pad = t; } DECR_LEN(cipher_size, pad); memset(data_ptr, 0, pad); data_ptr += pad; length_to_encrypt += pad; length += pad; } DECR_LEN(cipher_size, compressed->size); memcpy(data_ptr, compressed->data, compressed->size); data_ptr += compressed->size; length_to_encrypt += compressed->size; length += compressed->size; if (tag_size > 0) { DECR_LEN(cipher_size, tag_size); data_ptr += tag_size; /* In AEAD ciphers we don't encrypt the tag */ length += tag_size; } preamble_size = make_preamble(UINT64DATA (params->write.sequence_number), type, compressed->size + 2 + pad, ver, preamble); _gnutls_auth_cipher_set_mac_nonce(¶ms->write.cipher_state, UINT64DATA(params->write. sequence_number), 8); /* add the authenticated 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, final_cipher_size, 0); if (ret < 0) return gnutls_assert_val(ret); return length; }
/* 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, 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 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; if (unlikely(ver == NULL)) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); iv_size = _gnutls_cipher_get_implicit_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); } 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) { /* 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 != 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; } 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; }
/* 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(¶ms->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(¶ms->read. ctx.tls12, preamble, preamble_size); if (unlikely(ret < 0)) return gnutls_assert_val(ret); ret = _gnutls_auth_cipher_add_auth(¶ms->read. ctx.tls12, ciphertext->data, ciphertext->size-tag_size); if (unlikely(ret < 0)) return gnutls_assert_val(ret); ret = _gnutls_auth_cipher_tag(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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; }