| Copyright | (c) 2023-2024 Apotheca Labs (c) 2024-2025 Haskell Foundation |
|---|---|
| License | BSD-3-Clause |
| Maintainer | joris@well-typed.com, leo@apotheca.io |
| Stability | experimental |
| Portability | POSIX |
| Safe Haskell | Safe-Inferred |
| Language | GHC2021 |
Botan.Low.BlockCipher
Description
This module is based on the Block Ciphers section of the C Botan FFI documentation, and on the Block Ciphers section of the C++ API reference.
Synopsis
- newtype BlockCipher = MkBlockCipher {}
- type BlockCipherName = ByteString
- type BlockCipherKey = ByteString
- type BlockCipherCiphertext = ByteString
- withBlockCipher :: BlockCipher -> (BotanBlockCipher -> IO a) -> IO a
- blockCipherInit :: BlockCipherName -> IO BlockCipher
- blockCipherDestroy :: BlockCipher -> IO ()
- blockCipherName :: BlockCipher -> IO BlockCipherName
- blockCipherBlockSize :: BlockCipher -> IO Int
- blockCipherGetKeyspec :: BlockCipher -> IO (Int, Int, Int)
- blockCipherSetKey :: BlockCipher -> BlockCipherKey -> IO ()
- blockCipherEncryptBlocks :: BlockCipher -> ByteString -> IO BlockCipherCiphertext
- blockCipherDecryptBlocks :: BlockCipher -> BlockCipherCiphertext -> IO ByteString
- blockCipherClear :: BlockCipher -> IO ()
- pattern AES128 :: BlockCipherName
- pattern AES192 :: BlockCipherName
- pattern AES256 :: BlockCipherName
- pattern ARIA128 :: BlockCipherName
- pattern ARIA192 :: BlockCipherName
- pattern ARIA256 :: BlockCipherName
- pattern Blowfish :: BlockCipherName
- pattern Camellia128 :: BlockCipherName
- pattern Camellia192 :: BlockCipherName
- pattern Camellia256 :: BlockCipherName
- pattern Cascade :: BlockCipherName
- cascade :: BlockCipherName -> BlockCipherName -> BlockCipherName
- pattern CAST128 :: BlockCipherName
- pattern DES :: BlockCipherName
- pattern TripleDES :: BlockCipherName
- pattern GOST_28147_89 :: BlockCipherName
- pattern IDEA :: BlockCipherName
- pattern Lion :: BlockCipherName
- lion :: HashName -> ByteString -> Maybe Word -> BlockCipherName
- pattern Noekeon :: BlockCipherName
- pattern SEED :: BlockCipherName
- pattern Serpent :: BlockCipherName
- pattern SHACAL2 :: BlockCipherName
- pattern SM4 :: BlockCipherName
- pattern Threefish512 :: BlockCipherName
- pattern Twofish :: BlockCipherName
- blockCiphers :: [BlockCipherName]
- blockCipher128s :: [BlockCipherName]
- allBlockCiphers :: [BlockCipherName]
Introduction
A block cipher is a deterministic, cryptographic primitive suitable for encrypting or decrypting a single, fixed-size block of data at a time. Block ciphers are used as building blocks for more complex cryptographic operations. If you are looking to encrypt user data, you are probably looking for Botan.Low.Cipher instead.
Unless you need a specific block cipher, it is strongly recommended that you use
the AES256 algorithm.
>>>:{{-# OPTIONS_GHC -Wall #-} {-# LANGUAGE OverloadedStrings #-} import Botan.Low.BlockCipher import Botan.Low.RNG import Control.Exception import Data.ByteString as BS :}
For this example, we will use the AES256 algorithm. To use a block cipher, we
first need a secret key of the appropriate length. Such keys should be generated
securely, but to keep it simple in this example, we will just use a random
number
generator to generate a secret key.
>>>:{generateKey :: BlockCipher -> IO ByteString generateKey blockCipher = do rng <- rngInit "user" (_,keySize,_) <- blockCipherGetKeyspec blockCipher key <- rngGet rng keySize pure key :}
We can now initialise a block cipher object and set its secret key.
>>>:{initBlockCipher :: IO BlockCipher initBlockCipher = do blockCipher <- blockCipherInit AES256 key <- generateKey blockCipher blockCipherSetKey blockCipher key pure blockCipher :}
Using the initialised block cipher, we can now encrypt a message. To encrypt a message, it must be a multiple of the block size. To decrypt a message, simply reverse the process. As a sanity check, we show that decryption after encryption yields the original message
>>>:{roundtrip :: BlockCipher -> ByteString -> IO () roundtrip blockCipher message = do ciphertext <- blockCipherEncryptBlocks blockCipher message plaintext <- blockCipherDecryptBlocks blockCipher ciphertext print $ message == plaintext :}
Now we tie together the program. We initialise the block cipher (and set its secret key). Then, we generate a message, and check that it roundtrips through the encryption and decryption functions.
>>>:{main :: IO () main = do blockCipher <- initBlockCipher blockSize <- blockCipherBlockSize blockCipher -- AES256 block size is always 16 bytes let message = "0000DEADBEEF0000" assert (BS.length message == blockSize) $ pure () roundtrip blockCipher message :}
All that is left to do is run the program.
>>>mainTrue
type BlockCipherName = ByteString Source #
Block cipher name type
type BlockCipherKey = ByteString Source #
A block cipher secret key
type BlockCipherCiphertext = ByteString Source #
A block cipher ciphertext
withBlockCipher :: BlockCipher -> (BotanBlockCipher -> IO a) -> IO a Source #
Arguments
| :: BlockCipherName | cipher_name |
| -> IO BlockCipher | bc |
Initialize a block cipher object
blockCipherDestroy :: BlockCipher -> IO () Source #
Destroy a block cipher object immediately
Arguments
| :: BlockCipher | bc |
| -> IO BlockCipherName | name |
Get the name of a block cipher.
This function is not guaranteed to return the same exact value as used to initialize the context.
Arguments
| :: BlockCipher | bc: The cipher object |
| -> IO Int |
Return the block size of a block cipher.
blockCipherGetKeyspec Source #
Arguments
| :: BlockCipher | bc |
| -> IO (Int, Int, Int) | (min,max,mod) |
Get the key specification of a block cipher
Returns the minimum, maximum, and modulo of valid keys.
Arguments
| :: BlockCipher | bc: The cipher object |
| -> BlockCipherKey | key[]: A cipher key |
| -> IO () |
Set the key for a block cipher
Throws an error if the key is not valid.
blockCipherEncryptBlocks Source #
Arguments
| :: BlockCipher | bc: The cipher object |
| -> ByteString | in[]: The plaintext |
| -> IO BlockCipherCiphertext | out[]: The ciphertext |
Encrypt one or more blocks with a block cipher
Precondition: the length of the plaintext should be a multiple of the block size, or this function throws an error.
blockCipherDecryptBlocks Source #
Arguments
| :: BlockCipher | bc: The cipher object |
| -> BlockCipherCiphertext | in[]: The ciphertext |
| -> IO ByteString | out[]: The plaintext |
Decrypt one or more blocks with a block cipher.
Precondition: the length of the ciphertext should be a multiple of the block size, or this function throws an error.
If an incorrect key was set, the content of the decrypted plaintext is unspecified.
Arguments
| :: BlockCipher | bc: The cipher object |
| -> IO () |
Reinitializes a block cipher
You must call blockCipherSetKey in order to use the block cipher again.
Available ciphers
A number of algorithms are available to be used as block ciphers. An algorithm
is selected by passing the algorithm specification name of the algorithm as a
string to the block cipher functions. In some cases an algorithm specification
name is just the algorithm name, like "AES-128". In other cases an algorithm
specification name is a combination of an algorithm name paired with some
parameters, such as "Cascade(Serpent, AES-256)" where Cascade is the
algorithm name followed by a 2-tuple of parameters. Pattern synonyms are
included for each available algorithm name. Where necessary, such as in the
Cascade case, these algorithm names should be manually extended to full
algorithm specification names, or by using utility functions like cascade that
this module also provides.
WARNING: 64-bit block ciphers should be avoided.
WARNING: the Kuznyechik block ciphier is not included because its security is
disputed.
See the Available Ciphers section of the C++ API reference for more information about available ciphers.
Each algorithm expects a certain size of block to encrypt or decrypt. For the available algorithms, these block sizes in number of bits are listed below. For algorithms that require parameters, the block size may vary depending on the parameters. For the examples below, we pick some arbitrary parameters.
>>>:{forM_ allBlockCiphers $ \name -> do let name' = if name == Cascade then cascade Serpent AES256 else if name == Lion then lion SHA1 "RC4" (Just 64) else name blockCipher <- blockCipherInit name' sz <- blockCipherBlockSize blockCipher print (name, sz * 8) :} ("AES-128",128) ("AES-192",128) ("AES-256",128) ("ARIA-128",128) ("ARIA-192",128) ("ARIA-256",128) ("Camellia-128",128) ("Camellia-192",128) ("Camellia-256",128) ("Noekeon",128) ("SEED",128) ("Serpent",128) ("SM4",128) ("Twofish",128) ("Blowfish",64) ("Cascade",128) ("CAST-128",64) ("DES",64) ("TripleDES",64) ("GOST-28147-89",64) ("IDEA",64) ("Lion",512) ("SHACAL2",256) ("Threefish-512",512)
pattern AES128 :: BlockCipherName Source #
pattern AES192 :: BlockCipherName Source #
pattern AES256 :: BlockCipherName Source #
pattern ARIA128 :: BlockCipherName Source #
pattern ARIA192 :: BlockCipherName Source #
pattern ARIA256 :: BlockCipherName Source #
pattern Blowfish :: BlockCipherName Source #
pattern Camellia128 :: BlockCipherName Source #
pattern Camellia192 :: BlockCipherName Source #
pattern Camellia256 :: BlockCipherName Source #
pattern Cascade :: BlockCipherName Source #
cascade :: BlockCipherName -> BlockCipherName -> BlockCipherName Source #
>>>cascade Serpent AES256"Cascade(Serpent,AES-256)"
pattern CAST128 :: BlockCipherName Source #
pattern DES :: BlockCipherName Source #
pattern TripleDES :: BlockCipherName Source #
pattern GOST_28147_89 :: BlockCipherName Source #
pattern IDEA :: BlockCipherName Source #
pattern Lion :: BlockCipherName Source #
lion :: HashName -> ByteString -> Maybe Word -> BlockCipherName Source #
>>>lion SHA1 "RC4" Nothing"Lion(SHA-1,RC4)"
>>>lion SHA1 "RC4" (Just 64)"Lion(SHA-1,RC4,64)"
pattern Noekeon :: BlockCipherName Source #
pattern SEED :: BlockCipherName Source #
pattern Serpent :: BlockCipherName Source #
pattern SHACAL2 :: BlockCipherName Source #
pattern SM4 :: BlockCipherName Source #
pattern Threefish512 :: BlockCipherName Source #
pattern Twofish :: BlockCipherName Source #