{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreibnxyme5viuvojdyyrdlnuou4br2t5uzeai6kzwhcbaunizf3ezcq",
"uri": "at://did:plc:pi6woz4d47bkuws673w2il2r/app.bsky.feed.post/3mkbhxwd4xlm2"
},
"path": "/t/botan-bindings-devlog/6855?page=11#post_202",
"publishedAt": "2026-04-24T20:27:09.000Z",
"site": "https://discourse.haskell.org",
"tags": [
"whims",
"holes",
"more recent thread",
"solution to this",
"Botan C++",
"old set of bindings",
"wrote up a proper proposal",
"accepted and funded",
"yet another ecosystem discussion thread, this time about memory",
"memalloc"
],
"textContent": "# Botan: Project Recap and Major Ergonomics Changes\n\nI have been a little quiet on the forums, and you know what that means - I’ve been working on something sizeable, and all my energy has been put towards ‘doing’ rather than ‘talking’. I do tend to cycle between focuses, it helps keep things interestnig, and stops me from getting too far into the weeds, as I am often wont to do.\n\nThis last month’s focus has been botan, and I am happy to report some significant progress. A quick summary, I’d estimate the refactor to be 40% complete by volume / 60% complete by effort, with the `RNG`, `Hash`, `BlockCipher`, `Cipher`, and `MAC` modules more or less done, and the remaining refactor needs being split fairly evenly between the `PubKey` modules and the collection of miscellaneous specialized algorithms ranging from `Bcrypt` to `ZFEC`. Less completion by volume because there is still plenty to do, but more completion by effort because we’re running into fewer and fewer novel requirements, which is both nice and according to plan.\n\nNow, I’ve found myself at a pretty point to stop and do a recap - how we got here, what we’ve done, what we are doing next, and what the future holds - this should be helpful because this thread has grown quite large, spanning multiple years now, and as some readers have pointed out, it can be a bit difficult to follow what precisely is happening.\n\nSo, the recap before details on the refactor progress!\n\n# Recap: Some history\n\nHaskell has always been subject to the whims of its contributors, for better and for worse. Ten years ago, a significant number of critical packages were dependent on the work of one person, who in a rather explosive exit abandoned them while simultaneously forbidding their takeover.\n\nThis left several rather sizeable holes in the Haskell ecosystem, in particular, `cryptonite`, one of if not the Haskell ecosystem’s main source of cryptographic primitives, and one of its dependencies, `memory`, also among the set. Nothing went wrong immediately - they were stable packages, after all - but over time Haskell and GHC continued to change and evolve, and these libraries slowly began to suffer from bitrot and aging APIs.\n\nForks have been made, but without the original author, maintenance is difficult, and the original code is no longer trusted - the tolerance of unverified, handwritten cryptography being predicate on the trust in the author, now absent.\n\nIf you really want to get into it, there are the links already provided, and here is a more recent thread covering some discussion of the topic and how it still affects the ecosystem to this day.\n\n# Research for a Proposal\n\nMy solution to this was to develop some bindings to a suitable popular cryptography library. In the end, I chose to develop bindings to Botan C++, for 3 reasons:\n\n 1. It fit our rather specific needs - free and open source, with an appropriate license, good coverage of algorithms, a C FFI, well-supported across multuple architectures, and most importantly, a wide userbase including significant corporate usage, meaning that this library was under active development and could be counted on to receive development for the foreseeable future.\n\n 2. It was written in C++, which meant I could dive into the source code and trace things out if I needed - and this was important because at the time, Botan was not very well documented, so there was a lot of that _(Botan has greatly improved documentation since this project started, and this project has improved alongside it)_.\n\n 3. There existed an old set of bindings to an out of date version of botan, called `z-botan`. This was dependent on a custom base with bespoke wrappers around _everything_ , it used custom C++ to bind to Botan 2 rather than 3, and it didn’t build on a modern setup, so using that codebase was out of the question - but it made for a wonderful template, and gave a good the estimate for the size of the task of producing new bindings.\n\n\n\n\nThese choices were significant factors in the development of the resulting bindings, and I’m glad that I took the time to do the research on various candidates and alternatives before settling on Botan.\n\n_Even now I am pleased to briefly mention that Botan supports configuring compilation for WASM, so some of my recent work this last month has been towards eg support for building`botan-bindings` with `wasm32-wasi-ghc` - not quite ready to show off just yet, but in due time._\n\n# Initial project run, `botan-bindings` and `botan-low` released\n\nWith a well-defined goal, I got to work, and after making some progress, I wrote up a proper proposal which was accepted and funded\n\nA major point of this proposal and project is that the resulting library is actually owned by the Haskell Foundation, specifically to stop the problem of ownership of critical libraries threatening the stability of the ecosystem - in other words, I have given up ownership of the codebase and respository in order to ensure this, though I remain as a significant contributor. (Don’t worry - this was my idea, and it always the plan)\n\nDue to the funding, I was able to work on Botan full-time for several months, and in that time I managed to produce the `botan-bindings` and `botan-low` packages, giving us access to a solid and reliable cryptography system.\n\nThe low-level bindings were effective, and despite the imperative design, a result of a nearly 1:1 API translation, stable, with ergonomics then-intended to be provided as part of a higher-level interface.\n\nSo what did contain?\n\nWell, botan is a comprehensive cryptographic “kitchen sink” - it seeks to present a wide variety of algorithms under a unified interface, allowing the developer to choose what algorithms to employ.\n\n> NOTE: This is in comparison to more opinionated libraries like the `libsodium / nacl / saltine` family, which select a single algorithm per each intended use case - so there’s no configuration, because there aren’t any choices to make.\n\nIt of course contains your usual gaggle of suspects:\n\n * Random Number Generators\n * Hash\n * Block Ciphers\n * Cipher\n * Message Authentication Codes\n * Public Key Encryption & Signing\n * Bcrypt\n * Key Derivation\n * Password Hashing\n * One-time passwords\n\n\n\nBut really, the strength of botan is the sheer number of algorithms it supports - it has all the algorithms that you know and love, and a whole slew of others you’ve probably never heard of. Part of the goal of botan is, if you have botan, you shouldn’t need another cryptographic library, meaning it can serve as a convenient cryptographic foundation for the ecosystem, if we want.\n\nIn total, it has 26-ish hash algorithms and 24-ish block cipher algorithms (or more, depending on how you count configurations), and those algorithms then get used as algorithm parameters for higher-level symmetric ciphers and message auth codes, and some of those also have configuration parameters - so you can really customize your cryptographic system if you know what you are doing. It even has post-quantum algorithms which I’ve been eager to get into (I meant to last year, but my health took priority).\n\nIt isn’t perfect - specifically, our bindings are limited to the C FFI, and not all features are exposed in the C FFI - chiefly, not all X509 features are available, and the Botan TLS implementation is completely unavailable. There are things that can be done about this - for example writing our own C FFI bindings for the unavailable features - but while desirable, we do have native implementations that rely on the `crypton` fork, so it is less critical / lower priority.\n\nUnfortunately, sometimes good things must come to an end, and after the initial release, funding ended, and I had take a step back to find another means of employment - and so began a period of quiet maintenance, during which Joris joined as a contributor, and he has done a wonderful job at keeping the day-to-day tasks managed ever since.\n\nIn the meantime, the job I took at a fintech company ended up turning into 60-hour weeks of AI-fueled hell, which began impacting my health, and ultimately I was glad to leave after saving up enough. It was not a healthy environment, and it resulted in my abstaining from any form of software engineering for several months as I recuperated, started down my current journey of good health, and figured out if I even still liked programming.\n\n# The Project Returns\n\nWhen Jose called 6 or so months ago, and asked if I was interested in doing some more botan work, I wasn’t sure if I ready to return to programming just yet - I was still feeling really burnt out from the fintech thing, I definitely still needed some rest, but I remembered how much I enjoyed working on Botan on the intial project run, and I decided that I really don’t hate programming, just forced vibe coding and middle managers who think that yelling is acceptable behavior.\n\nSo, I set some limits - my health comes first, and I would be returning in a partial capacity, rather than full-time - but I would be returning, and this time as a provisional Haskell Foundation member, with funding for 3 months, with the potential to become a full member with funding for the rest of the year (this did happen about a month ago, so hooray for me for that!).\n\nSetting things up took a few months - but that was okay, it let me continue to focus on improving my health, and it gave me time to slowly get back into the project, which was more than a little bit daunting. I hadn’t touched the code in a while, and there was a lot of it, so the first month or so was mostly spent reading my own code and refamiliarizing myself with the bindings. Seeing it with fresh eyes, was a good reminder of where I last was in the project - lots of old plans, things blocking those plans, higher-level code waiting to be used once unblocked.\n\n# Observations\n\nOne of the biggest pain points was the bespoke `Make / Remake / mkBindings` modules that I had built, in order to maintain a zero-dependency footprint. I had tried to refactor it more than once before, but the resulting sprawl of spaghetti served to occlude how things worked more than it provided code re-use.\n\nIt was not a bad idea in theory - the original `cryptonite` followed the same ethos, one of the reasons for its own longevity, even after the absence of its author. However, the original `cryptonite` _did depend on`memory`_, and there being yet another ecosystem discussion thread, this time about memory, I realized that instead of writing memory routines by hand for `botan` (since I couldn’t use `memory`), _I could take a stab at filling that gap as well_ - to refactor the binding generators AND to provide a replacement for `memory`.\n\nTo do that, 2 functions were most important: `allocInit` for allocating FFI-capable pinned bytestrings, and `withByteArray` for accessing their pointers. And so the memalloc library was borne, a total overhaul of `memory` that seeks to provide a much stronger set of memory abstractions.\n\nThere’s the whole thread about it, but it is basically a categorization of memory allocations, handles, references, pointers, and arrays, that turns eg `withByteArray` AKA `withPtr :: (Storable) a -> (Ptr p -> IO b) -> IO b ` into a more generalized\n\n\n class (Memory mem, Memorable memo) => WithMemory mem memo where\n withMemory :: memo -> (mem (MemRep memo) -> IO a) -> IO a\n\n\n`memory` also separated `ByteArrayAccess` from `ByteArray (Allocation)`, which really inspired me to tear off allocators as its own class that works with `Memory` types, a la:\n\n\n class (Memorable (Allocation alr)) => Allocator alr where\n\n type family Layout alr :: Type\n type family Allocation alr :: Type\n allocInit\n :: alr\n -> Layout alr\n -> (Mem (Allocation alr) (MemRep (Allocation alr)) -> IO ())\n -> IO (Allocation alr)\n\n\nThis is nice, because `type WithPtr memo = WithMemory Ptr memo` and `withPtr = withMemory`. It makes using pointers / the C FFI a breeze for any memory-backed data structure, no need to copy or marshal through ByteString - and we can control where and how the memory is allocated, which is a big deal for memory safety and security.\n\nIt isn’t published yet, as I’m still testing it out, but it has made refactoring `botan-low` significantly easier, and cleaner, and it has been immediately useful in replacing all that `mkBindings` junk, plus it has helped provide a unified BotanObject class for wrapping opaque pointers. So, having proven its utility, it is due for release as a prequisite for this refactor.\n\n> I do have this tendency to write a lot of extraneous code for far-off future plans, that usually doesn’t make the first cut - an abstraction of an abstraction and so on - but I always do tuck those bits of code away for another day, and so I often ‘displace’ such explorative code to more speculative libraries. It is always nice to see some of it make the cut, and actually get used.\n\nThat’s sort of where we are now - or, where we were as of the last few updates, which have been more or less concerned with the integration of `memalloc` into `botan-low` under a `botan-low-refactor` branch - successfully getting rid of `Make / Remake / mkBindings` without affecting anything else.\n\n# Planning\n\nRefactoring low-level code let me clean things up, which eases the burden of maintenance, but it didn’t change the API. What does change the API, is being unblocked, and looking at your code with fresh eyes - all the easier to see various flaws that you were blind to before, or unable to deal with.\n\nSee, the new `memalloc` support allows us to supply the allocator as an argument - this allows us to use things other than ByteStrings for interacting with the C FFI. However, this would also change the API, which breaks with our original plans where `botan-low` remains 1:1 with the Botan C FFI - like `botan-bindings` but with ByteStrings.\n\nIn the old plan, allocators would be part of `botan(-high)`, because the allocator is a high-level concept that isn’t part of the Botan C FFI - but we can’t do that, because _it would need to be part of the lower-level API first_.\n\nSo, we can either stay true to the original plan API, or we can break with the API and make a new plan with changes - and since `botan-bindings` already serves as the 1:1 interface, and since `memalloc` gives us pointer access without going through ByteStrings, it wasn’t a hard decision - only `botan-bindings` needs to be 1:1.\n\nThis turned out to be the snowball that started an avalanche, as this in turn unblocked a whole host of things that have now made their way into `botan-low` as part of a _significant overhaul of the API_.\n\n# Ergonomic changes coming down the pipeline\n\nOkay, since `botan-low` no longer needed to be 1:1, this meant a few things\n\n * We could clean up and fix some of the oddities / inconsistencies in the botan C FFI API\n * We can rename a bunch of things for consistency\n * We can bring in a bunch of stuff from `botan(-high)`, like algorithm data types (instead of having to deal with botan’s finnicky algorithm name syntax)\n\n\n\nAnother thing, the first version of `botan-bindings` and `botan-low` were deliberately zero-dependency packages, aside from base and core libraries - that’s right, the bindings only requires `base`, plus just `bytestring`, `deepseq`, and `text` for low. This is intended to help with churn - zero dependencies means minimal surface for breakage, and stability is important for security. It also means that reasoning about the code and refactoring it isn’t difficult - all that you need to know about is right there.\n\nThe zero-dependency philosophy is something we are going to keep, but we are going to bend that rule ever so slightly, because we of course have one new dependency - the new `memalloc` library that has been developed firstly to replace `memory` with something having a modern API, and secondly to support refactoring `botan-low`.\n\n`memalloc` itself follows the same zero-dependency philosophy, except it includes a few more core libraries as dependencies, which I decided to control via a flag system, that I will be bringing back into `botan-low` to allow for selectively including support. This makes it easy to control through a `cabal.project` stanza eg:\n\n\n package *\n flags:\n -- Example: These are all on by default, but we can turn them off\n -use-array\n -use-bytestring\n -use-containers\n -use-ghc\n -use-mtl\n -use-primitive\n -use-text\n -use-vector\n\n\nEven though they’re not really being used yet, they will be, and we still retain the ability to turn on or off support for dependencies, as to include them safely when they are available. I’ve even done a little prep for better supporting non-GHC compilers eg by starting to gate off GHC-specific code behind a flag that turns on `-DUSE_GHC` and `#if defined(USE_GHC)` and so on.\n\nOnce in the project, we get to use our new `memalloc` library for all of our C FFI needs - its very consistent, and as a result, most of the functions in the `botan-low-refactor` branch (publically available soon!) are some variation on the form:\n\n\n objectDo :: Object -> arg -> ... -> IO ()\n objectDo obj arg ...\n = mask_ $ withObject obj $ \\ objPtr ->\n withArg arg $ \\ argPtr ->\n -- ...\n throwBotanIfNegative_ $ botan_object_do objPtr\n (ConstPtr argPtr)\n -- ...\n\n\nwhere `Object` is some opaque pointer, nicely managed by the `memalloc` classes which provide allocation and pointer access. Sometimes we need an `alloca` here, or an `allocaMany @[CSize,...]` there, and if we need to return a result, we use `throwBotanNegative` (no underscore) - all of this a vast improvement over the first-version `Make / Remake / mkBindings` modules, which was effective but terribly unorganized and a nightmare for maintenance.\n\nAnother benefit of the new memory management and allocator functions, most botan functions only require a few lines of code. There’s sometimes extra verbage due to needing to provide the allocator (currently, always ByteStringAllocator, but we’re preparing to expose the allocator as an argument), and we currently need to follow up every `allocInit ByteStringAllocator $ \\ fPtr` with a `withPtr fPtr $ \\ ptr -> ...` - for example:\n\n\n hashFinalize :: Hash -> IO HashDigest\n hashFinalize h = withHash h $ \\ hPtr -> do\n sz <- hashGetDigestSize h\n allocInit ByteStringAllocator sz $ \\ fPtr -> do\n withPtr fPtr $ \\ digestPtr -> do\n throwBotanIfNegative_ $ botan_hash_final hPtr digestPtr\n\n\nBut this isn’t too bad, and it’s only because `allocInit` yields the top `ForeignPtr` instead of the base `Ptr` (this is extremely sensible in-context, it is technically correct, the best kind of correct) that we just need to drill down an extra level - basically, we’re just “missing” a convenience function.\n\nSo, it is a little boiler-platey at the moment, but highly consistent, and now we don’t have dozens of inscrutible functions named things like `mkGetBoolCode_csize` or `mkWithObjectSetterCBytesLen` that we have to look up to remember their exact behaviors - though repeat / common cases such as `objectGetName` and `objectGetKeySpec` do get a convenience function tucked away in the internal prelude.\n\nSpeaking of, the new internal prelude also helps with readability, and all of the refactored modules are much cleaner now. For example, `Botan.Low.Hash` imports have gone from this:\n\n\n import Botan.Bindings.Hash\n import Botan.Low.Error.Internal\n import Botan.Low.Internal.ByteString\n import Botan.Low.Internal.String\n import Botan.Low.Make\n import Botan.Low.Remake\n import Data.ByteString (ByteString)\n import Foreign.C.Types\n import Foreign.ForeignPtr\n import Foreign.Ptr\n\n\nto this:\n\n\n import Botan.Low.Internal.Prelude\n import Botan.Bindings.Hash\n\n\nThe other refactored modules have seen a similar reduction in noise - basically, after declaring `Memorable` instances and a newtype wrapper, we get straight to implementing the bindings, no fuss - so, all of the `memalloc` work was really worth it.\n\nI’m still giving `memalloc` a shakedown - with something as critical as security and cryptography, it really pays to do it right - but seeing how smooth it’s made this refactoring, I have enough confidence in it now to begin the process of releasing it.\n\nThere are a few things I need to move over to `memalloc`, a few other things that need polish first, but I feel confident now that I’ve refactored most of the important modules, and because of that, I have started pulling in all those higher-level goodies I talked about earlier. Some of this will have been mentioned in recent updates, but we’ll go over everything because it is a recap.\n\n# Algorithm Data Types\n\nProbably the biggest deal / change in the API is we no longer require the developer / user to initialize an algorithm context via a specially-formatted algorithm name. I did my best to provide patterns and functions to do this for the user, but even so, passing around (byte)string identifiers is terrible.\n\n\n -- Ie, this\n type BlockCipherName = ByteString\n pattern\n AES128\n , AES192\n , AES256\n -- ...\n :: BlockCipherName\n pattern AES128 = BOTAN_BLOCK_CIPHER_AES_128\n pattern AES192 = BOTAN_BLOCK_CIPHER_AES_192\n pattern AES256 = BOTAN_BLOCK_CIPHER_AES_256\n -- ...\n\n -- Has become\n data BlockCipherType\n = AES128\n | AES192\n | AES256\n -- ...\n\n\nSo, now we have proper data types for algorithms, and their parameters too. This introduces a lot of type safety, and we translate to a bytestring name only when we’re initializing a context:\n\n\n h <- hashInit SHA3\n\n\nIn retrospect, it was kind of dumb to keep that for the high-level, just so the API could stay 1:1 - I’m glad we have chosen a different path.\n\n# Key / Nonce / Size specifiers\n\nSo, with that out of the way, there’s another common data type - size specifiers. A lot of algorithms have a `botan_foo_keyspec` function that returns a triple `(Int, Int, Int)`, which describes the range of sizes of valid keys, as `(min, max, mod)`.\n\nRather than pass around undocumented tuples, I’ve pulled in the `SizeSpec` class, calling it that instead of `KeySpec` because we can use it to describe eg nonce sizes as well.\n\n\n type role SizeSpec phantom\n data SizeSpec a\n = SizeEnum Int Int Int -- NOTE: Is min max mod\n | SizeList [ Int ] -- INVARIANT: Not empty, in increasing order\n | SizeExact Int\n deriving stock (Eq, Ord, Show)\n\n\nSo, now `blockCipherKeySpec`, `cipherKeySpec`, `macKeySpec`, etc all return one of these, and there’s a phantom type to track were it came from too.\n\n# Static algorithm sizing functions\n\nOne of the idiosyncracies of botan is that, despite being statically calculable on a per-algorithm basis, all queries all require a live object - for key specs, nonce block and digest sizes. So, even though we know that SHA3 has a 64-byte digest, we must still initialize a context to ask. This leads to code like this:\n\n\n enc <- cipherInit ChaCha20Poly1305 Encrypt -- Potentially slow operation\n > (_,keySize,_) <- cipherGetKeyspec enc\n > nonceSize <- cipherGetDefaultNonceLength enc\n\n\nSince we now have algorithm data types I’d rather look this up / calculate it from a table, rather than initialize a potentially sizeable context - so I’ve gone and calculated those values, and now we just need to pass in the algorithm type:\n\n\n blockCipherBlockSize :: BlockCipherType -> Int\n blockCipherBlockSize Blowfish = 8\n -- ... more algorithms\n blockCipherBlockSize Twofish = 16\n blockCipherBlockSize SHACAL2 = 32\n blockCipherBlockSize Threefish512 = 64\n -- Calculated via a function that is not exported\n\n\n# Splitting context vs algorithm functions\n\nSince we now have some functions that take just the algorithm type instead of a full live context, we need to separate the two. The botan FFI is pretty inconsistent in its nomenclature, but it does use `get` terminology already in a few places, so, now things are named according to this pattern:\n\n\n -- Context functions are named with 'get'\n hashGetName :: Hash -> IO ByteString\n hashGetBlockSize :: Hash -> IO Int\n hashGetDigestSize :: Hash -> IO Int\n\n h <- hashInit htype\n n <- hashGetName h\n bsz <- hashGetBlockSize h\n dsz <- hashGetDigestSize h\n\n -- Algorithm functions are shorter\n hashName :: HashType -> ByteString\n hashDigestSize :: HashType -> Int\n hashBlockSize :: HashType -> Int\n\n n = hashName htype\n bsz = hashBlockSize htype\n dsz = hashDigestSize htype\n\n\nAlso, I’ve tried to be more consistent with naming.\n\n# Preferred sizes\n\nBotan doesn’t always provide functions for a default or preferred size. This complicated eg key and nonce generation:\n\n\n rng <- rngInit \"user\"\n (_,keySize,_) <- blockCipherGetKeyspec blockCipher\n key <- rngGet rng keySize\n\n\nNow, algorithms with keys and nonces have sets of functions like this:\n\n\n cipherPreferredKeySize :: CipherType -> Int\n cipherPreferredKeySize (CBC bc _) = blockCipherPreferredKeySize bc\n cipherPreferredKeySize (CFB bc _) = blockCipherPreferredKeySize bc\n cipherPreferredKeySize (XTS bc) = 2 * blockCipherPreferredKeySize bc\n cipherPreferredKeySize ChaCha20Poly1305 = 32\n cipherPreferredKeySize (GCM bc128 _) = blockCipherPreferredKeySize bc128.un\n cipherPreferredKeySize (OCB bc128 _) = blockCipherPreferredKeySize bc128.un\n cipherPreferredKeySize (EAX bc _) = blockCipherPreferredKeySize bc\n cipherPreferredKeySize (SIV bc128) = 2 * blockCipherPreferredKeySize bc128.un\n cipherPreferredKeySize (CCM bc128 _ _) = blockCipherPreferredKeySize bc128.un\n\n -- NOTE: Uses the system RNG for now, probably should be UserThreadsafe\n generateCipherKey :: CipherType -> Int -> IO (Maybe CipherKey)\n generateCipherKey c sz | validSize (cipherKeySpec c) sz = Just <$> systemRNGGet sz\n generateCipherKey _ _ = pure Nothing\n\n generatePreferredCipherKey :: CipherType -> IO CipherKey\n generatePreferredCipherKey c = systemRNGGet (cipherPreferredKeySize c)\n\n\nI’ve gone and done my best to research preferred key and nonce-sizes where it wasn’t obvious, and tried to match common standards - but it isn’t perfect, so there’s still eg `generateKey` vs `generatePreferredKey`.\n\nAll in all, this makes generating keys and nonces dead simple, and yet again makes things safer - this is probably my second greatest pain point, the first being the bytestring algorithm names which have also been taken care of!\n\n# Easy functions\n\nYou can tell where this is going - now that we have functions that take the algorithm type instead of a live context, we can actually hide the context entirely.\n\nSo, I’ve gone and done that too:\n\n\n {-\n -- Hashing\n > hash SHA3 \"Fee fi fo fum\"\n -}\n hash :: HashType -> ByteString -> IO HashDigest\n hash htype msg = do\n h <- hashInit htype\n hashUpdateFinalize h msg\n\n {-\n -- Block ciphers\n > bc = AES256\n > k <- generatePreferredBlockCipherKey bc\n > ct <- blockEncrypt bc k msg\n > pt <- blockDecrypt bc k ct\n -}\n blockEncrypt :: BlockCipherType -> BlockCipherKey -> ByteString -> IO BlockCipherText\n blockEncrypt bcType key msg = do\n bc <- blockCipherInit bcType\n blockCipherSetKey bc key\n blockCipherEncryptBlocks bc msg\n\n blockDecrypt :: BlockCipherType -> BlockCipherKey -> BlockCipherText -> IO ByteString\n blockDecrypt bcType key ct = do\n bc <- blockCipherInit bcType\n blockCipherSetKey bc key\n blockCipherDecryptBlocks bc ct\n\n {-\n -- Ciphers\n > c = ChaCha23Poly1305\n > k <- generatePreferredCipherKey c\n > n <- generatePreferredCipherNonce c\n > ct <- encrypt c k n msg\n > pt <- decrypt c k ct\n -}\n encrypt :: CipherType -> CipherKey -> CipherNonce -> ByteString -> IO CipherText\n encrypt cipherType key nonce msg = do\n c <- cipherInit cipherType CipherEncrypt\n cipherSetKey c key\n cipherStart c nonce\n cipherEncrypt c msg\n\n decrypt :: CipherType -> CipherKey -> CipherNonce -> CipherText -> IO ByteString\n decrypt cipherType key nonce ct = do\n c <- cipherInit cipherType CipherDecrypt\n cipherSetKey c key\n cipherStart c nonce\n cipherDecrypt c ct\n\n {-\n -- Message authentication codes\n > m = HMAC SHA3\n > k <- generatePreferredMACKey m\n > n <- generatePreferredMACNonce m\n > tag <- mac m k n msg\n > valid <- auth m k n msg tag\n -}\n\n mac :: MACType -> MACKey -> Maybe MACNonce -> ByteString -> IO MACDigest\n mac macType key nonce input = do\n m <- macInit macType\n macSetKey m key\n maybe (pure ()) (macSetNonce m) nonce\n -- Can we do this ^^^ more idiomatically? Maybe: traverse_\n macUpdate m input\n macFinalize m\n\n auth :: MACType -> MACKey -> Maybe MACNonce -> ByteString -> MACDigest -> IO Bool\n auth macType key nonce input dg = do\n dg' <- mac macType key nonce input\n return $ dg' == dg\n\n\nThis makes the API so much easier to use, and I’m not quite done yet - I’m probably going to use `unsafePerformIO` for deterministic ‘easy’ functions, and a `MonadRandom` for ones requiring an RNG context - there’s a MonadRandom class I’ve got in the speculative wing - think `random:RandomGen` except it’s got an associated `Distribution` type rather than assuming uniform distribution, which really works for eg key specifiers which are technically descriptions of the multi-modal distribution of valid keys - so, obviously useful here.\n\n# Next up: Public Key Encryption\n\nSo, that’s where we’re at, and these changes are what’s coming down the pipeline. Getting rid of the `mkBindings` spaghetti has really unblocked things, and our decision to pull more things into `botan-low` has really improved the interface, and this in turn has made everything easier. `memalloc` has been so well-behaved, it has been mostly a straight shot of “pick a module, refactor it”, and one by one they are getting refactored.\n\nThere is a course some sort of sequence imposed by the structure of botan - ciphers require block ciphers, macs require hashes, etc - but those are done now, and so Public Key Encryption is next.\n\nThat’s all for now!",
"title": "Botan bindings devlog"
}