I have just recently learned about OtpKeyProv as a way to add 2FA to the KeePass database based on RFC4226 HOTP.
Reading the RFC it is clear to me how in a client-server infrastructure with a shared secret (known to both sides) HOTP can add an additional layer of security: Using SHA-1 as non-reversible function, both sides can calculate the OTP and verify that the other party is in posession of the secret key.
In my understanding, the idea behind an additional key provider Plugin is to derive (or be able to decrypt) a key or an additional fragment of a key based on user input. That key can then decrypt the master secret that is used to encrypt the password database.
However, in case of HOTP, there is no way to derive the secret (in HOTP context also called the "seed") from the user input - which is a good thing since this is the whole purpose of HOTP. So OtpKeyProv already needs to have a key which it somehow "unlocks" using the (based on an irreversible mathematical function and always changing ) user input.
I just can't wrap my mind around how OtpKeyProv does its magic - is there some documentation how OtpKeyProv works including security considerations? The readme that comes with the plugin does not give any hint.
Thanks
Alex
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
When you set up OptKeyProv you enter a secret. That secret is used to derive the KeePass master key, which is why you need to keep it secret. Entering the OTPs allows the plug-in to generate the secret.
I think using OTPs is too much hassle given the other mechanisms for extracting passwords from KeePass / browser / machine. You are better off ensuring your machine is not compromised than worrying about OTPs.
cheers, Paul
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Entering the OTPs allows the plug-in to generate the secret.
No offence, but this is like saying "some magic happens".
The OTPs are derived from some secret S using an unidirectional function. It is impossible to reconstruct S mathematically from any number of OTPs. That's the purpose of the whole algorithm described in the RFC.
All that a program can do is to check entered OTPs against the secret S which has to be known beforehand (together with the current state of the counter). Without S, the OTPs can not be distinguished from random numbers.
Therefore, for OtpKeyProv to do anything meaningful with the entered OTPs, it needs to have access to the secret indepently from the entered OTPs. It would check the OTPs' validity and only if they are valid, would programmatically decide to use S to derive the master key.
If someone were to create a patched version of OtpKeyProv where "if(isvalid(otps[]))" is exchanged with "if(true)" (I know I am oversimplifying this, but I think it gets the point across), the protection of all databases that exclusively use OtpKeyProv would be annulled.
I might be wrong with all of this, but as long as there is no proper documentation on how OtpKeyProv actually works, it must be considered snakeoil IMHO.
Cheers
Alex
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Ok, found some useful information at https://sourceforge.net/p/keepass/discussion/329220/thread/8b1d33f7/#af0d but it still lacks some details.
My guess is that the secret key is encrypted using the next three (if OTP count is 3 and lookahead is 0) unused OTPs. Once the secret key is sucessfuly decrypted, the next OTPs (which are the ones that the user will enter the next time) are calculated and the key is encrypted again.
If lookehead is >0, then multiple versions of the encrypted key are prepared.
Still a lot of guessing though. Confidence in the Plugin would be greater if the process were more transparent so its security could be reviewed independently.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
That is pretty much correct. The secret that you share with your authenticator app is saved in the dbname.otp.xml file, encrypted using the next set(s) of OTPs (the number of sets depends on lookahead value). When you generate that next set of OTPs using your authenticator app, OtpKeyProv will use them to decrypt the shared secret and supply it to KeePass to use as the key file secret portion of the database master key. Only after the database is successfully opened, will the OtpKeyProv plugin generate the next set(s) of OTPs and create a new dbname.otp.xml file containing the key file secret encrypted with the next OTPs.
The difficulty with using a lookahead value of 0 is that only one set of OTPs will open your database. However, once your authenticator app generates a set of OTPs it won't repeat them. If you generate the correct OTPs but don't successfully open the database, the next time you try to open the database, the OTPs generated by authenticator app won't be able to decrypt the dbname.otp.xml file. Once this occurs, the only way to recover the database is using the shared secret, which hopefully has been backed up. Choosing an unrealistically low lookahead value is a best a nuisance. It could result in complete loss of your password database.
As Paul alluded to before, the overall process is complicated and unless you have special circumstances the benefit probably doesn't outweigh the inconvenience of using a complex master key entry mechanism with its accompanying risk of permanent loss of your data due to user error.
Last edit: wellread1 2018-03-08
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thanks wellread1, that clarifies it for me!
IMHO the OTP secret should be re-encrypted once it could be successfully decrypted and not only when the database was successfully opened, but that's a minor detail
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
the OTP secret should be re-encrypted once it could be successfully decrypted
Actually, I am not certain of the exact sequence of events but it would be easy enough to determine the sequence by testing. In any event, that is not a complete solution becasue a typo entering the OTPs (one of the most likely errors) would expend the OTPs but would not decrypt the dbname.otp.xml file.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi,
I have just recently learned about OtpKeyProv as a way to add 2FA to the KeePass database based on RFC4226 HOTP.
Reading the RFC it is clear to me how in a client-server infrastructure with a shared secret (known to both sides) HOTP can add an additional layer of security: Using SHA-1 as non-reversible function, both sides can calculate the OTP and verify that the other party is in posession of the secret key.
In my understanding, the idea behind an additional key provider Plugin is to derive (or be able to decrypt) a key or an additional fragment of a key based on user input. That key can then decrypt the master secret that is used to encrypt the password database.
However, in case of HOTP, there is no way to derive the secret (in HOTP context also called the "seed") from the user input - which is a good thing since this is the whole purpose of HOTP. So OtpKeyProv already needs to have a key which it somehow "unlocks" using the (based on an irreversible mathematical function and always changing ) user input.
I just can't wrap my mind around how OtpKeyProv does its magic - is there some documentation how OtpKeyProv works including security considerations? The readme that comes with the plugin does not give any hint.
Thanks
Alex
PS: I tried to understand the source code but had to give up...
When you set up OptKeyProv you enter a secret. That secret is used to derive the KeePass master key, which is why you need to keep it secret. Entering the OTPs allows the plug-in to generate the secret.
I think using OTPs is too much hassle given the other mechanisms for extracting passwords from KeePass / browser / machine. You are better off ensuring your machine is not compromised than worrying about OTPs.
cheers, Paul
Hi!
No offence, but this is like saying "some magic happens".
The OTPs are derived from some secret S using an unidirectional function. It is impossible to reconstruct S mathematically from any number of OTPs. That's the purpose of the whole algorithm described in the RFC.
All that a program can do is to check entered OTPs against the secret S which has to be known beforehand (together with the current state of the counter). Without S, the OTPs can not be distinguished from random numbers.
Therefore, for OtpKeyProv to do anything meaningful with the entered OTPs, it needs to have access to the secret indepently from the entered OTPs. It would check the OTPs' validity and only if they are valid, would programmatically decide to use S to derive the master key.
If someone were to create a patched version of OtpKeyProv where "if(isvalid(otps[]))" is exchanged with "if(true)" (I know I am oversimplifying this, but I think it gets the point across), the protection of all databases that exclusively use OtpKeyProv would be annulled.
I might be wrong with all of this, but as long as there is no proper documentation on how OtpKeyProv actually works, it must be considered snakeoil IMHO.
Cheers
Alex
Ok, found some useful information at https://sourceforge.net/p/keepass/discussion/329220/thread/8b1d33f7/#af0d but it still lacks some details.
My guess is that the secret key is encrypted using the next three (if OTP count is 3 and lookahead is 0) unused OTPs. Once the secret key is sucessfuly decrypted, the next OTPs (which are the ones that the user will enter the next time) are calculated and the key is encrypted again.
If lookehead is >0, then multiple versions of the encrypted key are prepared.
Still a lot of guessing though. Confidence in the Plugin would be greater if the process were more transparent so its security could be reviewed independently.
That is pretty much correct. The secret that you share with your authenticator app is saved in the dbname.otp.xml file, encrypted using the next set(s) of OTPs (the number of sets depends on lookahead value). When you generate that next set of OTPs using your authenticator app, OtpKeyProv will use them to decrypt the shared secret and supply it to KeePass to use as the key file secret portion of the database master key. Only after the database is successfully opened, will the OtpKeyProv plugin generate the next set(s) of OTPs and create a new dbname.otp.xml file containing the key file secret encrypted with the next OTPs.
The difficulty with using a lookahead value of 0 is that only one set of OTPs will open your database. However, once your authenticator app generates a set of OTPs it won't repeat them. If you generate the correct OTPs but don't successfully open the database, the next time you try to open the database, the OTPs generated by authenticator app won't be able to decrypt the dbname.otp.xml file. Once this occurs, the only way to recover the database is using the shared secret, which hopefully has been backed up. Choosing an unrealistically low lookahead value is a best a nuisance. It could result in complete loss of your password database.
As Paul alluded to before, the overall process is complicated and unless you have special circumstances the benefit probably doesn't outweigh the inconvenience of using a complex master key entry mechanism with its accompanying risk of permanent loss of your data due to user error.
Last edit: wellread1 2018-03-08
Thanks wellread1, that clarifies it for me!
IMHO the OTP secret should be re-encrypted once it could be successfully decrypted and not only when the database was successfully opened, but that's a minor detail
Actually, I am not certain of the exact sequence of events but it would be easy enough to determine the sequence by testing. In any event, that is not a complete solution becasue a typo entering the OTPs (one of the most likely errors) would expend the OTPs but would not decrypt the dbname.otp.xml file.