{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreidecixprxvhkraro33hbjraifz2zgtqv6th2lgdeeb6qp7qkmoane",
"uri": "at://did:plc:pi6woz4d47bkuws673w2il2r/app.bsky.feed.post/3mlm62b5yfdz2"
},
"path": "/t/botan-bindings-devlog/6855?page=11#post_211",
"publishedAt": "2026-05-11T20:35:24.000Z",
"site": "https://discourse.haskell.org",
"tags": [
"botan 4"
],
"textContent": "# PubKey progress, Post-quantum support, and Better versioning\n\nToday’s update focuses on two related things - public key infrastructure, and post-quantum algorithms! The former is basically the same thing we’ve been doing with `Hash` and `MAC` and `Cipher` - upgrading it to the new `BotanObject`, bringing in algorithm data types, adding more convenient interfaces! I won’t bother pasting everything, just the highlights, because there’s a lot.\n\n# PubKey improvements\n\nFirst off, the new data type for public key (PK) algorithms:\n\n\n data PKType\n = RSAType\n | SM2Type\n | ElGamalType\n | DSAType\n | ECDSAType\n | ECKCDSAType\n | ECGDSAType\n | GOST_34_10Type\n | Ed25519Type\n | Ed448Type\n | XMSSType\n | DHType\n | ECDHType\n | X25519Type\n | X448Type\n | DilithiumType\n | ML_DSAType\n | KyberType\n | ML_KEMType\n | McElieceType\n | ClassicMcElieceType\n | FrodoKEMType\n | HSS_LMSType\n | SphincsPlusType\n | SLH_DSAType\n deriving stock (Show, Eq, Ord, Enum, Bounded)\n\n\nYou may note that the constructors are suffixed with `-Type` unlike our earlier primitives - this is because PKI (public key infrastructure) isn’t handled the same as primitive hashes, ciphers, etc. They usually combine the algorithm and any parameters into a single argument, but PK keeps the algorithm and the parameters as separate arguments - thus, we have a separate PKScheme type:\n\n\n data PKScheme\n = RSA Word32\n | SM2 ECGroup\n | ElGamal DLGroup\n | DSA DLGroup\n | ECDSA ECGroup\n | ECKCDSA ECGroup\n | ECGDSA ECGroup\n | GOST_34_10 ECGroup\n | Ed25519\n | Ed448\n | XMSS XMSSParams\n | DH DLGroup\n | ECDH ECGroup\n | X25519\n | X448\n | Dilithium DilithiumMode\n | ML_DSA DilithiumMode\n | Kyber KyberMode\n | ML_KEM KyberMode\n | McEliece Word32 Word32 -- n, t -- TODO: Make a McEliece parameter type\n | ClassicMcEliece ClassicMcElieceParams\n | FrodoKEM FrodoKEMMode\n | HSS_LMS ByteString -- TODO: Make a HSS-LMS parameter type\n | SphincsPlus SphincsPlusMode\n | SLH_DSA SLH_DSAMode\n deriving stock (Show, Eq)\n\n\nThere are also data types for DLGroup, ECGroup, alg-specific params (I’ve omitted the constructors for brevity):\n\n\n data ECGroup\n data DLGroup\n data DilithiumMode\n data KyberMode\n data ClassicMcElieceParams\n data FrodoKEMMode\n data SphincsPlusMode\n data SLH_DSAMode\n data XMSSParams\n\n\nThere is of course a function to get the suggested scheme:\n\n\n pkSuggestedScheme :: PKType -> PKScheme\n pkSuggestedScheme RSAType = RSA 3072\n pkSuggestedScheme SM2Type = SM2 Sm2p256v1\n pkSuggestedScheme ElGamalType = ElGamal MODP_IETF_2048\n pkSuggestedScheme DSAType = DSA DSA_BOTAN_2048\n pkSuggestedScheme ECDSAType = ECDSA Secp256r1\n pkSuggestedScheme ECKCDSAType = ECKCDSA Secp256r1\n pkSuggestedScheme ECGDSAType = ECGDSA Brainpool256r1\n pkSuggestedScheme GOST_34_10Type = GOST_34_10 Gost_256A\n pkSuggestedScheme Ed25519Type = Ed25519\n pkSuggestedScheme Ed448Type = Ed448\n pkSuggestedScheme XMSSType = XMSS XMSS_SHA2_10_512\n pkSuggestedScheme DHType = DH MODP_IETF_2048\n pkSuggestedScheme ECDHType = ECDH Secp256r1\n pkSuggestedScheme X25519Type = X25519\n pkSuggestedScheme X448Type = X448\n pkSuggestedScheme DilithiumType = Dilithium Dilithium6x5\n pkSuggestedScheme ML_DSAType = ML_DSA ML_DSA_6x5\n pkSuggestedScheme KyberType = Kyber Kyber1024_R3\n pkSuggestedScheme ML_KEMType = ML_KEM ML_KEM_768\n pkSuggestedScheme McElieceType = McEliece 2960 57\n pkSuggestedScheme ClassicMcElieceType = ClassicMcEliece ClassicMcEliece_6960119f\n pkSuggestedScheme FrodoKEMType = FrodoKEM FrodoKEM976_SHAKE\n pkSuggestedScheme HSS_LMSType = HSS_LMS \"SHA-256,HW(10,1)\"\n pkSuggestedScheme SphincsPlusType = SphincsPlus SphincsPlus_SHA2_128_Small\n pkSuggestedScheme SLH_DSAType = SLH_DSA SLH_DSA_SHA2_128_Small\n\n\nThen, generating a key is as simple as:\n\n\n prk <- generatePrivKey (pkSuggestedScheme SLH_DSAType)\n\n\nI would like to note that this only covers key generation - unlike the other cryptographic primitives, a given PK algorithm is only associated with key generation, and the various operations of key agreement / key encapsulation / signatures / encryption require their own additional scheme, which will be the subject of the next update.\n\nIts kind of complicated, not every PK algorithm supports every PK operation (in fact, most support only one or two, and you usually need different algorithms for eg key agreement vs signing vs encryption). In the mean time, I have created a table to help disambiguate the various PK algorithm and their uses - I’m still testing, but the preliminary result is this:\n\n\n ALG KA KEM Sign Encrypt PQ Notes\n -----------------------------------------------------------------\n - Prime factorization\n RSA Yes Yes Yes Yes\n - DL Groups\n DH Yes\n DSA Yes\n ELGAMAL Yes\n - ECC\n ECDH Yes\n EC*DSA Yes\n ECIES Yes Not FFI supported\n SM2 Yes Yes Yes Yes\n GOST-34.10 Yes Deprecated\n - Named curves\n X25519 Yes\n X448 Yes\n ED25519 Yes\n ED448 Yes\n - Post-quantum\n MCELIECE Yes Yes\n FRODOKEM Yes Yes\n KYBER Yes Yes\n ML_KEM Yes Yes\n DILITHIUM Yes Yes\n ML_DSA Yes Yes\n SPHINCS_PLUS Yes Yes\n SLH_DSA Yes Yes\n HSS_LMS Yes Yes Stateful\n XMSS Yes Yes Stateful\n\n\n# Post Quantum support\n\nThe keen-eyed among you will already have noticed the increased support for post-quantum algorithms, specifically the support for the recently approved FIPS / NIST final selection of post-quantum algorithms!\n\n * Key encapsulation\n * McEliece\n * ClassicMcEliece\n * FrodoKEM\n * Kyber\n * ML_KEM (NIST approved Kyber)\n * Digital signatures\n * Dilithium\n * ML_DSA (NIST approved Dilithium)\n * SphincsPlus\n * SLH_DSA (NIST approved Sphincs)\n * XMSS (stateful, use with caution)\n * HSS_LMS (stateful, use with caution)\n\n\n\nThe definitions for their parameter types is not super interesting, aside from my having to spend a few days* scouring the C++ source code to find their precise formats and arguments, stuff like inconsistent casing really screws with accurately identifying algorithms, etc, but that’s all taken care of now.\n\n> I am glossing over a lot here. It was a huge pain in the ass tracking down every algorithms’ specific, inconsistently capitalized capitalization-sensitive algorithm and parameter identifier because they only exist as magic strings in the C++ source code! For example, the algorithm is `SPHINCS+` with `SHA512` but the params actually need to be formatted as `SphincsPlus` and `sha2`, and this is just plain not mentioned anywhere! It’s fricken terrible! And you don’t have to deal with that anymore!\n\nYou still need an appropriate version of botan to enable support, however, and that takes us to our final section of the update!\n\n# Versioning support\n\nThese bindings were originally written against botan `3.2`, and in the time since, botan has received several updates - `3.11` is now available, and enough things have been added that I am currently determining how I am going to handle versioning support - which is a problem.\n\nOne of the issues with adding support for new algorithms is that I still need to handle botan versions that don’t have them yet - and while the lowest `botan-bindings` take bytestring names and parameters for algorithm identifiers, now that we have data types for algorithms, I need some way of indicating to the user whether a given algorithm is actually available.\n\nWhy is it a problem? Well, botan provides `botan/build.h` as file to import to gain access to the `BOTAN_HAS_<alg>` defines, which sounds perfect! Just import it, and use `CPP` and conditional compilation, right? Something like:\n\n\n module Botan.Low.PubKey where\n\n import Botan.Bindings.PubKey\n\n -- Like this\n #include <botan/build.h>\n\n -- So I could do things like:\n pkTypeIsSupported :: PKType -> Bool\n #if defined(BOTAN_HAS_RSA)\n pkTypeIsSupported RSAType = True\n #endif\n pkTypeIsSupported _ = False\n\n\nDid that work? Nope!\n\nIt fails because `build.h` has an indented define that causes the strict GHC C preprocessor to fail with an error - this little line here:\n\n\n #ifndef BOTAN_DLL\n #define BOTAN_DLL __attribute__((visibility(\"default\")))\n #endif\n\n\nIf anyone knows how to allow GHC to parse this without failing, it would be really nice to be able to just import the file. Otherwise, to fix this we need to either pre-parse that file as as pre-build step to fix it or otherwise generate the list of `BOTAN_HAS_<alg>` supported algorithms for us to consume, which seems like a lot of work for what ultimately is a fragile bandaid - really this should be fixed by `Botan C++` itself so I will probably create an issue for them.\n\nSince I can’t include the file directly, for now I have instead resorted to using `CApiFFI` to import the defines as constants one by one.\n\n\n -- Since the indented #define doesn't allow us to use the constants directly for\n -- conditional compilation, we will import the defines and allow the compiler to\n -- elide things via constant folding\n\n -- Prime factorization\n foreign import capi safe \"botan/build.h value BOTAN_HAS_RSA\" botan_has_rsa :: CInt\n\n -- DL\n foreign import capi safe \"botan/build.h value BOTAN_HAS_DL_GROUP\" botan_has_dl_group :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_DIFFIE_HELLMAN\" botan_has_diffie_hellman :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_DSA\" botan_has_dsa :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_ELGAMAL\" botan_has_elgamal :: CInt\n\n -- ECC\n foreign import capi safe \"botan/build.h value BOTAN_HAS_ECC_GROUP\" botan_has_ecc_group :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO\" botan_has_ecc_public_key_crypto :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_ECDH\" botan_has_ecdh :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_ECDSA\" botan_has_ecdsa :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_ECKCDSA\" botan_has_eckcdsa :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_ECGDSA\" botan_has_ecgdsa :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_SM2\" botan_has_sm2 :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_GOST_34_10_2001\" botan_has_gost_34_10_2001 :: CInt\n\n -- Named curves\n foreign import capi safe \"botan/build.h value BOTAN_HAS_X25519\" botan_has_x25519 :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_X448\" botan_has_x448 :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_ED25519\" botan_has_ed25519 :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_ED448\" botan_has_ed448 :: CInt\n\n -- Post-quantum\n foreign import capi safe \"botan/build.h value BOTAN_HAS_MCELIECE\" botan_has_mceliece :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_CLASSICMCELIECE\" botan_has_classicmceliece :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_FRODOKEM\" botan_has_frodoKEM :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_KYBER\" botan_has_kyber :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_KYBER_90S\" botan_has_kyber_90s :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_ML_KEM\" botan_has_ml_kem :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_DILITHIUM\" botan_has_dilithium :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_ML_DSA\" botan_has_ml_dsa :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_HSS_LMS\" botan_has_hss_lms :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2\" botan_has_sphincs_plus_with_sha2 :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE\" botan_has_sphincs_plus_with_shake :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_SLH_DSA_WITH_SHA2\" botan_has_slh_dsa_with_sha2 :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_SLH_DSA_WITH_SHAKE\" botan_has_slh_dsa_with_shake :: CInt\n foreign import capi safe \"botan/build.h value BOTAN_HAS_XMSS_RFC8391\" botan_has_xmss_rfc8391 :: CInt\n\n\n -- TODO: Also use has_dl_group, has_ecc_group\n pkTypeIsSupported :: PKType -> Bool\n pkTypeIsSupported X25519Type = botan_has_x25519 > 0\n pkTypeIsSupported X448Type = botan_has_x448 > 0\n pkTypeIsSupported RSAType = botan_has_rsa > 0\n pkTypeIsSupported McElieceType = botan_has_mceliece > 0\n pkTypeIsSupported ClassicMcElieceType = botan_has_classicmceliece > 0\n pkTypeIsSupported FrodoKEMType = botan_has_frodoKEM > 0\n pkTypeIsSupported KyberType = botan_has_kyber > 0 || botan_has_kyber_90s > 0\n pkTypeIsSupported ML_KEMType = botan_has_ml_kem > 0\n pkTypeIsSupported DilithiumType = botan_has_dilithium > 0\n pkTypeIsSupported ML_DSAType = botan_has_ml_dsa > 0\n pkTypeIsSupported HSS_LMSType = botan_has_hss_lms > 0\n pkTypeIsSupported SphincsPlusType = botan_has_sphincs_plus_with_sha2 > 0 || botan_has_sphincs_plus_with_shake > 0\n pkTypeIsSupported SLH_DSAType = botan_has_slh_dsa_with_sha2 > 0 || botan_has_slh_dsa_with_shake > 0\n pkTypeIsSupported XMSSType = botan_has_xmss_rfc8391 > 0\n pkTypeIsSupported Ed25519Type = botan_has_ed25519 > 0\n pkTypeIsSupported Ed448Type = botan_has_ed448 > 0\n pkTypeIsSupported ECDSAType = botan_has_ecc_public_key_crypto > 0 && botan_has_ecdsa > 0\n pkTypeIsSupported ECKCDSAType = botan_has_ecc_public_key_crypto > 0 && botan_has_eckcdsa > 0\n pkTypeIsSupported ECGDSAType = botan_has_ecc_public_key_crypto > 0 && botan_has_ecgdsa > 0\n pkTypeIsSupported SM2Type = botan_has_ecc_public_key_crypto > 0 && botan_has_sm2 > 0\n pkTypeIsSupported GOST_34_10Type = botan_has_ecc_public_key_crypto > 0 && botan_has_gost_34_10_2001 > 0\n pkTypeIsSupported DHType = botan_has_diffie_hellman > 0\n pkTypeIsSupported ECDHType = botan_has_ecdh > 0\n pkTypeIsSupported DSAType = botan_has_dsa > 0\n pkTypeIsSupported ElGamalType = botan_has_elgamal > 0\n\n\nThis isn’t perfect - instead of conditional compilation, we have to rely on constant folding to eliminate dead branches, which isn’t ideal, but at least now we can check whether algorithms are properly supported without having to attempt generating a key or context and then catching a NOT_IMPLEMENTED exception.\n\nAnyway I have spent a few days testing against various versions of botan, and it is extremely satisfying to be able to print out and verify what algorithms are supported by this installation, and see it change when I change installation versions. I intend to do the same thing for the other modules in the future, adding versioning support.\n\nAnother thing is that botan 4 is coming with a current ETA of 2027, which will be the first major version jump we have to handle, so it is better to get on this sooner rather than later - we should be able to absorb any changes due to the ergonomics refactor, since we no longer need to stay 1:1 with the bindings. I don’t expect huge changes to the FFI, though several algorithms are slated to be removed. Good thing I’m adding versioning support now, rather than later.\n\n# Additional miscellanea\n\nI am also looking into the view functions that have been added in 3.5 - they are designed to help avoid some of the song-and-dance routine required to give some algorithms the right-sized buffers by instead giving you access to the buffer and making you copy it yourself.\n\nOn the main branch, someone opened a PR to fix some base64 decoding for 3.12, so Joris has merged a PR, and new minor versions are on their way!\n\n# Health\n\nMy health has continued to improve, so much that I have recently broken a personal record / hit a milestone and was able to walk 12 miles (a half-marathon!) in a single day\n\nOne year ago, I was having to consider getting a cane because of my leg, but this weekend, I was able to jump properly on it for the first time in a very long time. I have only been able to regain my health, because this project and your support affords me the time I need to focus on it - I can work on my PT while mulling over problems, and I don’t have the stress of a boss clocking my time and forcing me to sit down for 8-10 hours a day.\n\nThat means working on this project is a joy that gives me energy, rather than one that drains me - and that keeps these updates keep coming!\n\n* * *\n\nThat’s all for now!",
"title": "Botan bindings devlog"
}