keepassxreboot / keepassxc

KeePassXC is a cross-platform community-driven port of the Windows application “Keepass Password Safe”.
https://keepassxc.org/
Other
21.47k stars 1.48k forks source link

Support FIFO keyfiles and/or piped keyfile to stdin #1210

Open a-dma opened 7 years ago

a-dma commented 7 years ago

What do you think about having the possibility of storing GPG-encrypted keyfiles and have KeepassXC decrypt them on-the-fly when their content is required? This would allow users who store GPG keys on a physical device (OpenPGP Card) to add an extra layer of security in a fairly transparent way. This behavior can be emulated with the CLI once the --key-file argument code lands, but it would still be a bit of a hack.

An alternative could also be to have to possibility of specifying a script or a command to execute in order to produce the content of the keyfile, although this would have quite a few more security implications because of possible side-effects.

Thanks.

a-dma commented 6 years ago

I managed to get this to work, but it's very hacky. Now that --key-file works it is possible to use a gpg-encrypted keyfile. Unfortunately the code reading the keyfile content seeks the file. This means that tricks like <() in bash won't work. On zsh =() should work since it uses a temp file, but I haven't tested it.

The workaround I found to get this to work in bash is to use a here string. An example command for listing the credentials is

(gpg -d key_file.dat.gpg 2>/dev/null >/dev/fd/999; keepassxc-cli ls -k /dev/fd/999 Passwords.kdbx) 999<<<''

Any chance to change the code that reads the keyfile to something that treats it as a stream and buffers it internally? That would make process substitution (and possibly even pipes) work.

TheZ3ro commented 6 years ago

Any chance to change the code that reads the keyfile to something that treats it as a stream and buffers it internally? That would make process substitution (and possibly even pipes) work.

Honestly I don't see much usage other than this specific case. Having a keyfile as file is the standard and so we shall read from a file, at least for core. Maybe cli can implement this

a-dma commented 6 years ago

I agree that having a keyfile as a file is fairly standard, but since keyfiles are typically small in the grand scheme of things, maybe it would be possible to buffer them internally until the database is unlocked.

However, if you do not want to do this, I understand.

TheZ3ro commented 6 years ago

I just noticed that some people already asked about it in #255 and it was a "planned" feature.

I won't close this since it's more specific. I think I need to buy an OpenPGP key now. Some help in testing/developing would be appreciated.

a-dma commented 6 years ago

I've been thinking a bit more about this and came to the conclusion that having a more generic system to produce the content of the keyfile would allow a lot of flexibility. If the keyfile is actually a script that can be run (what interpreter/command is used to do that could be a setting) we could do all sort of things. For example:

Would you guys be in favor of something like this? What are your thoughts?

TheZ3ro commented 6 years ago

I don't really like executing scripts from KeePassXC, and a keyfile isn't really a safe user-provided field (unlike field inside the KDBX, where the user explicitly added it)

We thought about this and I think the choice was to implement a challenge-response against the PGP/GPG agent. This way we can refresh the secret part stored in the KDBX and perform a new C-R every time, a kind of "forward-secrecy" (like we do actually with the yubikey).

This method is a lot safer than plain gpg-encrypted keyfile (that are forever static so no "forward-secrecy" provided) but less "portable"

a-dma commented 6 years ago

I see your point. However, it could still be a user-settable parameter whether to allow this or not. As far as forward-secrecy goes, I don't see keyfiles as being meant to be changed often, but people who care about that can still do it (even today) manually or rely on the YubiKey mechanism. This wouldn't change that.

All in all I still think that is a very good opt-in feature that gives KeePassXC a lot of potential to integrate with many other applications, without having to explicitly write support for it.

Just my two cents.

a-dma commented 4 years ago

I just wanted to update this to say that my earlier solution has stopped working a few updates ago (haven't tried to find out why), but using a named FIFO works. In case somebody find it interesting, this is how to use a gpg-encrypted keyfile.

(fifo=$(mktemp -u); gpg -d /home/user/key.file.gpg 2>/dev/null >$fifo; $(keepassxc --keyfile $fifo --pw-stdin /home/user/databse.kdbx); rm $fifo)

The main downside now is that the FIFO will be unlinked but not deleted until after KeepassXC exits.

As a side note, I still believe that having first-class support for gpg-encrypted keyfiles would be quite cool.

phoerious commented 4 years ago

Pretty sure we're not gonna do that. If we are going to implement PGP, then it won't be via keyfiles, although I am still hoping for PGP to finally die, so it can make way for something else that can actually provide practical and secure message encryption.

netboy3 commented 4 years ago

I just wanted to update this to say that my earlier solution has stopped working a few updates ago (haven't tried to find out why), but using a named FIFO works. In case somebody find it interesting, this is how to use a gpg-encrypted keyfile.

(fifo=$(mktemp -u); gpg -d /home/user/key.file.gpg 2>/dev/null >$fifo; $(keepassxc --keyfile $fifo --pw-stdin /home/user/databse.kdbx); rm $fifo)

The main downside now is that the FIFO will be unlinked but not deleted until after KeepassXC exits.

As a side note, I still believe that having first-class support for gpg-encrypted keyfiles would be quite cool.

Would you care to explain how you made it work with a named pipe? The solution you offer above is not a pipe. You simply decrypt your keyfile into a temporary file, then delete it. There's no pipe or FIFO here.

a-dma commented 4 years ago

Unfortunately it doesn't work with a FIFO. I used to have a call to mkfifo between the first two commands but it doesn't work (probably never has), because of how the keyFile is handled in FileKey.cpp where there are calls to make sure that it is a regular file.

For that reason I went back to a regular file that I manually delete after starting KeepassXC. Not the best solution but better than having the key file sitting unencrypted on the disk.

Relaxing some of the constraints on the type of file that can be used would help with this, but I understand that it opens up for a few tricky-to-reason-about issues and that the developers of this project may not want that.

graealex commented 3 years ago

I also want to add that this would be useful. Obviously not with a plain keyfile, but rather with hardware tokens. It would remove the need of a password function in the database, and instead use the intrinsic PIN to unlock the HW token, and hardware encrypt/decrypt functions of a smart card that implements it.

Right now I'm using Yubikeys as HW tokens, with the challenge-response function as an additional method to secure the password database, which also works on Android via NFC, so it is pretty useful. But having the decryption happening inside the Yubikey itself would still be a much cleaner approach, as the private key would never leave the HW token. At least that's my understanding. In theory, and with the right terminal and smart card, entering the PIN could be completely decoupled from the PC, as it is happening right now with many smart passports. Although the benefit of that approach for a password database is questionable. This makes more sense for signing applications.

droidmonkey commented 3 years ago

Encryption and decryption never happen on smart cards or yubikeys. These devices merely perform cryptographic transformations of passed in data (challenge) to produce a response. The key that performs that operation never leaves the hardware, which provides the security.

graealex commented 3 years ago

Not exactly sure what you're talking about. How can the encryption of data with asymmetric RSA not happen on the HW token, if the private key is never going to leave the HW?

droidmonkey commented 3 years ago

The response forms part of the overall key which is then transformed and used to perform encryption operations. You don't need to give up the private key to do this. Please read up on how these devices work.

graealex commented 3 years ago

It's not like documentation on how smart cards are implementing certain details is plentiful. If you have a pointer to some good documentation, I'd be glad to actually read up on it, because I'm interested in the implementation details.

droidmonkey commented 3 years ago

This is a pretty good high level overview: https://www.thetestspecimen.com/security-keys-yubikey/

If you want to go deeper you'll have to pull up research papers and white papers for specific protocols like PKCS#11, FIDO 2, U2F, etc.

phoerious commented 3 years ago

Think of HMAC-SHA1 as the secret-key equivalent to the PKCS#11 public-key interface. Since we are not verifying the authenticity of the response, it doesn't matter to us if it's actually a valid signature or decryption or just some random string that comes back. The main advantage of a smartcard interface would be the additional PIN you have to enter (and of course the wider compatibility). The encryption itself is not handled by the key.

In theory, a smart card/TPM could encrypt and decrypt the whole payload, but that would be insanely slow. Instead you use it to encrypt a shared secret, which then forms your actual encryption key. In fact, that's generally how PGP works. You never use your private key to actually decrypt your message. RSA, even on a normal computer, is just way too inefficient. Instead, you encrypt a symmetric AES key, which in turn decrypts your payload. That's also how you can encrypt a message for multiple parties. You simply encrypt the same key multiple times with different public keys.

a-dma commented 3 years ago

That is correct and for more details on how that happens in the GnuPG context a good (albeit quite technical) resource is the OpenPGP Card specification https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.4.1.pdf.

One thing that I believe is worth underscoring is the built-in and transparent support for smart cards in GnuPG. If somebody were to have a keyfile encrypted using GnuPG then KeePassXC (given the proper required support) could use GnuPG to decrypt it and use it. If the private key necessary for that decryption operation happens to be on a smart card all the additional complexity is handled by GnuPG "for free".

neonspectra commented 3 years ago

I found this thread while trying to implement GPG as a frontend for authenticating to my keepassxc database in a manner that resembles single-sign-on without an actual identity provider.

It is awful form to write cleartext encryption keys to disk, so I initially tried using a fifo alongside the --key-file option like @a-dma. I ran into the same issue that Keepassxc would refuse to read out from a fifo, and that's how I ran into this thread.

I managed to come up with a very hacky workaround to this, though. My workaround uses the --pw-stdin option instead and works like this:

I really like this idea of signing onto keepass using gpg, and it would be nice for a more "supported" way to authenticate to keepassxc using gpg.

I know a lot of people keep their gpg keyring passphrase within keepass, but that seems like a pretty backward way of doing things to me. gpg is designed to be your one-stop digital identity, authentication, and encryption tool. It makes more sense to use it for primary authentication than it does keepass.

droidmonkey commented 3 years ago

I don't understand the purpose of adding gpg as a middleman. You use a password to unlock your gpg wallet, no? Why not just use that password for your keepassxc database. With argon2 you don't need a ridiculously large password.

neonspectra commented 3 years ago

You use a password to unlock your gpg wallet, no? Why not just use that password for your keepassxc database.

From an architectural and unix philosophy standpoint it makes more sense to give gpg my master password than keepassxc since gpg is a more appropriately scoped tool for the job.

gpg already has streamlined integration into ssh, sending emails, and encrypting files from the command line because the primary purpose of gpg is digital authentication and signature-verifiable cryptography.

Also it's really annoying to make the extra step of digging for my gpg passphrase if i want to encrypt/decrypt a file from the command line or write+use bash scripts that integrate gpg, which I do a lot as part of my workflow.

tl;dr: keepassxc is a password database. It does password database things. gpg does authentication and encryption things. I don't think it makes sense for keepass to overstep the scope of being a database for my passwords and try to be my authentication tool too.

droidmonkey commented 3 years ago

Your use of the password field instead of key file is the solution to this issue. It does not make sense for us to accept a key file from stdin, and there is no functional difference between a key file and a password when it comes to keepass databases. They are both ingested, hashed, and the hash used as key material. I am closing this with your solution as the way forward.

neonspectra commented 3 years ago

Really? My way "works", but there are several issues with doing it my way:

  1. My way is limited to ASCII only key data. You can't use a binary key as a password because you can't copy paste binary data into the password field in your database settings to set this up. This wouldn't be a problem if fifo read support actually worked properly.
  2. You have to create and manage your symmetric key that gpg uses to auth into keepass separately from your actual keepass database. And if you ever want to refresh or update that symmetric key with a new one, you have to redo this same ultra annoying process for setup.

The most correct solution to this issue would be to implement gpg-encrypted keyfiles inline with the keypass database and make a direct call to gpg for decryption, or even streamline the use of gpg subkeys as authentication for keepassxc.

I understand that your time is valuable and maybe your team doesn't want to implement direct gpg support, even though it's pretty standard in the unix world to authenticate to stuff using gpg. That's fine. But let's go back to the original fifo issue that started this thread.

It is poor form to expect a user to decrypt a keyfile to a cleartext file on disk for use with the --keyfile option. There is no way to guarantee secure deletion of any data written to a modern SSD. It is almost 2022 and nobody should be storing cleartext keyfiles, ever. It isn't a problem that keepass provides the option to read cleartext keyfiles, but there should be a general expectation that people using a keyfile are probably doing so because they want to use a third party cryptography solution (like gpg) to manage their master key for keepassxc.

Using a fifo to pass cleartext of an encrypted binary keyfile is the correct way to securely pass such data after being decrypted by a third party program in order to ensure that cleartext does not get written to disk. The fact that keepassxc does not properly read data in this way when the --keyfile command line argument is used is an issue.

It warrants actually being fixed rather than just closing the issue and telling people to do the silly workaround hack that I made up instead.

droidmonkey commented 3 years ago

At the end of the day what is your primary concern? That would be your keepass database is secure and you don't expose your credentials in cleartext. There is no gain in security in using a 32 bit keyfile versus a 20 character ascii password. Why introduce yet more code paths and complicate startup even more when it is unnecessary? Surely you can appreciate streamlined code over supporting every single possible way to setup a database unlock scenario. This issue is 4 years old, it has not been implemented and won't be implemented given the existing capabilities.

The most correct solution to this issue would be to implement gpg-encrypted keyfiles inline with the keypass database and make a direct call to gpg for decryption, or even streamline the use of gpg subkeys as authentication for keepassxc.

This does not work with the keepass database standard.

neonspectra commented 3 years ago

This does not work with the keepass database standard.

Thanks for clarifying. You probably know way more about the database format than I do.

I guess the point that I'm trying to make here is that the workflow for using a gpg encrypted keyfile is less than ideal.

There are multiple ways to approach building out an integration for it, but both of the ways to approach it are hobbled by quirks in keypassxc's command line options:

Surely you can appreciate streamlined code over supporting every single possible way to setup a database unlock scenario.

Streamlined code is code that accepts all possible inputs and handles them in a graceful and expected way.

An mockup of a sensical ideal workflow would be having the ability to do something like this:

All I'm trying to say here is that the solution of generating a string full of random ASCII and copypasting it into the GUI is really hacky.

This isn't the way most people generate keyfiles for stuff. Even though it technically accomplishes the task at hand it isn't intuitive and it certainly isn't a clean solution.

Maybe this isn't a high priority issue at this point, but it's still at the very least a UX issue. I've jumped around a lot and named multiple potential ways to address the UX concerns in this thread (fix fifo read input, allow password set from binary file read, etc). My point is just that there are a lot of different things you could do to make this workflow better, and people shouldn't need to google a four year old github issue just to figure out how to use encrypted keyfiles in this program.

droidmonkey commented 3 years ago

I renamed this request to be clearer as to what would really solve the problem at hand.

phoerious commented 3 years ago

I cannot recommend using GPG/OpenPGP at all. GPG is crypto practices from about 20 years ago with new things added on only haphazardly if at all. If anything, using GPG to decrypt your database is less secure than using KeePassXC directly, because GPG misses any kind of useful key derivation besides a laughable handful of hash iterations. I would always use KeePassXC as my primary credential store and autofill the GPG password prompt from there.

If you want more security than a simple password, use a YubiKey. You can use that also to store your OpenPGP key.

neonspectra commented 3 years ago

@phoerious I did some more research and unfortunately in a lot of ways you are 100% correct. I'm going to go off on a little tangent here in case someone finds this thread googling the same way I did.

This issue still makes sense to leave open in its current form as a fifo issue... having fifo keyfile reads would be super useful for anyone who still wants to use third party crypto to control their Keepassxc access. But yeah... for the reasons listed below you are actually correct that using gpg for signon might not be the best idea. Thanks for your comment giving me a kick to educate myself better :)


So here's why you actually might not want to use gpg to access keepassxc, despite it being more appropriately scoped to act as your primary authentication as per the unix philosophy

gpg, as it turns out, defaults to running your salted private key passphrase through AES128 salted hashed with SHA1. So you really aren't going to be able to achieve high iter time to protect locked keys from brute force attacks the way you would with more modern passphrase encryption methods. This is actually a complete joke compared to the AES256+Argon2 used to encrypt your database in keepassxc.

The most unfortunate thing about this is that gpg actually in theory supports better algorithms than this, but the string to key (s2k) options in gpg are actually broken in all 2.x versions of gpg. The program also gives zero warning that they are broken and will ignore any custom setting in favour of AES128+SHA1. This issue appears to have existed in gpg since 2015. Yikes.

The icing on the cake here is that it looks like gpg's dev tracker is broken at the time of this writing, so I have no idea if this issue ever gained traction. But it sounds like it never will. Not a good look.

gpg is a really cool tool with some nifty features for digital signature verification and message encryption, but yeah, it is pretty lacking when it comes to protecting the keys actually stored on your system. What a shame.


Edit: I originally misspoke and stated that gpg salted your passphrase with SHA1. This is not true. I corrected my original statement. The reality is way worse. GPG hashes your salted passphrase with SHA1, meaning it is prone to collision attacks. Big yikes.

brianddk commented 1 year ago

I too ran into this issue. For me I'm trying to use the bash process substitution feature. Same idea as others, but perhaps a bit more streamlined like:

keepassxc-cli db-create --set-key-file <(gpg -d keyfile.gpg) example.kdbx

This would would launch GPG's pinentry for challenge / response. This would require that someone have my GPG private key, private key pin, and the keyfile.gpg to access example.kdbx. The advantage of enabling this type of FIFO work over GPG intergration is that with this more flexible approach anything could be in the <(), like perhaps an openssl command or the like.