int _gnutls_decrypt_pbes1_des_md5_data(const char *password, unsigned password_len, const struct pbkdf2_params *kdf_params, const struct pbe_enc_params *enc_params, gnutls_datum_t *encrypted_data, /* we re-use it */ gnutls_datum_t *decrypted_data) { int result; gnutls_datum_t dkey, d_iv; cipher_hd_st ch; uint8_t key[16]; const unsigned block_size = 8; if (enc_params->cipher != GNUTLS_CIPHER_DES_CBC) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); if (encrypted_data->size % block_size != 0) return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); /* generate the key */ pbkdf1_md5(password, password_len, kdf_params->salt, kdf_params->iter_count, sizeof(key), key); dkey.data = key; dkey.size = 8; d_iv.data = &key[8]; d_iv.size = 8; result = _gnutls_cipher_init(&ch, cipher_to_entry(GNUTLS_CIPHER_DES_CBC), &dkey, &d_iv, 0); if (result < 0) return gnutls_assert_val(result); result = _gnutls_cipher_decrypt(&ch, encrypted_data->data, encrypted_data->size); if (result < 0) { gnutls_assert(); goto error; } if ((int)encrypted_data->size - encrypted_data->data[encrypted_data->size - 1] < 0) { gnutls_assert(); result = GNUTLS_E_ILLEGAL_PARAMETER; goto error; } decrypted_data->data = encrypted_data->data; decrypted_data->size = encrypted_data->size - encrypted_data->data[encrypted_data->size - 1]; result = 0; error: _gnutls_cipher_deinit(&ch); return result; }
/* 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, record_parameters_st * params) { uint8_t MAC[MAX_HASH_SIZE]; uint16_t c_length; uint8_t pad; int length; uint16_t blocksize; int ret, i, pad_failed = 0; opaque preamble[PREAMBLE_SIZE]; int preamble_size; int ver = gnutls_protocol_get_version (session); int hash_size = _gnutls_hash_get_algo_len (params->mac_algorithm); blocksize = gnutls_cipher_get_block_size (params->cipher_algorithm); /* actual decryption (inplace) */ switch (_gnutls_cipher_is_block (params->cipher_algorithm)) { case CIPHER_STREAM: if ((ret = _gnutls_cipher_decrypt (¶ms->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 (¶ms->read.cipher_state, ciphertext.data, ciphertext.size)) < 0) { gnutls_assert (); return ret; } /* ignore the IV in TLS 1.1. */ if (_gnutls_version_has_explicit_iv (session->security_parameters.version)) { 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[%p]: 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 (_gnutls_version_has_variable_padding (ver) && 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 (params->mac_algorithm != GNUTLS_MAC_NULL) { digest_hd_st td; ret = mac_init (&td, params->mac_algorithm, params->read.mac_secret.data, params->read.mac_secret.size, ver); if (ret < 0) { gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } preamble_size = make_preamble (UINT64DATA (params->read.sequence_number), type, c_length, ver, preamble); mac_hash (&td, preamble, preamble_size, ver); if (length > 0) mac_hash (&td, ciphertext.data, length, ver); mac_deinit (&td, MAC, ver); } /* This one was introduced to avoid a timing attack against the TLS * 1.0 protocol. */ if (pad_failed != 0) { gnutls_assert (); 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. */ 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; }
int _gnutls_decrypt_session_ticket(gnutls_session_t session, const gnutls_datum_t *ticket_data, gnutls_datum_t *state) { cipher_hd_st cipher_hd; gnutls_datum_t IV; gnutls_datum_t stek_key_name, stek_cipher_key, stek_mac_key; uint8_t cmac[TICKET_MAC_SIZE]; struct ticket_st ticket; int ret; /* callers must have that checked */ assert(!(session->internals.flags & GNUTLS_NO_TICKETS)); /* Retrieve ticket decryption keys */ if (_gnutls_get_session_ticket_decryption_key(session, ticket_data, &stek_key_name, &stek_mac_key, &stek_cipher_key) < 0) return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); ret = unpack_ticket(ticket_data, &ticket); if (ret < 0) return ret; /* If the key name of the ticket does not match the one that is currently active, issue a new ticket. */ if (memcmp (ticket.key_name, stek_key_name.data, stek_key_name.size)) { ret = GNUTLS_E_DECRYPTION_FAILED; goto cleanup; } /* Check the integrity of ticket */ ret = digest_ticket(&stek_mac_key, &ticket, cmac); if (ret < 0) { gnutls_assert(); goto cleanup; } if (memcmp(ticket.mac, cmac, TICKET_MAC_SIZE)) { ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); goto cleanup; } if (ticket.encrypted_state_len % TICKET_BLOCK_SIZE != 0) { ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); goto cleanup; } /* Decrypt encrypted_state */ IV.data = ticket.IV; IV.size = TICKET_IV_SIZE; ret = _gnutls_cipher_init(&cipher_hd, cipher_to_entry(TICKET_CIPHER), &stek_cipher_key, &IV, 0); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = _gnutls_cipher_decrypt(&cipher_hd, ticket.encrypted_state, ticket.encrypted_state_len); if (ret < 0) { gnutls_assert(); goto cleanup2; } state->data = ticket.encrypted_state; state->size = ticket.encrypted_state_len; ticket.encrypted_state = NULL; ret = 0; cleanup2: _gnutls_cipher_deinit(&cipher_hd); cleanup: deinit_ticket(&ticket); return ret; }
/* 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) 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 (¶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; }
static int decrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv, struct ticket_st *ticket) { cipher_hd_st cipher_hd; gnutls_datum_t key, IV, state, mac_secret; uint8_t cmac[MAC_SIZE]; time_t timestamp = gnutls_time(0); int ret; /* Check the integrity of ticket */ mac_secret.data = (void *) &priv->key[MAC_SECRET_POS]; mac_secret.size = MAC_SECRET_SIZE; ret = digest_ticket(&mac_secret, ticket, cmac); if (ret < 0) return gnutls_assert_val(ret); if (memcmp(ticket->mac, cmac, MAC_SIZE)) return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); if (ticket->encrypted_state_len % BLOCK_SIZE != 0) return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); /* Decrypt encrypted_state */ key.data = (void *) &priv->key[KEY_POS]; key.size = CIPHER_KEY_SIZE; IV.data = ticket->IV; IV.size = IV_SIZE; ret = _gnutls_cipher_init(&cipher_hd, cipher_to_entry(CIPHER), &key, &IV, 0); if (ret < 0) { gnutls_assert(); return ret; } ret = _gnutls_cipher_decrypt(&cipher_hd, ticket->encrypted_state, ticket->encrypted_state_len); if (ret < 0) { gnutls_assert(); goto cleanup; } /* Unpack security parameters. */ state.data = ticket->encrypted_state; state.size = ticket->encrypted_state_len; ret = _gnutls_session_unpack(session, &state); if (ret < 0) { gnutls_assert(); goto cleanup; } if (timestamp - session->internals.resumed_security_parameters.timestamp > session->internals.expire_time || session->internals.resumed_security_parameters.timestamp > timestamp) { gnutls_assert(); ret = GNUTLS_E_EXPIRED; goto cleanup; } ret = _gnutls_check_resumed_params(session); if (ret < 0) { gnutls_assert(); goto cleanup; } session->internals.resumed = RESUME_TRUE; ret = 0; cleanup: _gnutls_cipher_deinit(&cipher_hd); return ret; }