[PATCH gpgme] Add RFC 9980 post-quantum public-key algorithms

Stavros Kousidis stavros.kousidis at th-koeln.de
Wed Sep 23 15:24:47 CEST 2026


Hi,

I was a bit too quick in promising that I would adapt the patch to this 
model.

I understand the motivation to keep GPGME’s existing family/parameter 
abstraction. One concern I have, though, is that the newer OpenPGP 
composite algorithm identifiers have different semantics from RSA, 
ECDSA, or ECDH.

For the latter, the algorithm identifier denotes a family and parameters 
such as curve or key size complete the definition. For the RFC 9980 
composites, the OpenPGP algorithm identifier itself already denotes the 
complete construction, e.g. ML-KEM-768+X25519.

Mapping that back to |GPGME_PK_MLKEM| plus auxiliary parameters 
therefore decomposes a protocol-defined algorithm and requires 
applications to reconstruct its identity afterwards. I think that is 
problematic, especially because there is no longer an unambiguous |bits| 
value: in a construction such as ML-KEM-768+X25519 there are two 
cryptographic components, and |768| is a parameter-set designation, not 
a key length or security strength in bits.

Reconstructing the concrete algorithm from a generic family plus |curve| 
and |length| therefore looks to me like a fruitful source of mismatches. 
I strongly think this should not be pursued, and that the 
protocol-defined composite algorithm should remain represented as such 
rather than being decomposed and reconstructed later.

Best
Stavros


On 9/23/26 11:46 AM, Stavros Kousidis wrote:
> Hello,
>
> Thanks, understood. My original patch followed the OpenPGP protocol 
> view and exposed the concrete RFC 9980 combinations directly, but I 
> see that this does not fit GPGME’s existing abstraction.
>
> I’ll rework the patch so that the OpenPGP algorithm numbers are 
> normalized in |_gpgme_map_pk_algo| to generic algorithm families:
>
>  * RFC 9980 ML-KEM combinations → |GPGME_PK_MLKEM|, corresponding to
>    the proposed |GCRY_PK_MLKEM| pseudo ID 331.
>  * RFC 9980 ML-DSA combinations → |GPGME_PK_MLDSA|, corresponding to
>    |GCRY_PK_MLDSA| 332.
>
> The concrete OpenPGP combination can then continue to be distinguished 
> through the existing curve/key-length metadata, which also keeps RFC 
> 9980 distinguishable from the NIST/Brainpool composite draft.
>
> I’ll also align the GnuPG-facing names with the existing GnuPG naming 
> conventions.
>
> One question before I send v2: how would you like the RFC 9980 SLH-DSA 
> algorithm IDs 32–34 represented? Should there be a generic 
> |GPGME_PK_SLHDSA| pseudo identifier, analogous to ML-KEM and ML-DSA, 
> or should those remain unmapped for now?
>
> Best
> Stavros
>
>
> On 9/23/26 11:14 AM, Werner Koch wrote:
>> Hello!
>>
>> thanks for working with GPGME and for your patch.
>>
>> I see two problems with your implementation:
>>
>> 1. The name of the identifiers do not match those used by GnuPG.  For
>>     example GPGME_PK_MLKEM768_X25519 vs PUBKEY_ALGO_MLK768_25519 in
>>     GnuPG.  This is of course easy to fix by using 
>> GPGME_PK_MLK768_25519.
>>
>>     Also for "ML-KEM-768+X25519" vs. "mlk768".  For the new Brainpool
>>     support we use "mlk768_bp384".
>>
>> 2. A combinatorial explosion due to the way RFC-9850 work.  In gpgme we
>>     should avoid this.  For example there is an internal mapping
>>
>>       int
>>       _gpgme_map_pk_algo (int algo, gpgme_protocol_t protocol)
>>       {
>>         if (protocol == GPGME_PROTOCOL_OPENPGP)
>>           {
>>             switch (algo)
>>               {
>>               case 1: case 2: case 3: case 8: case 16: case 17: break;
>>               case 18: algo = GPGME_PK_ECDH; break;
>>               case 19: algo = GPGME_PK_ECDSA; break;
>>               case 20: break;
>>               case 22: algo = GPGME_PK_EDDSA; break;
>>               default: algo = 0; break; /* Unknown.  */
>>               }
>>           }
>>
>>     used to map the OpenPGP algorithm numbers to to those used by
>>     Libgcrypt.  This is actually straighforward but in Libgcrypt we do
>>     not have separate Ids for each ML-KEM/ECDH combination. Instead we
>>     use one identifier (8 for ML-KEM with ECDH) and use the length and
>>     curve parameter to specify the details.  This reflects what we have
>>     always done with RSA, Elgamal, and ECC: A base algorithm and
>>     parameters for the details.
>>
>>     For the 9980 algos I would suggest to map all ML-KEM algos to a new
>>     GPGME_PK_MLKEM which will match a new GCRY_PK_MLKEM Peseudo ID with
>>     value 331.  For the ML-DSA algos a GPGME_PK_MLDSA would match the
>>     Libgcrypt GCRY_PK_MLDSA identifier (value 332).  Using these new
>>     identifiers we can keep the IETF way of combining the algos apart
>>     from your and the BSIs original key combiner (as specified by
>>     LibrePGP).  They are already denoted by GPGME_PK_KYBER and
>>     (eventually) GPGME_PK_DILITHIUM.
>>
>>     GPGME applications already need to take the curve (and length)
>>     parameter in account and thus can decide whether the 9980 or the
>>     LibrePGP key combiner is used.
>>
>>
>> BTW, GnuPG 2.5.23 was released yesterday with support for 9980 inclduing
>> Brainpool and NIST versions of ML-KEM.
>>
>>
>>
>> Salam-Shalom,
>>
>>     Werner
>>
>>
-- 
TH Köln E-Mail-Signatur – Prof. Dr. Stavros Kousidis
*Prof. Dr. Stavros Kousidis*
Faculty of Information, Media and Electrical Engineering
Institute of Computer and Communication Technology
www.th-koeln.de/en/person/stavros.kousidis/ 
<https://www.th-koeln.de/en/person/stavros.kousidis/>
TH Köln - University of Applied Sciences
Campus Deutz
Betzdorfer Straße 2, 50679 Köln
TH Köln – Technology Arts Sciences


More information about the Gnupg-devel mailing list