Dave here, with a plan for storing secrets.
Threat model:
- Database and keyfile may (will?) be stored on un-trusted servers.
- Database contains many valuable secrets (passwords), encrypted by key stored in keyfile.
- Source code is public.
- Keyfile and database SHOULD never be stored on the same server, but will always need to be accessible from the client.
Out-of-scope threats:
keylogger, malware, rubber hose, chocolate
Failure mode:
Attacker obtains a copy of both keyfile and database and derives the encrypted key by attacking the keyfile.
Defenses:
- keyfile is encrypted with a key derived from a password.
- prototype uses bcrypt as the key derivation function.
- The high-entropy master password is selected randomly (diceware).
- the keyfile has no visible signature/structure -- someone who happens upon it will be unable to distinguish it from from random noise. This is not a big deal, but it adds plausible deniability.
Details
What I am most interested in is whether I am botching the keyfile encryption. The prototype (see https://github.com/scholarly/pynacl/blob/master/examples/bcrypt_wrap.py) is written in python using pynacl and py-bcrypt.
workfactor: 1 byte, default: 8
nonce: 24 random bytes
salt: 32 random bytes
key: 32 random bytes
magic = 24 bytes of sha1(salt+MAGIC+workfactor) [optional, looks random to casual observer]
kek = bcrypt.kdf(password,salt,key_bytes=32,rounds=1<<workfactor)
ciphertext = nacl.secret.SecretBox(kek).encrypt(key,nonce)
keyfile = magic + salt + ciphertext
Questions
- Is this an appropriate way to use bcrypt.kdf? (which seems to be PBKDF2withBcryptHash. I don't know if this is a standard implementation or only used in py-bcrypt)
- Is there a problem with using SecretBox instead of a specialized key wrapping cipher?