Add Yubikey 2FA for unlocking databases#127
Conversation
|
I'm glad you managed to rebase it, but I'm not a fan of how all the commits in keepassx/keepassx#52 are squished into one. There was information about the design and implementation of the feature in those messages. Thoughts? What was your reasoning for rebasing the branch instead of merging the current |
|
@Thynix good call. Would be nice to preserve the commit history. There isn't much of a reason not to other then rushing things, and rushing things with a password manager seems reckless. |
|
@kylemanna @Thynix the thing is that previous commit were done on a different repo, and now that we are forked the codebase is different and the old commit can have some conflict. Edit: I've just tried: Edit2: I'm solving the conflict, can you reply to this? @kylemanna https://github.com/keepassx/keepassx/pull/52/files#r94312692 |
The previous commits were done against the upstream keepassx repository, which should have the same ancestors as all forks. So, that's a relatively moot point. The real problem is that the patch set has sat on the sidelines and never got merged. As a result, after a few years I stopped rebasing it myself since it was apparent it would never get merged and I didn't want to half ass rebase it and risk corrupting password files for the users that stumbled upon and use my fork. So, sadly it has sat frozen in time. But, with the new found attention for the this pull request, let me see what I can do in the next few days to bring this back up to speed. :) |
|
@kylemanna Now KeePassXC repo is detached from KeePassX one, we have added other features and fixed things. This PR is a rebase with fixed conflict on our develop branch. Yes, the original PR's creator (#119) squished all the commit and I agree is bad. I've checked manually all the code from this PR and from yours and (a part from minor changes) it's the same code. I think the commit history is a small problem and that we can get over it, focusing instead on the
|
I think we're on different pages and the direction of this project seems much different then one I'm interested in supporting. |
I've just stated my personal opinion about it. I'm not even the one who squished the commit history so there is no need to be "rude" at me. I'm trying to understanding your rejection to us. We are maintaining this tool for hobby, no one pays us, we dedicate most of our free time to this project for the community, we are very active and we listen and reply to every single issue. |
|
@kylemanna, I want to make sure we are working closely with the individuals (such as yourself) who put significant effort into previous submissions to the original KeePassX project and were previously ignored. It is our intention to preserve the integrity and trustworthiness of the code being submitted and merged into the baseline. To that end I would like to know what you do not agree with so that we can make corrections to our process where needed. Your orignal work must be preserved for both credit and posterity. @TheZ3ro, the commits must be unsquished. |
|
Fine by me. You guys can re-do the merge and submit a new PR. I'm closing this |
There was a problem hiding this comment.
Well, not so fast.
I agree we can't simply squash commits in PRs. IMHO we should generally just merge the full history. It's both documentation of what was done and credit for the work people did. We want everyone to contribute and give them the credit they deserve. People's commits should be visible and count towards their GitHub profile. This is true for contributions of any size.
This branch should be re-merged or re-rebased with the full history intact. However, I would prefer to keep this open and force-push the unsquashed branch since we have had some discussion here already.
Besides that I had a first read through the code and made some annotations. Please check.
src/keys/CompositeKey.cpp
Outdated
| clear(); | ||
|
|
||
| for (const Key* subKey : asConst(key.m_keys)) { | ||
| Q_FOREACH (const Key* subKey, key.m_keys) { |
There was a problem hiding this comment.
Any reason why you went back to Q_FOREACH over range-based for with qAsConst()? -> https://doc.qt.io/qt-5/qtglobal.html#qAsConst
Question also applies to following uses of Q_FOREACH.
There was a problem hiding this comment.
Duplicate of the previous PR: #119 (review)
There was a problem hiding this comment.
Alright. But don't forget to use qAsConst() to avoid detaching and unwanted deep copies.
src/keys/YkChallengeResponseKey.cpp
Outdated
| return true; | ||
| } | ||
|
|
||
| /* If challenge failed, retry to detect YubiKeys int the event the YubiKey |
src/keys/YkChallengeResponseKey.cpp
Outdated
| QString YkChallengeResponseKey::getName() const | ||
| { | ||
| unsigned int serial; | ||
| QString fmt("YubiKey[%1] Challenge Response - Slot %2 - %3"); |
There was a problem hiding this comment.
Should probably be translatable since it appears in the GUI.
src/keys/YkChallengeResponseKey.cpp
Outdated
|
|
||
| return fmt.arg(QString::number(serial), | ||
| QString::number(m_slot), | ||
| (m_blocking) ? "Press" : "Passive"); |
src/keys/drivers/YubiKey.cpp
Outdated
| YubiKey* YubiKey::m_instance(Q_NULLPTR); | ||
|
|
||
| /** | ||
| * @brief YubiKey::instance - get instance of singleton |
There was a problem hiding this comment.
Thumbs up for API comments 👍, but they should probably go in the header file.
8c21b6a to
7350972
Compare
|
Alright, ladies and gents, I rebased Kyle's branch with all commits intact. It was a bit messy because upstream changes were merged in rather than rebased upon, but I got it working. Please test. I haven't yet changed any of the things I (or @TheZ3ro) annotated during code review. That still needs to be done. There are also two annoying bugs which should be fixed as well before this can be merged:
|
|
Added the 2 new bug to the TODO list in the first comment And I forgot, Nice work @phoerious |
* Add initial header file for forthcoming challenge response support. * A ChallengeResponseKey operates by submitting some challenge data and getting a deterministic result. * In the case of the forthcoming YubiKey integration, the master seed is submitted as the challenge to the YubiKey hardware and the YubiKey returns a HMAC-SHA1 response. Signed-off-by: Kyle Manna <[email protected]>
* Each Challenge Response Key consists of a list of regular keys and now challenge response keys. * Copy ChallengeResponseKeys when copying the object. * Challenge consists of challenging each driver in the list and hashing the concatenated data result using SHA256. Signed-off-by: Kyle Manna <[email protected]>
* Pass the master seed from the database to CompositeKey::challenge() function which will in turn issue challenges to all selected drivers. Signed-off-by: Kyle Manna <[email protected]>
* The challengeMasterSeed() function return empty if not present maintaining backwards compatability. * This commit is where the challenge response result is computed into the final key used to encrypt or decrypt the database. Signed-off-by: Kyle Manna <[email protected]>
* Use compile time detection of the YubiKey libraries and link against
the libraries if present. Can be disabled with:
$ cmake -DCMAKE_DISABLE_FIND_PACKAGE_YubiKey=FALSE
* A stub file provides empty calls for all the function calls integrated
in to the UI to support this. In the future a more modular approach
maybe better, but opting for simplicity initially.
Signed-off-by: Kyle Manna <[email protected]>
* Implement a YubiKey challenge response class. One object will be created for each challenge response key available. Signed-off-by: Kyle Manna <[email protected]>
* Basic testing for YubiKey code. Signed-off-by: Kyle Manna <[email protected]>
* Add YubiKey support to the GUI widgets. Signed-off-by: Kyle Manna <[email protected]>
* If a removed Yubikey is to blame, re-inserting the Yubikey won't resolve the issue. Hot plug isn't supported at this point. * The caller should detect the error and cancel the database write. Signed-off-by: Kyle Manna <[email protected]>
* Attempt one retry in the event the event the device was removed and re-inserted. Signed-off-by: Kyle Manna <[email protected]>
* This was bugging me. Oops. * No functional changes. Signed-off-by: Kyle Manna <[email protected]>
|
Is this working on MacOS (El Capitan) as well? No matter what i try, installing from dmg or building myself, i am not able to choose a Yubikey based challenge response. Even though Yubikey is listed as extension. Do you have any insights? |
|
I suspect you have the 2.1.4 version. Yubikey support requires 2.2.0 which hasn't been released yet. |
|
You are correct. Sorry for not mentioning the version number, it was quite late already. Is there an approximate timeframe, when 2.2.0 is going to be released? Or could I build it myself? |
|
You can build the develop branch. We do not have a definite release date for 2.2.0 yet, but it won't be too long. |
- Added YubiKey 2FA integration for unlocking databases [#127] - Added TOTP support [#519] - Added CSV import tool [#146, #490] - Added KeePassXC CLI tool [#254] - Added diceware password generator [#373] - Added support for entry references [#370, #378] - Added support for Twofish encryption [#167] - Enabled DEP and ASLR for in-memory protection [#371] - Enabled single instance mode [#510] - Enabled portable mode [#645] - Enabled database lock on screensaver and session lock [#545] - Redesigned welcome screen with common features and recent databases [#292] - Multiple updates to search behavior [#168, #213, #374, #471, #603, #654] - Added auto-type fields {CLEARFIELD}, {SPACE}, {{}, {}} [#267, #427, #480] - Fixed auto-type errors on Linux [#550] - Prompt user prior to executing a cmd:// URL [#235] - Entry attributes can be protected (hidden) [#220] - Added extended ascii to password generator [#538] - Added new database icon to toolbar [#289] - Added context menu entry to empty recycle bin in databases [#520] - Added "apply" button to entry and group edit windows [#624] - Added macOS tray icon and enabled minimize on close [#583] - Fixed issues with unclean shutdowns [#170, #580] - Changed keyboard shortcut to create new database to CTRL+SHIFT+N [#515] - Compare window title to entry URLs [#556] - Implemented inline error messages [#162] - Ignore group expansion and other minor changes when making database "dirty" [#464] - Updated license and copyright information on souce files [#632] - Added contributors list to about dialog [#629]
|
I'd like to correct a few misconceptions about U2F that have been uttered here. A good start might be to read https://www.yubico.com/about/background/fido/ for those interested. U2F 2.0 will be supported as part of Windows Hello, a sort of authentication replacement for smartcards. Briefly, nothing about U2F requires online components, and that is why it can and is used for things like (offline) ssh private key authentication. It's basically an easy to use UX around 2 factor, where the master key never leaves the device or can even be read (without cracking the physical key open I suppose). Also, they're the cheapest 2 factor hardware around, which is another reason I hope it's inclusion into KeePaasXC might be reconsidered. In my view it's a perfect fit, and it solves 2 factor for those that are not familiar with TOTP or (not underservedly so imho) consider it not an ideal UX. It comes closest to that real world analog that is so readily understood: the hardware token is a key and inserting it unlocks your website/program/etc. I don't know if it is any more or less safe than TOTP, but I do know that U2F keys I have been able to 'sell' (cheap+ easy to use UX) where TOTP proved bothersome. The fact that one U2F key can't be backup up is usually circumvented by adding multiple keys. |
|
The problem is that U2F devices maintain a counter, so the same challenge doesn't yield the same response when we issue it twice, but we need a deterministic response in order to decrypt the database. The counter is there to prevent device cloning (see https://developers.yubico.com/U2F/Protocol_details/Overview.html#_4_device_cloning_detection), but makes the protocol useless for our purpose. The usual way to allow OTP-based authentication for offline crypto vaults is to pre-calculate a series of OTPs and use them to encrypt a secret which is then used to decrypt the actual crypto vault. But that has a few disadvantages:
This, however still assumes a deterministic response. The fact that U2F generates a signature from a dynamic component (i.e., the counter), complicates this a lot. We cannot pre-calculate the signature (since we don't know the device's secret) and we don't even know how the counter will increase. It could increase by one, but it could also increase by a 1000. The only requirement is that it is larger than the last time we saw it. All that considered, I don't see a way to make U2F work for KeePassXC. SSH authentication and WinLogon are totally different scenarios. |
|
Hmm, I was not aware of that counter, thanks for the pointers. How is that handled by current implementers of U2F, because the key can be used with multiple services simultaneously. Personally, unlocking my keepass db is a very similar scenario to unlocking my computer/ssh-agent: I want access to something secured. |
|
The device generates a signature using public key cryptography and returns both the counter and the signature. An online service (or your PC in case of SSH login/pam_u2f) can easily verify the signature that way, but we cannot use it as an auxiliary encryption key. |
|
You mean it can easily verify that because it can easily store the previous counter number? Online services don't share their counter, yet may share the public key. In that sense, online or offline does not seem to make the difference. |
|
A service can store the previous counter (or just ignore it, doesn't matter here), but more importantly, it can verify the signature using the public key. But signature verification isn't the same thing as using the signature to strengthen your encryption key. Both A and B (with A != B) may be valid signatures for a message M (and a variable, but known counter c), but to strengthen our encryption key, we need need A and B to always be the same if M stays the same, because in the end we need a fixed byte sequence that goes into our encryption key. That's were online and offline differ. |
|
I think it's dawning on me: in fact it is where authentication and encryption differs. Right? Keepass not only needs authentication, but also a key to encrypt, or some combo, and U2F does not (seem to) provide that, since it's different every time. Useful for authentication (which is why PAM and Hello can use it), encryption not so much. |
|
Yes. The counter basically modifies the challenge (i.e., the message). You send a message M to the device and it generates a signature sig(M, c), where c is the counter, and then returns both the signature and the counter. So the service can now append M and c and check if the signature is valid for (M, c) and then let you pass or not (authentication). But a password vault does not have such an authentication scheme. All it has is a fixed encryption key, so we cannot respond to the counter. If we send a challenge M and get a signature sig(M, c), which will always be different as c is incremented, we cannot use it to decrypt the database. |
|
Thanks for helping me understand! Keep up the good work! |
|
I'm running MacOS X |
|
You need to configure at least one slot to provide HMAC-SHA1. |
|
@tristan-k @phoerious this should be a FAQ somewhere, I had the same problem and eventually figured it out. The solution is to use the official YubiKey Personalization Tool |
|
Hi everyone, If you need encryption and authentication for the database, why don't you support CCID as well? |
|
I couldn't find a quick protocol overview for CCID, but it will either have the same problems as U2F (please read my other answers for that) or it will serve the same purpose as HMAC-SHA1 to add entropy to the master key (in which case it would be rather redundant). The usual way to integrate smart cards, however, is to let them handle the actual encryption (not sure if that is what you meant). In that case, though, I would rather prefer using GnuPG's gpg-agent for that, which optionally allows you to use smart card devices for encryption. But that is an issue related to #440 and #278, not this one. |
|
if I understand this feature correctly, when using a Yubikey, the database uses the master-password plus a random seed, then passed through the yubikey challenge-response, and is encrypted with the Yubikey response. |
I could not find anything to confirm that keepassx supports hardware keys. And as mentioned by @rusty-snake[1]: > The yubikey support in kpxc seems to be based on > https://github.com/kylemanna/keepassx / > keepassx/keepassx#52 > which was never merged. For me it looks like kpx never got official > support for it. > > keepass seems to support hw keys (via plugin). Also of note is the PR that added yubikey support to keepassxc: keepassxreboot/keepassxc#127 This partially reverts commit 09ac1a7 ("keepass*: remove nou2f", 2022-02-05) / PR netblue30#4903. See also commit 91b0417 ("keepass*: fix typo in private-dev note", 2022-02-06). Closes netblue30#4883. [1] netblue30#4883 (comment)

Description
This just just keepassx/keepassx#52 rebased against keepassxc. All glory belongs to Kyle Manna
This is a merge in
feature/yubikeyfrom #119@johseg you can add commit by pushing to
feature/yubikeybranchThings to do:
Q_FOREACHwith C++11forRebased 2FA code by Kyle Manna #119 (review)Q_DECL_OVERRIDEwith C++11overrideSee this PR for more #119
Motivation and Context
Adds 2FA with yubikeys to keepassxc
Fixes #26
How Has This Been Tested?
I've been using his fork for a while. This here is pretty much untested, I use it since today ...
Screenshots (if appropriate):
Types of changes
Checklist: