September 7, 2010
Crypto-API is a generic interface for cryptographic operations, platform independent quality Entropy, property tests and known-answer tests (KATs) for common algorithms, and a basic benchmark infrastructure. Maintainers of hash and cipher implementations are encouraged to add instances for the classes defined in Crypto.Classes. Crypto users are similarly encouraged to use the interfaces defined in the Classes module.
An example class instance:
instance Hash MD5Context MD5Digest where outputLength = Tagged 128 blockLength = Tagged 512 initialCtx = md5InitialContext updateCtx = md5Update finalize = md5Finalize
The hash user can remain agnostic about which type of hash is used:
authMessage :: Hash ctx dgst => B.ByteString -> MacKey -> dgst -> Bool authMessage msg k = (==) (hmac' k msg) hashFile :: Hash c d => FilePath -> IO d hashFile = liftM hash L.readFile
Example: Block Cipher
Users of block cipher instances probably want to use Crypto.Modes:
import Crypto.Classes import Crypto.Modes (cbc) import Data.Serialize (encode) cipherMsgAppendIV :: (BlockCipher k) => k -> B.ByteString -> IO B.ByteString, cipherMsgAppendIV msg = do iv <- getIVIO return $ B.append (encode iv) (cbc k iv msg)
Its easy to get a DRBG (aka PRNG) that can be used for generating seed material for keys, building asymmetric keys, obtaining initialization vectors, nonces, or many other uses. See Crypto.Random (which users System.Crypto.Random for entropy):
newGenIO :: CryptoRandomGen g => IO g genBytes :: (CryptoRandomGen g) => g -> ByteLength -> Either GenError (ByteString, g) getIV :: (CryptoRandomGen g, BlockCipher k) => g -> Either GenError (IV k, g) buildKeyPair :: CryptoRandomGen g => g -> BitLength -> Maybe ((p, p), g)
A quick peek in the Test.Crypto module will show you that testing is decent (particularly for AES) and getting better all the time.
Given a BlockCipher instance the entire test code for an AES implementation is:
-- Omitting hack-job instances for SimpleAES in this snippet main = do ts <- makeAESTests (AESKey $ B.replicate 16 0) runTests ts
This automatically reads in hundreds of NIST Known Answer Tests (KATs) and checks the implementation. A lesser infrastructure exists for testing Hashes. Cipher property tests are still needed.
As with tests, benchmarking is quite simple:
import Data.Digest.Pure.MD5 import Benchmark.Crypto import Criterion.Main main = defaultMain [benchmarkHash (undefined :: MD5Digest) "pureMD5"]
So please, if you maintain a hash, cipher, or other cryptographic primitive please add instances for the crypto-api classes. If you need these primitives then consider using the crypto-api interfaces, allowing you to remain algorithm and implementation agnostic in all your low level code.