Page MenuHome GnuPG

agent: Enhancement of PKDECRYPT for KEM interface
Open, WishlistPublic


Currently (as of GnuPG 2.4.5), PKDECRYPT computation for ECC is a bit different to the computation of RSA (or DSA).
It is basically only a part of computation of decryption for ECC (that is, (only), ECDH computation with private key to get shared secret).
Although the both data of e (ephemeral key) and s (encrypted session key) are sent to gpg-agent, gpg-agent only use e to get shared secret.
(And it is actually client of gpg-agent to get the session key (by decryption of AESKEYWRAP) from encrypted session key using the result of gpg-agent.

It would be good to enhance PKDECRYPT command (say, by adding option --kem for ECC) to support computation including decryption by AESWRAP. This way, ECC computation of PKDECRYPT is aligned to the one of RSA and DSA.

This makes sense in the context of PQC, which uses hybrid KEM.

The enhanced PKDECRYPT would go like this:

  • SETKEY --another <KEYGRIP-of-Kyber> ; new option for SETKEY command
  • PKDECRYPT --kem <KEM_ID>; new option for PKDECRYPT command
    • gpg-agent inquires CIPHERTEXT (e and s in S-expression)
    • gpg-agent inquires OPTION (for optional data, KDF parameter in the term of RFC 6637)
    • compute decaps with KEYGRIP-of-ECC key, using ECC part in the e part of CIPHERTEXT (KEM_ID should match the ECC key)
    • compute decaps with KEYGRIP-of-Kyber key, using Kyber part in the e part of CIPHERTEXT (KEM_ID should match the Kyber key)
    • determined by KEM_ID, compute KEM combiner using OPTION, results --> KEK
    • compute AESKeyUnwrap using the KEK and s part of CIPHERTEXT, results --> session key

Event Timeline

gniibe triaged this task as Wishlist priority.Feb 26 2024, 8:31 AM
gniibe created this task.

As a first experiment, let us use CIPHERTEXT in the format of (enc-val(ecdh(s%m)(e%m)(k%m))) (s: encrypted-session-key, e: ecc ephemeral key, k: kyber ephemeral key).

I extracted data from and compose x25519 key and MLKEM768 key. Here they are.
x25519 :

MLKEM768 :

In the test vector of v4 PQC subkey (in A.2. V4 PQC Subkey Artifacts), the data seems broken;

# off=500 ctb=c7 tag=7 hlen=3 plen=3657 new-ctb
:secret sub key packet:
	version 4, algo 105, created 1372636800, expires 0
	unknown algorithm 105
  • IIUC, the size should be 3654 (instead of 3657). 6-octet: [version=4] [created-time-4-octet] [ALGO=105] 32-octet: public ecc key 1184-octet: public mlklm768 key 32-octet: secret scalar of ecc 2400-octet: secret of mlklm768
  • (6+32+1184+32+2400) = 3654

I see an octet of 00 after mlklm768 public key.
I see two octets of 6c39 after secret of mlklm768.

On March 11 and 18, the private key file DE1AB1D22899CEC7DBB1A7863F34E6E92BFB7756.key was wrong.
I updated on March 25. Now, the endian is GnuPG (d is big endian).

I use this for testing:

And do:

gpg-connect-agent --hex <<EOF
/definqfile CIPHERTEXT encrypted-sexp.txt
SETKEY --another 1C4D98ABD3BD87C0C505A9FE97BB766EAB38B87F

I created a pubkey (actually a subkey) for your above test keys:

sec   ed25519 2024-04-05 [SC] [expires: 2027-04-05]
      Keygrip = EF3CE1515B032A51E997362FDD79356B7EC0EC0E
uid           [ultimate] pqc-test-april-4
ssb   ky768_cv25519 2024-04-05 [ER]
      Keygrip = DE1AB1D22899CEC7DBB1A7863F34E6E92BFB7756,1C4D98ABD3BD87C0C505A9FE97BB766EAB38B87F

(There is still the Renc bug T7072 in the used code base)

Update: The fingerprint computation was wrong - fixed and updated listing.

In the current code, just for testing against the test vector in m, there are specific value in the key combiner KDF.
Namely, the value 105 for fixedInfo is defined in the draft (and it will be changed).

This kind of dependency to the protocol specification can't be avoided, because it is intentionally specified for domain separation.

Later, let me change the value for LibrePGP, or let us invent some method to specify the value by gpg-agent command line.

I merged the change by Werner to get the value from frontend.

Here is the updated file for testing, which uses a single octet of 105 (hex 69) for FIXED_INFO.

I had wrong interpretation about symmetric cipher algorithm identifier in the draft. It specifies symmetric cipher for the following Symmetrically Encrypted Data Packet (I was wrongly interpret as if it were specifying algo for AES keywrap).

I modified the SEXP in the gpg-agent protocol as:

        c: cipher identifier (symmetric)
        e: ECDH ciphertext
        k: ML-KEM ciphertext
        s: encrypted session key
        fixed-info: A buffer with the fixed info.

Here is the updated file for testing.