keechallenge
keechallenge copied to clipboard
Improve security with rolling Challenge-Response, HMAC into Scrypt function
Considering a brute force threat model I would advice for the following changes:
- Do not store the symmetric key, ever.
- Use the output of the CR from the YubiKey into a DK = scrypt(Passphrase,Salt,N,p,dkLen); Use the user's password as passprhase and the CR output in Salt. If you use PBKDF2 use a high value for rounds.
- Save the new challenge, re-encrypt and save the password database
Whilst a great idea from a security POV, and please correct me if I'm wrong, but don't systems like this have a chance to go out of sync between multiple databases (Similar to OtpKeyProv's problem?).
@TomAtYubico Would be great to hear your response.
OATH-HOTP based solution may go out of sync because of the counter. HMAC-SHA1(K,C) if C is incremented on the device side (e.g. pressing device button multiple times) the back-end may desync (according to counter-window size/configuration, etc)
CR wont go out of sync if you keep tracks of challenge being sent. If you have multiple databases, you have to keep track of each challenge per database. Given that a user might have 1-3 databases you could probably use the same challenge without breaking everything.
Has this been followed up?
@TomAtYubico I am considering making a fork of this, I know its been a few months but could we possibly talk about the best ways to secure this ? Ideally I would like to avoid any syncing issues possibly by storing the records in the KDBX
It is a good idea to improve the plugin.
Can you outline your approach to using the YubiKey with KeePass for securing offline password databases? A flow diagram and cryptographic proof or references would be great!
@whinis, do you have an update on this?
I don't have enough background in cryptography but I have forked the code and made a change that autodetects the yubikeys slot based on code from KeePassXC. I plan on working on the storage into the database instead of the xml as well.
@TomAtYubico If I understand what you are suggesting I store a Challenge C I use C with the Yubikey to get CR I encrypt the database with CR and passphrase Next time I open the database I present challenge C and get back CR If successful I generate new challenge C' and store it Reencrypt database with CR'
How do you plan to obtain CR' for re-encrypting the database?
@TomAtYubico I have spent a fair amount of time looking at the KeePass plugin code and if anyone can tell me I am wrong I would love to hear a better solution. However as it stands in order to change the key used each login one would need the plugin to fire off a change master key request which includes the dialog to re-enter a master password. This would need to be requested after the database was decrypted and honestly seems like a major hassle for small gain.
I do agree that the current method of using the key that is suppose to be entirely private to the yubikey as a secret for the database is backwards so I have implemented a change on my branch at https://github.com/whinis/keechallenge that I would like your input on. Rather than store or even ask for the secret key I do the following on creation of the database
- generate a challenge and receive a response from the yubikey
- generate a 64 byte secret that will be used to generate the database key
- SHA256 the response from the yubikey and encrypt the 64 byte secret with this response
- generate a SHA256 of the secret and provide that to KeePass as part of the derivative key
On unlock it will use the saved challenge to get the response, decrypt the secret, and provide the SHA256 to KeePass, optionally if the config value is set it will also generate a new challenge for enhanced security and then request a new response from the yubikey for encrypting the secret with.
Outside of using HMAC-SHA1(K,C) or requiring the user to go through a master key change every time they unlock their database I think this is the best solution but would love your input. @strangeflower This might also answer your questions
Here's a diagram that I created last year in an attempt to understand how Keechallenge handles the secret key. Can you do something similar to outline your approach?
I can later today, the important thing to notice here however is that KeeChallenge never gets access to the MP and therefore would need to ask for another one from user.
I tried KeepassXC and actually like the usability it provides for Yubikeys. On each database change one has to touch the Yubikey to confirm the change, that's it.
It may be useful to check how they did this and what security it provides. I haven't had time for this yet.
Im not sure, I stopped using KeepassXC cause it had poor browser integration and KeeAgent is just a little more convenient than the one they setup
@strangeflower I am not nearly as much of an artists as you are however
Here is my terrible diagram, Important differences is that in the new scheme the YubiKey Secret is never stored or used outside of the yubikey and any needs for a new response causes another user prompt
Is it possible with the current API to provide a new KPS to KeePass when it writes the database? It would be reasonable from user perspective to confirm changes to the data by touching the yubikey. If possible, then the challenge can be changed each time the database is accessed and the secret (and challenge) each time the database is written. Second, to hinder brute-force attacks it makes sense to replace AES function by a computationally intensive function, like @TomAtYubico suggested above.
I would need to look at the api more however my understanding is that all secret changes would require the change password dialog. However replacing the AES function could easily be done plugin side however KeePass side it would still use whatever is selected.
@whinis did anything happen of this?
It seems the author of this plugin has abandoned it, but @TomAtYubico raised some important concerns.
So this presents an interesting challenge, you cannot reencrypt the database in my checking without doing a password change meaning every time you save the database you must input both your normal password as well as yubikey to generate a new challenge greatly reducing usability.
I also asked if it would be possible to get either the passphrase or a derived passphrase from keepass so that the user wouldn't need to type 2 passwords and was told no each plugin need to be entirely separate. There is also no way of storing the Challenge or other information in the unencrypted section of the database removing the need for the xml file.
If we go off of what @TomAtYubico said then using CR we would need to store the challenge in it as well as I assume a AES256 encrypted secret using the response that decrypted would be used as input into keepass. This is doable but its security lessened since to change it requires full input from keepass including main passphrase.
I am unsure what the correct path forward is and what people would like to see nor the security implications of each path.
If we go off of what @TomAtYubico said then using CR we would need to store the challenge in it as well as I assume a AES256 encrypted secret using the response that decrypted would be used as input into keepass. This is doable but its security lessened since to change it requires full input from keepass including main passphrase.
I am unsure what the correct path forward is and what people would like to see nor the security implications of each path.
Hi @whinis, can you explain this a bit more. Are you saying we have two possible implementations, or just one?
I understood that one implementation would require the master password + Yubikey authentication on every save of the database. Is there another possible implementation?
So yes, one implementation would require a master password +yubikey + yubikey password(challenge) every save of the database and has the upside of not requiring a secondary xml file.
A second implementation would still require a xml or at minimum a text file with an encrypted string that is returned by the plugin. I am unsure on the security of storing the challenge in the file as well or if this should also require a password from the user.
Keepass does not allow storing the encrypted string in the unencrypted section of the database in such a way the plugin can access it nor does it allow access to the master password by the plugins. Meaning if we don't want to store the challenge we must prompt the user for it.
So yes, one implementation would require a master password +yubikey + yubikey password(challenge) every save of the database and has the upside of not requiring a secondary xml file.
A second implementation would still require a xml or at minimum a text file with an encrypted string that is returned by the plugin. I am unsure on the security of storing the challenge in the file as well or if this should also require a password from the user.
Keepass does not allow storing the encrypted string in the unencrypted section of the database in such a way the plugin can access it nor does it allow access to the master password by the plugins. Meaning if we don't want to store the challenge we must prompt the user for it.
Considering I use my Yubikey as a supplement to a master password on my database anyways, I would much prefer the first implementation. AFAIK this is how KeepassXC does it.
To quote from their FAQ:
Q: When I use KeeChallenge with KeePass2, it creates an extra file. Why do I have no such file when using KeePassXC?
A: Our implementation differs from how KeeChallenge handles YubiKeys. KeeChallenge uses the HMAC secret directly to enhance the database. To make this work, they need to store the secret in a side-car file, encrypted with the response of a challenge-response pair that is calculated ahead of time. In KeePassXC, we do not require any knowledge of the HMAC secret. We use the database's master key as challenge and then use the response to encrypt the database. That way we do not need an extra file and also gain the advantage that the required response changes every time you save the database, which resembles actual two-factor authentication more closely.
I would love a fork of Keechalenge to emulate this behavior.
Yes, but due to restrictions you would need to enter the password twice or just use the yuibkey without a "master" password
Yes, but due to restrictions you would need to enter the password twice or just use the yuibkey without a "master" password
Ok, I see. So have you already implemented your second implementation idea on your fork? It sounds like the best we will get unless we PR the Keepass plugin api. Side note: where does Keepass host their code?
Technically their source code is at https://keepass.info/download.html
I have not implemented either yet was waiting to see what wins out but I can implement the form to request the "password" for the yubikey in the coming days.
Technically their source code is at https://keepass.info/download.html
I have not implemented either yet was waiting to see what wins out but I can implement the form to request the "password" for the yubikey in the coming days.
I remember looking into this a few months ago, and the people over at the SourceForge repo for KeePass didn't seem interested in implementing Yubikey natively. I hope things change, but they might need some convincing.
I remember looking into this a few months ago, and the people over at the SourceForge repo for KeePass didn't seem interested in implementing Yubikey natively. I hope things change, but they might need some convincing.
I asked previously and they had no interest in providing the master key or any derivative or for storage to get rid of the XML to any plugin
so I have pushed a version to https://github.com/whinis/KeeChallenge that has a form that requires you to input a "challenge" if the XML is not present. Only issue is for some reason versions with the XML have appeared to stop working due to padding reasons and I am still investigating that.
@Mo2000 @Johbii I have made a new issue on my fork at https://github.com/whinis/keechallenge/issues/1 and will publish a plgx for you to test that should work. I would advise against using it for databases you trust currently until some more test and confirm