    {- How to use @cryptonite@ with symmetric block ciphers

    > {-# LANGUAGE OverloadedStrings #-}
    > {-# LANGUAGE ScopedTypeVariables #-}
    > {-# LANGUAGE GADTs #-}
    >
    > import           Crypto.Cipher.AES (AES256)
    > import           Crypto.Cipher.Types (BlockCipher(..), Cipher(..), nullIV, KeySizeSpecifier(..), IV, makeIV)
    > import           Crypto.Error (CryptoFailable(..), CryptoError(..))
    >
    > import qualified Crypto.Random.Types as CRT
    >
    > import           Data.ByteArray (ByteArray)
    > import           Data.ByteString (ByteString)
    >
    > -- | Not required, but most general implementation
    > data Key c a where
    >   Key :: (BlockCipher c, ByteArray a) => a -> Key c a
    >
    > -- | Generates a string of bytes (key) of a specific length for a given block cipher
    > genSecretKey :: forall m c a. (CRT.MonadRandom m, BlockCipher c, ByteArray a) => c -> Int -> m (Key c a)
    > genSecretKey _ = fmap Key . CRT.getRandomBytes
    >
    > -- | Generate a random initialization vector for a given block cipher
    > genRandomIV :: forall m c. (CRT.MonadRandom m, BlockCipher c) => c -> m (Maybe (IV c))
    > genRandomIV _ = do
    >   bytes :: ByteString <- CRT.getRandomBytes $ blockSize (undefined :: c)
    >   return $ makeIV bytes
    >
    > -- | Initialize a block cipher
    > initCipher :: (BlockCipher c, ByteArray a) => Key c a -> Either CryptoError c
    > initCipher (Key k) = case cipherInit k of
    >   CryptoFailed e -> Left e
    >   CryptoPassed a -> Right a
    >
    > encrypt :: (BlockCipher c, ByteArray a) => Key c a -> IV c -> a -> Either CryptoError a
    > encrypt secretKey initIV msg =
    >   case initCipher secretKey of
    >     Left e -> Left e
    >     Right c -> Right $ ctrCombine c initIV msg
    >
    > decrypt :: (BlockCipher c, ByteArray a) => Key c a -> IV c -> a -> Either CryptoError a
    > decrypt = encrypt
    >
    > exampleAES256 :: ByteString -> IO ()
    > exampleAES256 msg = do
    >   -- secret key needs 256 bits (32 * 8)
    >   secretKey <- genSecretKey (undefined :: AES256) 32
    >   mInitIV <- genRandomIV (undefined :: AES256)
    >   case mInitIV of
    >     Nothing -> error "Failed to generate and initialization vector."
    >     Just initIV -> do
    >       let encryptedMsg = encrypt secretKey initIV msg
    >           decryptedMsg = decrypt secretKey initIV =<< encryptedMsg
    >       case (,) <$> encryptedMsg <*> decryptedMsg of
    >         Left err -> error $ show err
    >         Right (eMsg, dMsg) -> do
    >           putStrLn $ "Original Message: " ++ show msg
    >           putStrLn $ "Message after encryption: " ++ show eMsg
    >           putStrLn $ "Message after decryption: " ++ show dMsg

    |-}

    module Crypto.Tutorial where