Page MenuHome GnuPG

gpg-agent 2.2: Command "READKEY --card --no-data -- OPENPGP.1" overwrites protected-private-key with shadowed-private-key
Closed, ResolvedPublic

Description

With other words, gpg-agent silently deletes the locally stored private key without asking for confirmation. Let's hope the user has a backup because otherwise they have lost access to the private key material. I consider this a serious regression.

This bug is only present in GnuPG 2.2.

Reproduce:

  1. Ensure you use gpg 2.2.37 or later (but not 2.3 or later)
$ gpg --version
gpg (GnuPG) 2.2.42-beta145
libgcrypt 1.8.11-beta2
  1. Have a secret key available.
$ gpg -K
sec   rsa3072 2023-02-24 [SC] [expires: 2025-02-24]
      4A24FA992B0A1181179F817EE0CF08F792BD5422
uid           [ultimate] Ada Lovelace <ada@example.net>
ssb   rsa3072 2023-02-24 [E] [expires: 2025-02-24]
  1. Use --edit-key to move the primary key to the card and quit without saving.
$ gpg --edit-key 4A24FA992B0A1181179F817EE0CF08F792BD5422
gpg (GnuPG) 2.2.42-beta145; Copyright (C) 2023 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  rsa3072/E0CF08F792BD5422
     created: 2023-02-24  expires: 2025-02-24  usage: SC  
     trust: ultimate      validity: ultimate
ssb  rsa3072/FF2AE96F2FD6024F
     created: 2023-02-24  expires: 2025-02-24  usage: E   
[ultimate] (1). Ada Lovelace <ada@example.net>

gpg> keytocard
Really move the primary key? (y/N) y
Please select where to store the key:
   (1) Signature key
   (3) Authentication key
Your selection? 1

sec  rsa3072/E0CF08F792BD5422
     created: 2023-02-24  expires: 2025-02-24  usage: SC  
     trust: ultimate      validity: ultimate
ssb  rsa3072/FF2AE96F2FD6024F
     created: 2023-02-24  expires: 2025-02-24  usage: E   
[ultimate] (1). Ada Lovelace <ada@example.net>

gpg> quit
Save changes? (y/N) N
Quit without saving? (y/N) y
  1. Verify that the secret key of the primary key is still available.
$ gpg -K
sec   rsa3072 2023-02-24 [SC] [expires: 2025-02-24]
      4A24FA992B0A1181179F817EE0CF08F792BD5422
uid           [ultimate] Ada Lovelace <ada@example.net>
ssb   rsa3072 2023-02-24 [E] [expires: 2025-02-24]
  1. Run the command "READKEY --card --no-data -- OPENPGP.1"
$ gpg-connect-agent "READKEY --card --no-data -- OPENPGP.1" /bye
OK
  1. Observe that the secret key of the primary has been replaced with a stub effectively deleting the secret key without confirmation.
$ gpg -K
sec>  rsa3072 2023-02-24 [SC] [expires: 2025-02-24]
      4A24FA992B0A1181179F817EE0CF08F792BD5422
      Card serial no. = 0006 09074582
uid           [ultimate] Ada Lovelace <ada@example.net>
ssb   rsa3072 2023-02-24 [E] [expires: 2025-02-24]

This regression was introduced by rG755920d43357: agent: Let READKEY update the display-s/n of the Token entry. which unconditionally overwrites the private key file "to update the shadow key" without checking whether the private key file actually contains a shadow key.

Event Timeline

I should probably add that Kleopatra calls this command when reading a smart card to create the key stubs if necessary. Kleopatra does this since gpg4win-3.1.24 (according to the tags) and the KDE Gear 22.04 release (see T5782: Kleopatra: Smartcard unusable secret key until used via command line).

werner added a subscriber: werner.

Thanks for the report; the regression happened due to fixing T6135.

aheinecke triaged this task as High priority.EditedFeb 28 2023, 9:45 AM

Since I have closed T6377 which had high priority I am assigning this issue the same prio. Which I also think is appropriate.

I doubt that the bug is only in 2.2. The code in 2.4 is different but it may happen there anyway. It depends on the usage pattern.

My fix is to disallow overwriting a private key with a stub file. Thus we now test right before writing on the open file whether it is a shadow file - it will be updated only then.

Make sure that the fix doesn't break "gpg --edit-key; keytocard; save" which explicitly does replace the private key with a stub file.

I can't see any explicit thing there.

I think we should make it explicit - this will be safer. As of now agent_write_shadow_key will do a check only in its special update mode which should be okay for now.

If agent_write_shadow_key does now also check for an existing private key file, then I'd replace following code in cmd_readkey:

if (agent_key_available (grip))
  {
    /* Shadow-key is not available in our key storage.  */
    rc = agent_write_shadow_key (0, grip, serialno, keyid, pkbuf, 0,
                                 dispserialno);
  }
else
  {
    /* Shadow-key is available in our key storage but ne check
     * whether we need to update it with a new display-s/n or
     * whatever.  */
    rc = agent_write_shadow_key (1, grip, serialno, keyid, pkbuf, 0,
                                 dispserialno);
  }

with a simple call of agent_write_shadow_key (removing the maybe_update flag) and let agent_write_shadow_key do all checking for an already existing private key file and whether it's a stub file that needs updating.

From a brief check this should be okay for all callers of agent_write_shadow_key and it would make sure that agent_write_shadow_key never overwrites an existing private key. If this should ever become necessary to overwrite an existing private key with a shadow stub, then add a force flag. But currently I don't see this need.

I am pretty sure we have the same problem in 2.4 - due to different access patterns it might not exhibit itself.

I did some reworking and the outcome of the READKEY command is now (agent log):

updating regular key file '/home/wk/b/gnupg-2.2/test-new-findkey/private-keys-v1.d/EB6BE32EAE0E67693278FACE77C1D33CCDEE5AC6.key' by a shadow key inhibited
error writing key: Conflicting use
command 'READKEY' failed: Conflicting use

Thus the file is not replaced. I am not sure whether returning an error here is better than simply ignoring the crration of a stub file.

Ignoring the error seems to be the best choice. I also think that --force should not overwrite a shadow key file. It seems safer to explicitly delete the key first. A --force option for READKEY does not sound right.

werner changed the task status from Open to Testing.Mar 14 2023, 10:26 AM

I agree. Something called READ... shouldn't change existing data. (Updating existing data to a new format that doesn't alter the semantics of the existing data is okay.)

Both

gpg --edit-key; keytocard; quit

and

gpg --edit-key; keytocard; save

work as expected.

Tested with gpg (GnuPG) 2.2.42-beta102

ebo edited projects, added gnupg22 (gnupg-2.2.42); removed gnupg22.

Due to back porting another change the fix for 2.2 is now also needed in 2.4.