
Hello: I am implementing a high-level protocol which requires the use of various cryptographic primitives. I have the following goals in mind: 1. Allow others to extend the library to use different underlying crypto implementations, ciphers, curves, etc, and 2. Have the library be well-typed so that cryptographic operations are unambiguous, leading to code which can be easily audited. According to the spec, the user is allowed to instantiate the protocol with any combination of supported ciphers and curves, but the high-level operations always remain the same regardless of what primitives are used. Pursuant to the above goals, I've defined two typeclasses, "Cipher" and "Curve". I'm using TypeFamilies to facilitate the creation of data types that are independent of the underlying crypto library. The Cipher typeclass is as follows: class Cipher c where data Ciphertext c :: * data SymmetricKey c :: * data Nonce c :: * data Digest c :: * cipherName :: c -> ByteString cipherEncrypt :: SymmetricKey c -> Nonce c -> AssocData -> Plaintext -> Ciphertext c cipherDecrypt :: SymmetricKey c -> Nonce c -> AssocData -> Ciphertext c -> Maybe Plaintext cipherZeroNonce :: Nonce c cipherIncNonce :: Nonce c -> Nonce c cipherGetKey :: SymmetricKey c -> Nonce c -> SymmetricKey c cipherHashToKey :: Digest c -> SymmetricKey c cipherHashToAD :: Digest c -> AssocData cipherHash :: ScrubbedBytes -> Digest c cipherConcatHash :: Digest c -> ScrubbedBytes -> Digest c cipherHMAC :: SymmetricKey c -> ScrubbedBytes -> Digest c newtype Plaintext = Plaintext ScrubbedBytes newtype AssocData = AssocData ScrubbedBytes Unfortunately, the spec as written assumes that all variables are blobs of bytes. The issue I'm running in to is that, in my goal to have everything well-typed, my typeclasses are filling up with conversion functions like "cipherHashToKey" and "cipherHashToAD". These type conversions are necessary because I'm required to compute values like "HMAC-HASH(GETKEY(k, n), data)" (which returns "Digest c") and assign it to a variable whose type is "SymmetricKey c" (hence the need for cipherHashToKey). Is all my effort to write a well-typed library being done in vain? Should I just give up and have all the functions use ByteStrings? -- Alex