This is a slightly tough one to explain with my current experience, lacking mainstream terminology. But here goes.

I have an encryption/security model whereby I do not store users plaintext passwords. It's pretty simple actually:

1 - on sign up users plaintext password is hashed with one algorithm, this hash is stored as users password for authentication purposes.

2 - the plaintext password is also hashed another way, this produces the users encryption key encryption password (AES 256) through key derivation.

So on sign in if the hash does not match the users storage password (#1) auth fails and no attempt is made for decryption or secondary hash. Simple.

Problem comes logically where I want to use biometricID for both android and iOS. Biometric auth only returns a boolean while autofilling text fields does return plaintext password saved in iOS keychain & equivalent for android. So I actually need the users plaintext password to create the correct hashes. BUT I'm confused as to how implement biometric auth as well as get users plaintext password on sign in all while maintaining strong security.

So I guess my question becomes this: how do current systems with some sort of encryption model requiring plaintext passwords on sign in use biometric auth to maintain a high degree of security? After all, just about every credit card app and banking app I have uses biometric auth... are they storing plaintext passwords or just storing an initial hash?

EDIT: it has dawned on me that I could use bio-auth as a second layer of security but this still defeats the purpose of bio-auth and actually adds a layer of inconvenience no matter how slight and STILL requires manual or system credential autofill.

2 Answers 2


As you noted in your #2, both iOS and modern Android systems allow for secure local storage. Though the details (and security) differ by platform, and with Android can differ by device. At a minimum there is storage tied to the particular app and can be tied to biometric unlock. You locally store an encrypted version of the password in normal app storage, and then you have the key to decrypt that live in the device's secure storage.

That key is made available upon biometric unlock and used to decrypt the encrypted password. Then the password is used as your existing scheme describes. The mechanisms for iOS and Android differ. On iOS you can actually have the decryption of the password done within the device's Secure Enclave. Indeed, you can have the enclave generate this key and encrypt the password for you on first setup.

A note on different key derivation for the same password

Your overall approach of having an authentication key and an encryption key derived from the same password is good. Your goal seems to be to have the service that is authenticated to never receive secrets that could be used to decrypt the user's data. That is a very good thing to aim for.

There are a couple of things to worry about and a few things that I don't understand. I should add that I helped design the system used by 1Password for this.

What is the authentication process?

I am assuming that the app authenticates to a service under your control. Are you treating the client created "authentication hash" as a password as far as the server is concerned? I am assuming that that is roughly what you are thinking, but you need to spell that out, and make it clear what gets sent to the server and what the server does with it.

The client must control which KDF process is used.

If, say, you use a different salt and different KDF parameters for the two processes, be sure that the server can't get away with sending the parameters for deriving the encryption key when it should be sending the parameters for deriving the authentication key. Presumably, when a new device is enrolled, the client app will need to get salt and parameters from the server, and so that is when a malicious server (or an attacker in the network) could send the parameters intended for deriving the encryption key to the client telling the client that these are for the authentication key. The client would then derive the encryption key and send that over the network.

The solution to this is to hard-code something into the client that makes the key derivation different for the two cases even if they are given the same salt and parameters. If you use, say, HKDF in the process, you can code in different "info strings" for the two cases. That way, even with the decryption salt and parameters, the key that is derived by the authentication KDF will never be the same as what is derived by the encryption KDF.

Make authentication zero-knowledge

As long as you are building a custom authentication scheme, you might has well use a zero knowledge one.

Instead of transmitting the derived authentication hash/key to the server for authentication use it as part of a Password Authenticated Key Exchange (PAKE). This means that an attacker who can listen into to the client/server exchange (or tamper with it) can't learn anything that could be used to replay a login attempt. It also means that the server proves its identity to the client.

Do not use SRP (as we did with 1Password). There are much better PAKEs available know (and more patents have expired). AuCPace would be my first choice for building something today, but OPAQUE is also a good choice (and there may be more libraries for it).

Use a slow/hard KDF for deriving the authentication and encryption keys from the password.

Assuming that people will use weak passwords, you need to plan for what happens if their encrypted data gets captured either from their device or your servers. So you should use something like Argon2, scrypt, bcrypt, or perhaps even PBKDF2 (again, not my first choice, but it is what was available in all of the right places when we designed the 1Password system).

Anyway, that is what occurs to me off of the top of my head late at night.

  • Very good answer, I appreciate input from a high profile security professional. I will consider a PAKE, as for KDF, I am enforcing a strong (but not stupid rules) for password, sources below (including various 1Password sources- thank you) and have not yet decided 100% on my hashing algo but I'm not shy about slow sign in/hashing so it will be strong as possible. My server does the same thing as client with password given from client (in case of server/client compromise) and I will be checking digital sigs on key passing. Plus have implemented sub-TLS hybrid encryption for server-client comms.
    – RobbB
    Oct 6, 2022 at 0:46
  • To prevent MiTM attacks. As well as pre-installation server public key hardcoding. Using the following resources for password gen rules: here and here. Avoiding stupid rules that can help brute forcing such as minimum 1 special char.
    – RobbB
    Oct 6, 2022 at 0:49
  • Worst case scenario I've created a good challenge for some cyber criminal who loves puzzles
    – RobbB
    Oct 6, 2022 at 0:51

POTENTIAL SOLUTION: This solution would not be without its own security risks, and defeat the purpose of not storing the encryption key password derivation hash BUT it could be an option for users who want convenience over security.

The solution would be to:

#1 take users plaintext password on sign up and hash it once.

#2 Store this hash in iOS keychain/keystore android application level.

#3 Hash this hash with a different algorithm, this becomes the users storage hash for authentication only.

#4 Hash the #1/#2 hash with a different algorithm and derive an AES 256 key from this for key encryption.

On sign in with biometric auth, if auth successful the #1/#2 hash is retrieved, hashed twice as per #3 & #4 and then used for actual local & server sign in.

The only vulnerability I see is physical device attacks such as nand attacks. Even without this model, saving user credentials to keychain/keystore for autofill is prone to these attacks. So I'm not sure it's so much of a compromise.

I would like to see some security professionals input on this.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .