I've created an AES implementation in Python as a learning experience (mainly for encrypting/decrypting files), and wanted to make sure that I haven't made any huge mistakes in my logic (of course, implementation is another story).
The block cipher mode of operation is CTR. The implementation supports AES-128, AES-192, and AES-256 (default).
The AES key and HMAC key are generated from a user password using hashlib's pbkdf2_hmac with SHA-256, and a random 16-byte salt (I create a 64-byte key and split it in half for each key; for AES-128, I create a 32-byte key and split it in half).
The CTR IV is created by a random 8 byte nonce and an 8 byte counter that starts at 0. The salt is written as the first block of the ciphertext, followed by the CTR IV as the second block.
Lastly, a 32-byte HMAC value of the ciphertext is created using the HMAC key and SHA-256 and written as the last two blocks of the ciphertext (32 bytes).
When decrypting, the HMAC value is checked against the rest of the ciphertext before decrypting.
Are there any issues with this logic? From a cryptographic point, where are the weak points?