Page MenuHome GnuPG

Unable to export SSH keys for ED25519 keys generate on a SmartCard
Open, Needs TriagePublic

Description

I failed to export the public part of an ED22519 key generated on a SmartCard for SSH usage.

These are the steps to reproduce the error.

  1. I am using a YubiKey with 5.7.1 firmware.
$ gpg-card     
Reader ...........: Yubico YubiKey OTP FIDO CCID 01 00
Card type ........: yubikey
Card firmware ....: 5.7.1
Application type .: OpenPGP
Version ..........: 3.4
[...]
  1. Generate the key
gpg/card> generate --algo=ed25519 OPENPGP.3
OpenPGP card no. 30 220 318 detected
gpg/card> list
[...]
Signature key ....: [none]
      keyref .....: OPENPGP.1
      algorithm ..: rsa2048
Encryption key....: [none]
      keyref .....: OPENPGP.2
      algorithm ..: rsa2048
Authentication key: 194459B63E0611BCBB194C0DFE93045CF66E6104
      keyref .....: OPENPGP.3  (sign,auth)
      algorithm ..: ed25519
      stored fpr .: AFDB92C7832F093570322ED0D1DEA97D05F46C0B
      created ....: 2025-03-27 10:16:38
gpg/card> quit
  1. Create the GPG key block with the authentication key usage flag.
$ gpg --expert --full-generate-key
gpg (GnuPG) 2.4.7; Copyright (C) 2024 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.

Please select what kind of key you want:
  (14) Existing key from card
Your selection? 14
Serial number of the card: D2760001240100000006302203180000
Available keys:
   (1) 194459B63E0611BCBB194C0DFE93045CF66E6104 OPENPGP.3 ed25519 (sign,auth*)
Your selection? 1

Possible actions for this ECC key: Sign Authenticate
Current allowed actions: Sign Authenticate

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? s

Possible actions for this ECC key: Sign Authenticate
Current allowed actions: Authenticate

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? q
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: Test
Email address:
Comment: SSH
You selected this USER-ID:
    "Test (SSH)"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
gpg: revocation certificate stored as '.../.gnupg/openpgp-revocs.d/AFDB92C7832F093570322ED0D1DEA97D05F46C0B.rev'
public and secret key created and signed.

pub   ed25519 2025-03-27 [SCA]
      AFDB92C7832F093570322ED0D1DEA97D05F46C0B
uid                      Test (SSH)

Please mind the key usage was just set to "Authenticate", but the create key block has the S, C and A flags set.

Eporting the SSH key fails.

$ gpg --export-ssh-key AFDB92C7832F093570322ED0D1DEA97D05F46C0B
gpg: key "AFDB92C7832F093570322ED0D1DEA97D05F46C0B" not found: Unusable public key
gpg: export as ssh key failed: Unusable public key

Ignoring the key usage flag by adding ! to the fingerprint exports the public key. It works well together with the GnuPG SSH agent when added to the authorized_keys file.

$ gpg --export-ssh-key AFDB92C7832F093570322ED0D1DEA97D05F46C0B!
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINFxhOM2mqvHw3sHp2S+ydgwf9CxfpBqOnlXvq5bS2wB openpgp:0x05F46C0B

I was able to figure out, the following check bails out in export_ssh_key() in g10/export.c.

else if (!pk->flags.valid)
  {
    if (DBG_LOOKUP)
      log_debug ("\tprimary key not valid\n");
  }

It seems the key signature is corrupt.

$ gpg --expert --edit-key AFDB92C7832F093570322ED0D1DEA97D05F46C0B!
gpg (GnuPG) 2.4.7; Copyright (C) 2024 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.

gpg: key D1DEA97D05F46C0B: 1 bad signature
gpg: key D1DEA97D05F46C0B: Warning: errors found and only checked self-signatures, run 'check' to check all signatures.
Secret key is available.

sec  ed25519/D1DEA97D05F46C0B
     created: 2025-03-27  expires: never       usage: SCA
     card-no: 0006 30220318
     trust: ultimate      validity: ultimate
[ultimate] (1). Test (SSH)

gpg> check
key D1DEA97D05F46C0B: 1 bad signature

Creating a key outside the SmartCard works well

$ gpg --expert --full-generate-key
gpg (GnuPG) 2.4.7; Copyright (C) 2024 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.

Please select what kind of key you want:
  (11) ECC (set your own capabilities)
Your selection? 11

Possible actions for this ECC key: Sign Certify Authenticate
Current allowed actions: Sign Certify

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? s

Possible actions for this ECC key: Sign Certify Authenticate
Current allowed actions: Certify

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? a

Possible actions for this ECC key: Sign Certify Authenticate
Current allowed actions: Certify Authenticate

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? q
Please select which elliptic curve you want:
   (1) Curve 25519 *default*
   (2) Curve 448
   (3) NIST P-256
   (4) NIST P-384
   (5) NIST P-521
   (6) Brainpool P-256
   (7) Brainpool P-384
   (8) Brainpool P-512
   (9) secp256k1
Your selection? 1
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: Test
Email address:
Comment: SSH
You selected this USER-ID:
    "Test (SSH)"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: revocation certificate stored as '.../.gnupg/openpgp-revocs.d/620D895C25DF5F862213ED20AD85622B0D05F070.rev'
public and secret key created and signed.

pub   ed25519 2025-03-27 [CA]
      620D895C25DF5F862213ED20AD85622B0D05F070
uid                      Test (SSH)

In this case the key usage flags C and A are consistent and the export works as expected.

$ gpg --export-ssh-key 620D895C25DF5F862213ED20AD85622B0D05F070
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFp3pLO20FLKMs3AcygHeVWxQ/i6rdXpsxZZt2XXv1dT openpgp:0x0D05F070

It is even possible to move the key to the SmartCard and use it from there.

$ gpg --expert --edit-key 620D895C25DF5F862213ED20AD85622B0D05F070 
gpg (GnuPG) 2.4.7; Copyright (C) 2024 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  ed25519/AD85622B0D05F070
     created: 2025-03-27  expires: never       usage: CA  
     trust: ultimate      validity: ultimate
[ultimate] (1). Test (SSH)

gpg> check

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

gpg: WARNING: such a key has already been stored on the card!

Replace existing key? (y/N) y

sec  ed25519/AD85622B0D05F070
     created: 2025-03-27  expires: never       usage: CA
     trust: ultimate      validity: ultimate
[ultimate] (1). Test (SSH)

Note: the local copy of the secret key will only be deleted with "save".
gpg> save

This problem seems to be specific to ED255519 keys as it doesn't happen for ECDSA or RSA keys.

Details

Version
2.4.7

Event Timeline

Using the primary key for ssh was not intended and thus not tested. I have not yet found the time too look closer at your report. Just one remark:

Please mind the key usage was just set to "Authenticate", but the create key block has the S, C and A flags set.

The reason or this might be that the usage flags where not used but the fallback to use all capabilities possible for the used algorithm.