Page MenuHome GnuPG

Cannot add existing ECDSA key as a signing subkey
Open, HighPublic

Description

Hi,

I'm trying to add an existing ECDSA key as a signing subkey of an existing RSA key.

Unfortunately this doesn't work. Instead, gpg only wants to add the ECDSA key as an ECDH encrypting subkey instead.

I dug through the source code and figured out why this is:

  1. gpg send READKEY for the ECDSA keygrip to gpg-agent.
  2. gpg-agent returns something like this:
D (10:public-key(3:ecc(5:curve10:NIST P-256)(1:q ... )))

Notice in particular that there is no indication of whether this is an ECDSA key or an ECDH key, just that it is an ECC key.

  1. libgcrypt matches this key type using the ecc_names string array in cipher/ecc.c, and eventually maps it to GCRY_PK_ECC.
  1. this enum is converted by gnupg to the OpenPGP equivalent in map_gcry_pk_to_openpgp() in gnupg common/openpgp-oid.c:
switch (algo)
  {
  case GCRY_PK_EDDSA:  return PUBKEY_ALGO_EDDSA;
  case GCRY_PK_ECDSA:  return PUBKEY_ALGO_ECDSA;
  case GCRY_PK_ECDH:   return PUBKEY_ALGO_ECDH;
  default: return algo < 110 ? (pubkey_algo_t)algo : 0;
  }

GCRY_PK_ECC falls through to the default case and its value is cast directly to pubkey_algo_t. The value of GCRY_PK_ECC is 18, which happens to match PUBKEY_ALGO_ECDH.

I hacked around this like so to produce an ECDSA subkey:

   switch (algo)
     {
     case GCRY_PK_EDDSA:  return PUBKEY_ALGO_EDDSA;
     case GCRY_PK_ECDSA:  return PUBKEY_ALGO_ECDSA;
     case GCRY_PK_ECDH:   return PUBKEY_ALGO_ECDH;
+    case GCRY_PK_ECC:    return PUBKEY_ALGO_ECDSA;
     default: return algo < 110 ? (pubkey_algo_t)algo : 0;
     }

Of course this is just a dirty hack and not an actual proper fix.

Can we please get the option to use an ECC key as either an ECDH encrypting subkey, or an ECDSA signing subkey?

Cheers,
Scott.

Note: I tested this on my distro's versions (gnupg-2.2.27, libgcrypt-1.9.2), but the logic in question hasn't changed in gnupg-2.3.1.

Details

Version
gnupg-2.2.27, libgcrypt-1.9.2

Event Timeline

smlx renamed this task from Cannot use existing key to add ECDSA signing subkey to Cannot add existing ECDSA key as a signing subkey.Aug 14 2021, 7:53 AM
smlx created this task.
werner triaged this task as High priority.Aug 14 2021, 1:25 PM
werner added a project: gnupg (gpg22).
werner added a project: backport.
werner added a subscriber: werner.

Will do.

I tried applied the bulk of the patch to 2.2 but w/o reading the key creation time from the card. We don't have the supporting code for latter in 2.2. However this does not make sense. Users should switch to 2.3 if they needs this feature.

werner changed the task status from Open to Testing.Aug 26 2021, 11:54 AM

Will only be fixed for 2.3 and that has already been released.

Hi,

I tested this on 2.3, and it doesn't seem to be fixed. When adding an existing ECDSA subkey I don't get the option to choose whether to make it a signing or encrypting subkey. Instead it only allows me to choose an encrypting subkey.

If I add this patch I can create a signing subkey.

--- ./gnupg-2.3.2/common/openpgp-oid.c
+++ ./gnupg-2.3.2/common/openpgp-oid.c
@@ -581,6 +581,7 @@
     case GCRY_PK_EDDSA:  return PUBKEY_ALGO_EDDSA;
     case GCRY_PK_ECDSA:  return PUBKEY_ALGO_ECDSA;
     case GCRY_PK_ECDH:   return PUBKEY_ALGO_ECDH;
+    case GCRY_PK_ECC:    return PUBKEY_ALGO_ECDSA;
     default: return algo < 110 ? (pubkey_algo_t)algo : 0;
     }
 }

This is on the following package versions (latest at time of writing):

	gnupg-2.3.2/
	libassuan-2.5.5/
	libgcrypt-1.9.4/
	libgpg-error-1.42/
	libksba-1.6.0/
	npth-1.6/

Regards,
Scott.

Note that I'm referring to file based keys, not card based.

For anyone stumbling across this issue I created a docker image containing gpg with the patch above applied: https://github.com/smlx/gnupg-piv-agent

This patch breaks adding existing ECDH encryption subkeys to a key because now gpg tries to treat the encryption subkey as signing subkey. This can be reproduced with test t-addexistingsubkey in gpgme.

Yes, that patch is not a great solution. Ideally there would be an interactive choice in the gpg CLI between encrypting/signing subkey during the add-existing-subkey operation.

I have no idea about the gpgme API, but some way of indicating how the ECC key should be treated (encrypting or signing) is required there too I guess.

I've got a similar patch, but I'm not sure it's any better -- I'm adding EcDSA support for cards (via gnupg-pkcs11-scd) and with this patch I can sign subkeys and data.

diff --git a/agent/pksign.c b/agent/pksign.c
index b877addb0..86efd9f58 100644
--- a/agent/pksign.c
+++ b/agent/pksign.c
@@ -447,7 +447,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
           err = gcry_sexp_build (&s_sig, NULL, "(sig-val(eddsa(r%b)(s%b)))",
                                  (int)len/2, buf, (int)len/2, buf + len/2);
         }
-      else if (algo == GCRY_PK_ECC)
+      else if (algo == GCRY_PK_ECC || algo == GCRY_PK_ECDSA)
         {
           unsigned char *r_buf, *s_buf;
           int r_buflen, s_buflen;
@@ -501,7 +501,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
         err = do_encode_raw_pkcs1 (data, datalen,
                                    gcry_pk_get_nbits (s_skey),
                                    &s_hash);
-      else if (algo == GCRY_PK_DSA || algo == GCRY_PK_ECC)
+      else if (algo == GCRY_PK_DSA || algo == GCRY_PK_ECC || algo == GCRY_PK_ECDSA)
         err = do_encode_dsa (data, datalen,
                              algo, s_skey,
                              &s_hash);
diff --git a/common/sexputil.c b/common/sexputil.c
index b7ddea8fc..cb31408ea 100644
--- a/common/sexputil.c
+++ b/common/sexputil.c
@@ -1030,6 +1030,9 @@ get_pk_algo_from_key (gcry_sexp_t key)
       s = gcry_sexp_nth_data (l1, 1, &n);
       if (n == 5 && !memcmp (s, "Ed448", 5))
         algo = GCRY_PK_EDDSA;
+      else if (n) {
+        algo = GCRY_PK_ECDSA;
+      }
       gcry_sexp_release (l1);
     }