It’s update time again!
Today’s update is a bit of documentation,
-
botan-bindings
has been more or less documented, first pass.
- Each function’s documentation should at least contain its C declaration
- Most functions have documentation cloned from the Botan C FFI header file
- The correctness of the documentation is sometimes suspect, as some of it
is taken from or inferred from the C++ documentation.
-
botan-low
has had the module-level documentation added, and a few modules have been documented
In addition, we have the first sneak peek at the botan
high-level bindings!
- Implemented
Botan.Prelude
, Botan.Error
, Botan.Utility
, Botan.Version
high-level idiomatic bindings
- First stab at implementing
Botan.Hash
with high-level API design
- Implemented ‘pure / referentially transparent’ versions of hash functions
- Implemented
HashSpec
data type
- Implemented prototype
Hash
and IncrementalHash
classes with data families
- Can use the
HashSpec
data type with the generic HashCtx
and HashDigest
, or can use the data families and get strongly-typed Ctx a
and Digest a
.
- Implemented MD5 as a hash / data family
The naming is a mess, and any of it may be changed wildly at any moment since I’m still shaking it down, but here’s what I’ve got:
The first nice thing is that there is now a HashSpec
datatype for supported hash algorithms, based on Z-Botan
's HashType
as a reference (and saving me a lot of effort). This is of course far more convenient than remembering algorithm names yourself.
data HashSpec
-- Cryptographic hashes
= Blake2b Blake2bSize
| Keccak1600 Keccak1600Size
| MD4
| MD5
| RIPEMD160
| SHA160
| SHA224
...
-- remainder omitted
hashCtxInitWithSpec :: HashSpec -> HashCtx
hashCtxInitWithSpec = hashCtxInit . hashSpecName
hashWithSpec :: HashSpec -> ByteString -> HashDigest
hashWithSpec spec = hashWithHashCtx (hashCtxInitWithSpec spec)
I’m thinking I might push the HashSpec down to botan-low
, keeping the monotyped functionality together and leaving the polymorphic types for the higher-level botan
, and I’ll be doing the same for other cryptographic primitives in the future after I decide upon a consistent handling, but this is a big leap forward in usability.
The second nice thing is the data families interface / API, which I adapted from a prior experimental project investigating different ways of class-ifying cryptographic primitives. It’s not quite the same as cryptonite
, but I’ve found the data families + newtypes approach to be the most effective, albeit at the cost of some up-front boilerplate for the newtypes. It’s nice, because it enables type-applications to control the algorithm selection.
-- The data families and classes
data family Ctx a
data family Digest a
class Hash a where
hashWithCtx :: Ctx a -> ByteString -> Digest a
class (Hash a) => IncrementalHash a where
hashInit :: Ctx a
hashUpdate :: Ctx a -> ByteString -> Ctx a
hashUpdates :: Ctx a -> [ByteString] -> Ctx a
hashFinalize :: Ctx a -> Digest a
-- MD5 implemented as an example
data MD5
newtype instance Ctx MD5 = MD5Ctx
{ getMD5Ctx :: HashCtx }
newtype instance Digest MD5 = MD5Digest
{ getMD5ByteString :: ByteString }
deriving newtype (Eq, Ord)
type MD5Ctx = Ctx MD5
type MD5Digest = Digest MD5
instance Show (Digest MD5) where
show :: Digest MD5 -> String
show (MD5Digest bytes) = Text.unpack $ hexEncode bytes Lower
instance Hash MD5 where
hashWithCtx :: Ctx MD5 -> ByteString -> Digest MD5
hashWithCtx (MD5Ctx ctx) bytes = MD5Digest $ hashWithHashCtx ctx bytes
instance IncrementalHash MD5 where
hashInit :: MD5Ctx
hashInit = MD5Ctx $ hashCtxInit "MD5"
hashUpdate :: MD5Ctx -> ByteString -> MD5Ctx
hashUpdate (MD5Ctx ctx) bytes = MD5Ctx $ hashCtxUpdate ctx bytes
hashUpdates :: MD5Ctx -> [ByteString] -> MD5Ctx
hashUpdates (MD5Ctx ctx) chunks = MD5Ctx $ hashCtxUpdates ctx chunks
hashFinalize :: MD5Ctx -> MD5Digest
hashFinalize (MD5Ctx ctx) = MD5Digest $ hashCtxFinalize ctx
hash :: (IncrementalHash a) => ByteString -> Digest a
hash = hashFinalize . hashUpdate hashInit
It’s still a little in flux, and the Hash
and IncrementalHash
classes aren’t necessarily set in stone (for instance, the hashInit
could belong to Hash
or even a separate class entirely) but I think you get the gist. Let me know if you think this is unnecessarily complex compared to cryptonite
's simple, non-data-family- Digest a
and such - I want to get some design feedback before investing a ton of effort into doing it for all of the primitives.
Also, note that the HashSpec
enum doesn’t interfere with our higher-level classy bindings, as the enum is simply a listing of botan-supported algorithms. We are free to define our own cryptographic schemes using the data families, and the high-level API might be split off onto its own in the future, in a sort of mirror symmetry to splitting off botan-bindings
in the other direction.
That’s it for now. My next concrete goal is to create -Spec
data types for other cryptographic algorithms & primitives, while I play around with the higher-level interfaces. That plus documentation should render the botan-low
library more-or-less complete (minus ZFEC), though it may undergo nomenclatural overhaul.
I’ve noticed that my update pace has slowed down a little. This isn’t unexpected - after all, I have been giving updates every day or other for the last month, and while I’ve made it way further than I expected in that time, it has also been a pretty hefty pace to sustain. I feel I need to recoup some energy, and so I might take a bit of a break once I achieve the next major milestone; if this thread gets a bit quiet, don’t worry - I’m not going away, just taking a breather