Open tucnak opened 2 years ago
Hi. Thanks for the idea and willingness to contribute. I don't fully understand what you are proposing to use gpg smartcards for.
Let me elaborate. Currently, in a standard wallet, if you have a password set, there are two things the password is used for: encrypting the keystore (e.g. seed words and private keys when stored in memory) using KDF1(password), and encrypting the storage (wallet file on disk) using KDF2(password).
When setting the password, there is a checkbox (on by the default) "Encrypt wallet file" (to encrypt the storage). The storage, when plaintext, is just a json dict that contains some metadata and also some keystore objects (usually just one). Regardless of having a password set, the storage is loaded in memory as plaintext at runtime (after opening a wallet). The keystore contains seed words, xprv, xpub, or simply private keys. To try to avoid having private keys directly loaded in memory, the secrets in the keystore are encrypted (somewhat independently of the storage being encrypted).
That is, typically, the user is prompted for their password to open a wallet (to decrypt the storage), then the storage is stored in memory without encryption. Then, the user is prompted for the password every time secrets from the keystore are needed (e.g. to spend coins or to display the seed).
Further note, currently, the storage is just a json dict (+encryption), but in the future we plan to transition to something that allows "partial writes" (as a proper DB would). That is, currently every time the DB is updated, if we want to persist the update on disk, we have to reserialise the DB as json (then encrypt it) and then write the whole thing to disk. This gets inefficient for large DB sizes. (see https://github.com/spesmilo/electrum/issues/4823 )
For further context, I shall also explain how "hardware-device" (such as Trezor) encrypted wallets work. When you have a Trezor and create a wallet with encryption disabled (it is enabled by default), the file created will have storage encryption disabled, and the keystore will be a watching-only keystore (for which keystore encryption does not make sense as it has no secrets). When spending coins, the Trezor is sent parts of the txn being made via an API (via USB) to sign the tx. If you enable "Encrypt wallet file", the storage gets encrypted, and so the wallet file stored on disk containing e.g. tx history and other metadata gets encrypted. However, note that Trezor does not expose an API for encryption. What we do is we query the Trezor for an xpub (bip32 master public key) at a fixed custom hardened derivation path. This xpub is then used ~as if it was a user-provided password. This method works for all hwd manufacturers, the key only depends on the seed words used to initialise the device and the hardcoded custom derivation path. Also, due to the derivation path being hardened (see bip32), we can consider it unrelated to the keys that are used for bitcoin signing (spending coins, etc).
With all that said, again, what exactly would you like the smartcard to do? What would the threat model be, compared to using a software password?
Instead of having the smartcard decrypt the whole wallet file upon opening a wallet, would it make sense to try to do something similar to how storage encryption is implemented with hardware wallets? E.g. just store an encrypted random-generated "password" in the wallet file as 32 bytes, and have the smartcard decrypt those 32 bytes, and then have the software use the decrypted random "password" as usual (as if it was a user-provided password without a smartcard being used)?
Re the dependencies, you did not mention GPG itself :P I am not sure if we would want to bundle that in the binaries... but I guess it's ok to ask the user to install it themselves if they already have a gpg smartcard.
Thank you for the outline of how the hardware wallet support is implemented!
With all that said, again, what exactly would you like the smartcard to do?
Signing, encryption, authentication— what it can normally do. AFAIK, Bitcoin is based off secp256k1 ECDSA keys, and most Yubikey devices support it (up to three keys— one for each capability.) Bottom line— this means that I can keep my private key on a physical device which does not allow it to be accessed, transferred, or anything, but actually using the keys in the ways intended. That is— to sign a transaction, or decrypt the locally stored wallet data. My software stack may be compromised in all kinds of ways, but unless I do physically touch the PIN-unlocked key at the right moment, no cryptographic operation will occur.
The "touch" protection is a definite upgrade from non-interactive HSM's willing to perform ops freely.
Also, let's say that Trezors of this world are specific, and somewhat expensive hardware with a limited toolset of a limited applications in the wild, too. However, the Yubikey is something that is already owned by hundreds of thousands of people be it in personal, or business use— for the simple purpose of TOTP, two-factor authentication, and in a more limited capacity— PGP encrypted mail, among other things GPG.
Instead of having the smartcard decrypt the whole wallet file upon opening a wallet, would it make sense to try to do something similar to how storage encryption is implemented with hardware wallets? E.g. just store an encrypted random-generated "password" in the wallet file as 32 bytes, and have the smartcard decrypt those 32 bytes, and then have the software use the decrypted random "password" as usual (as if it was a user-provided password without a smartcard being used)?
Absolutely! The cards incapable of signing ECDSA messages could be used to secure the software wallets in all kinds of ways, the biggest upshot being that of password-less. "Keep the card, don't worry about forgetting the password." To be honest, I couldn't care less for this particular angle, primarily because being an avid Yubikey user, I for the most part would enjoy to either keep the private key on-device, or simply rely on my cv25519 key for decrypting of the software wallet in RAM, whenever I feel like using it, simply because the "touch" flow is something I'm incredibly familiar with, now that I've been doing it for years when reading and writing mail, securing documents, and performing suchlike activities.
Re the dependencies, you did not mention GPG itself :P
Most GPG users typically prefer their software to link against the system libgpg, and update it regularly.
@tucnak My understanding is that yubikey may support secp256k1 but does not support BIP32 derivation (which is the key thing other hardware wallets do). Maybe it does on-device key derivation using ECDH, but I doubt much more.
In your original comment you suggested that it could be used in lieu of a hardware wallet (I already have a seed, use OpenGPG, etc), but a hardware wallet contains the master private key within it and never exposes it to Electrum. And it can be asked to sign with abstract BIP32 derivation directives, so that it can operate with a deterministic child private key while Electrum deals in the matching public keys. All of the Electrum wallet key usage and signing infrastructure (outside of fixed private key import) is based around BIP32.
Is this something you have already solved in your patch, or rather were assuming it should be possible if yubikey supported the right curve?
Not to my knowledge, I know there was an official effort to make BIP32 derivation work for Yubikey NEO from Yubikey themselves, with implementations in both Java as well as Python but it didn't amount to anything in particular: "THIS PROJECT IS ABANDONED. Due to technical limitations ykneo-bitcoin was never realized." The obvious limitation is that in order to juggle multiple keys you would have to store multiple keys with the sign capability, and currently YubiHSM is the only device in their lineup that is capable of doing that, up to 255 curved keys total. However, some members would be O.K. with ever using a single address and although it's not a good decision from the privacy standpoint, for some users it's a no-concern. I manage the crypto for a Ukrainian charity which is how it receives some of its donations, and we simply rock a single Bitcoin address where the payouts from various swaps go to. For this, the derivation is simply unnecessary, as it would hurt our transparency.
And we already use Yubikey for some of our mission-critical tasks!
However, forget about any of it, in my opinion it would be beneficial to offer the password-less access to the software wallet as a matter of curtesy, as it's one less password to worry about, and the security at rest can by guaranteed by any type of key that is capable of encryption.
To be clear, to me it seems pointless to try to use a yubikey or similar gpg smartcard for signing txs, as (1) like @rt121212121 explained above they do not support bip32, (2) any meaningful improvement in security would require a trusted display IMHO (or at least an independent second display).
"password-less access to the software wallet" however looks doable.
Hello,
I'm creating this issue to find out whether if Electrum would be willing to accept a patch of mine. If this has the potential to land in master I would be willing to do this, but my primary concern is that I wouldn't want to maintain a fork of my own, keep it in sync with upstream and all.
The idea is very simple— currently, the only way to protect a permanent wallet in software is via a password, and for those of use who don't have a dedicated hardware like Trezor, or Ledger it's the only option. I do own a Yubikey, however, which functions as a perfectly valid OpenPGP smart card, and I would love to use that for protecting the wallet with E-capability key as opposed to a password. In a setup like this, the encryption key never leaves the device and can only be triggered to decrypt after the hardware PIN is entered, and user touches the device physically. For the most part my Yubikey is plugged in, and I'm using it constantly so I would typically ever enter the PIN once— and then I can use it for a whole range of things by simply touch; it would be very handy to be able to do the same when operating the wallets in Electrum, thereby eliminating a need to remember the password to each individual wallet.
In practice, this means adding a python-gnupg dependency and a limited set of GUI changes, where wallet files like it could have
.gpg
filename suffix, indicating that it's meant to be manipulated by means of an external GnuPG keystore, provided by a local/remote gpg-agent.An additional option would have to be added in the keystore dialog:
Does this make sense from the maintenance standpoint? I would love to go on and implement this for personal use, but maintaining a fork of my own is simply too much effort, it seems like. Also: Yubikey is traditionally used in 2FA, so perhaps there's extra avenue to further utilise its OpenPGP smart card capability in order to avoid the 3rd party like TrustedCoin.