Thanks to @SEJPM's great answer, I spent some time understanding how it works, and here is a transcript of the workflow in pseudo-code (language-agnostic):
dek = 16 bytes random # fixed once forever
# encrypt the data
nonce0 = 16 random bytes
ciphertext, tag = AES_GCM_cipher(dek, nonce0).encrypt(plaintext)
save to disk:
nonce0 | ciphertext | tag
This is case for the Fundamental theorem of software engineering which states:
"We can solve any problem by introducing an extra level of indirection."
In this case what is usually done is introducing an extra randomly drawn intermediate key (usually called "data encryption key, DEK").
You then encrypt the DEK with a key derived from ...
One serious about password-based cryptography must not walk away from KDF_PBKDF2, but run to Argon2 (or scrypt if it's more readily available)! Bitcoin mining has (in 2020) shown that dedicated ASICs performing SHA-256 at high rate (220 TH/s) and efficiency (15 pJ/H) can become commercially available for few thousand US dollars. This makes it untenable to ...
What I was thinking so far is as follows:
When user first logs into app, input their password along with a salt (stored in a database) to a key-derivation function to generate a
Use this hash to encrypt an AES key
Store this on a database
Firstly, make sure that the user's password has good strength. In general, this is the weakest point even there ...
What you've proposed is simply a "CTR-then-ECB" over the plaintext. Just a composition of two common cipher modes.
This "double cipher" scheme will fail IND-CPA from your reuse of IV's as the encryption oracle in this case must not only take an arbitrary attacker's "messages" but also use whatever "IV's" they choose to ...