So the goal is to use AES-CBC-256 to encrypt the data in a file. To derive the AES key and IV pbkdf2 will be used to generate a 384 bit output using a given password and salt, where the first 256 bits will be the key and the last 128 will be the IV. The key and iv will be used to encrypt the file and then a hash of the key will be stored at the beginning of the encrypted file so that when decryption happens, the hash of the supplied key will be compared to check whether the correct password has been supplied. Is this a secure method?
No, you should use Authenticated Encryption or AEAD.
Generate another 128 bits from the KDF, use that as the key to HMAC-SHA-256, compute HMAC of IV and ciphertext, store the HMAC result (called authentication tag) with the ciphertext.
When decrypting, pass the password through KDF to generate IV and HMAC key, compute HMAC over IV and ciphertext, compare with stored authentication tag. If they don't match, the password was bad or the ciphertext was tampered with. Do not decrypt. Decrypt only if the authentication tag is good.
CBC is slow, because it has to work one block at a time and cannot encrypt blocks in parallel. CTR would be better.
HMAC is a strong MAC but is slow.
Modern AEAD modes like AES-GCM or even ChaCha20-Poly1305 (the only modes allowed in TLS1.3 and libsodium, preferred in modern SSH and IPSec, with ChaPoly being the only mode allowed in age and WireGuard and is the default in libsodium) are faster than AES-CBC or AES-CBC+HMAC.
Using age would be much better than rolling your own. For example, age uses modern chunked AEADs and scrypt, has a go and rust implementation, and was created by a reputable person (the maintainer of go's standard library's crypto code).
Your method is somewhat similar to to the method used by
openssl for aes-256-cbc encryption, with a key derived from a password by pbkdf2, e.g.:
openssl aes-256-cbc -e -a -salt -pbkdf2 -iter 10000
As in your method, the pbkdf2 function in the
openssl command above derives a 348-bit key from the password, then this is split into a 256-bit encryption key and a 128-bit iv. However, you'll need to store the salt used by the pbkdf2 function somewhere (in the
openssl command above, the salt is stored in bytes 8-15 in the cyphertext). Also, openssl does not store a hash of the key with the ciphertext. However, incorrect passwords/keys at decryption often produce padding errors, so this can be used to detect incorrect passwords/keys in some cases.
For more information on how to detect incorrect passwords/keys, see https://crypto.stackexchange.com/questions/5921/how-and-why-can-a-decryption-program-tell-me-that-a-key-is-incorrect.
For more information on how
openssl aes-256-cbc -e -a -salt -pbkdf2 -iter 10000 works 'under the hood', see See https://crypto.stackexchange.com/questions/3298/is-there-a-standard-for-openssl-interoperable-aes-encryption