{-|
Module      : Botan.Low.Cipher
Description : Symmetric cipher modes
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

A block cipher by itself, is only able to securely encrypt a single
data block. To be able to securely encrypt data of arbitrary length,
a mode of operation applies the block cipher’s single block operation
repeatedly to encrypt an entire message.
-}

{-# LANGUAGE OverloadedStrings #-}

module Botan.Low.Cipher (

  -- * Cipher
  -- $introduction

  -- * Usage
  -- $usage

    Cipher(..)
  , CipherName
  , CipherKey
  , CipherNonce
  , CipherInitFlags (..)
  , cipherInitFlags
  , CipherUpdateFlags (..)
  , cipherUpdateFlags
  , withCipher
  , cipherInit
  , cipherDestroy
  , cipherName
  , cipherOutputLength
  , cipherValidNonceLength
  , cipherGetTagLength
  , cipherGetDefaultNonceLength
  , cipherGetUpdateGranularity
  , cipherGetIdealUpdateGranularity
  , cipherQueryKeylen
  , cipherGetKeyspec
  , cipherSetKey
  , cipherReset
  , cipherSetAssociatedData
  , cipherStart
  , cipherUpdate
  , cipherEncrypt
  , cipherDecrypt
  , cipherClear

  -- * Cipher modes
  , CipherMode
  , cbcMode
  , cfbMode
  , cfbModeWith
  , xtsMode

  -- ** CBC padding
  , CBCPaddingName
  , pattern PKCS7
  , pattern OneAndZeros
  , pattern X9_23
  , pattern ESP
  , pattern CTS
  , pattern NoPadding

  -- * AEAD
  , AEADName
  , pattern ChaCha20Poly1305
  , chaCha20Poly1305

  -- * AEAD modes
  , AEADMode
  , gcmMode
  , gcmModeWith
  , ocbMode
  , ocbModeWith
  , eaxMode
  , eaxModeWith
  , sivMode
  , ccmMode
  , ccmModeWith

  -- * Convenience
  , cipherEncryptOnline
  , cipherDecryptOnline
  , cipherModes
  , cbcPaddings
  , aeads
  , allCiphers

  ) where

import           Botan.Bindings.Cipher
import           Botan.Bindings.ConstPtr (ConstPtr (..))
import           Botan.Low.BlockCipher
import           Botan.Low.Error.Internal
import           Botan.Low.Internal.ByteString
import           Botan.Low.Internal.String
import           Botan.Low.Make
import           Botan.Low.Remake
import           Control.Exception
import           Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
import           Data.Word
import           Foreign.C.Types
import           Foreign.ForeignPtr
import           Foreign.Marshal.Alloc
import           Foreign.Ptr
import           Foreign.Storable

{- $introduction

A `cipher` mode is a cryptographic algorithm suitable for encrypting and
decrypting large quantities of arbitrarily-sized data. An `aead` is a cipher
mode that also used to provide authentication of the ciphertext, potentially
with plaintext `associated data`.

-}

{- $usage

Unless you need a specific `cipher` or `aead`, it is strongly recommended that you use the `cbcMode AES256 PKCS7` and `gcmMode AES256` (or `ChaCha20Poly1305`) algorithms respectively.

> import Botan.Low.Cipher
> encrypter <- cipherInit ChaCha20Poly1305 Encrypt

To use a cipher, we first need to generate (if we haven't already) a secret key.

> import Botan.Low.RNG
> rng <- rngInit "user"
> -- We will use the maximum key size; ChaCha20Poly1305 keys are always 32 bytes
> (_,keySize,_) <- cipherGetKeyspec encrypter
> -- Block cipher keys are randomly generated
> key <- rngGet rng keySize

After the key is generated, we must set it as the cipher key:

> cipherSetKey encrypter key

If the cipher is an `aead`, we may also set the `associated data`:

> cipherSetAssociatedData encrypter "Fee fi fo fum!"

To ensure that the key is not leaked, we should generate a new nonce for every encryption. The range of allowed nonce sizes depends on the specific algorithm.

> import Botan.Low.RNG
> -- The default ChaCha20Poly1305 nonce is always 12 bytes.
> nonceSize <- cipherGetDefaultNonceLength encrypter
> nonce <- rngGet rng nonceSize

To encrypt a message, it must be a multiple of the block size. If the cipher was an aead, the authentication tag will automatically be included in the ciphertext

> -- Rarely, some cipher modes require that the message size be aligned to the block size
> -- Consult algorithm-specific documentation if this occurs.
> message = "I smell the blood of an Englishman!"
> cipherStart encrypter nonce
> ciphertext <- cipherEncrypt encrypter message

To decrypt a message, we run the same process with a decrypter, using the same `key` and `nonce` to decode the `ciphertext`:

> decrypter <- cipherInit ChaCha20Poly1305 Decrypt
> cipherSetKey decrypter key
> cipherSetAssociatedData decrypter "Fee fi fo fum!"
> cipherStart decrypter nonce
> plaintext <- cipherDecrypt decrypter ciphertext
> message == plaintext -- True

You can completely clear a cipher's state, leaving it ready for reuse:

> cipherClear encrypter
> -- You'll have to set the key, nonce, (and ad, if aead) again.
> cipherSetKey encrypter anotherKey
> cipherStart encrypter anotherNonce
> cipherSetAssociatedData encrypter anotherAD
> -- Process another message
> anotherCiphertext <- cipherEncrypt encrypter anotherMessage

If you are encrypting or decrypting multiple messages with the same key, you can reset the cipher instead of clearing it, leaving the key set:

> cipherClear encrypter
> -- This is equivalent to calling cipherClear followed by cipherSetKey with the original key.
> -- You'll have to set the nonce  (and ad, if aead) again, but not the key.
> cipherStart encrypter anotherNonce
> cipherSetAssociatedData encrypter anotherAD
> -- Process another message with the same key
> anotherCiphertext <- cipherEncrypt encrypter anotherMessage

-}

-- NOTE: This is *symmetric* ciphers  For the 'raw' interface to ECB mode block ciphers, see BlockCipher.hs

newtype Cipher = MkCipher { Cipher -> ForeignPtr BotanCipherStruct
foreignPtr :: ForeignPtr BotanCipherStruct }

withCipher     :: Cipher -> (BotanCipher -> IO a) -> IO a
-- | Destroy the cipher object immediately
cipherDestroy  :: Cipher -> IO ()
createCipher   :: (Ptr BotanCipher -> IO CInt) -> IO Cipher
(Cipher -> (BotanCipher -> IO a) -> IO a
withCipher, Cipher -> IO ()
cipherDestroy, (Ptr BotanCipher -> IO CInt) -> IO Cipher
createCipher)
    = (Ptr BotanCipherStruct -> BotanCipher)
-> (BotanCipher -> Ptr BotanCipherStruct)
-> (ForeignPtr BotanCipherStruct -> Cipher)
-> (Cipher -> ForeignPtr BotanCipherStruct)
-> FinalizerPtr BotanCipherStruct
-> (Cipher -> (BotanCipher -> IO a) -> IO a, Cipher -> IO (),
    (Ptr BotanCipher -> IO CInt) -> IO Cipher)
forall botan struct object a.
Storable botan =>
(Ptr struct -> botan)
-> (botan -> Ptr struct)
-> (ForeignPtr struct -> object)
-> (object -> ForeignPtr struct)
-> FinalizerPtr struct
-> (object -> (botan -> IO a) -> IO a, object -> IO (),
    (Ptr botan -> IO CInt) -> IO object)
mkBindings
        Ptr BotanCipherStruct -> BotanCipher
MkBotanCipher (.ptr)
        ForeignPtr BotanCipherStruct -> Cipher
MkCipher (.foreignPtr)
        FinalizerPtr BotanCipherStruct
botan_cipher_destroy

type CipherNonce = ByteString
type CipherKey = ByteString

type CipherName = ByteString

type CipherMode = ByteString

type CBCPaddingName = ByteString

pattern PKCS7
    ,   OneAndZeros
    ,   X9_23
    ,   ESP
    ,   CTS
    ,   NoPadding
    ::  CBCPaddingName

pattern $bPKCS7 :: AEADName
$mPKCS7 :: forall {r}. AEADName -> ((# #) -> r) -> ((# #) -> r) -> r
PKCS7       = BOTAN_CBC_PADDING_PKCS7
pattern $bOneAndZeros :: AEADName
$mOneAndZeros :: forall {r}. AEADName -> ((# #) -> r) -> ((# #) -> r) -> r
OneAndZeros = BOTAN_CBC_PADDING_ONE_AND_ZEROS
pattern $bX9_23 :: AEADName
$mX9_23 :: forall {r}. AEADName -> ((# #) -> r) -> ((# #) -> r) -> r
X9_23       = BOTAN_CBC_PADDING_X9_23
pattern $bESP :: AEADName
$mESP :: forall {r}. AEADName -> ((# #) -> r) -> ((# #) -> r) -> r
ESP         = BOTAN_CBC_PADDING_ESP
pattern $bCTS :: AEADName
$mCTS :: forall {r}. AEADName -> ((# #) -> r) -> ((# #) -> r) -> r
CTS         = BOTAN_CBC_PADDING_CTS
pattern $bNoPadding :: AEADName
$mNoPadding :: forall {r}. AEADName -> ((# #) -> r) -> ((# #) -> r) -> r
NoPadding   = BOTAN_CBC_PADDING_NO_PADDING

cbcMode :: BlockCipherName -> CBCPaddingName -> CipherName
cbcMode :: AEADName -> AEADName -> AEADName
cbcMode AEADName
bc AEADName
padding = AEADName
bc AEADName -> AEADName -> AEADName
forall a. (IsString a, Semigroup a) => a -> a -> a
// AEADName
forall a. (Eq a, IsString a) => a
BOTAN_CIPHER_MODE_CBC AEADName -> AEADName -> AEADName
forall a. (IsString a, Semigroup a) => a -> a -> a
// AEADName
padding

cfbMode :: BlockCipherName -> CipherName
cfbMode :: AEADName -> AEADName
cfbMode AEADName
bc = AEADName
bc AEADName -> AEADName -> AEADName
forall a. (IsString a, Semigroup a) => a -> a -> a
// AEADName
forall a. (Eq a, IsString a) => a
BOTAN_CIPHER_MODE_CFB

cfbModeWith :: BlockCipherName -> Int -> CipherName
cfbModeWith :: AEADName -> Int -> AEADName
cfbModeWith AEADName
bc Int
feedbackSz = AEADName -> AEADName
cfbMode AEADName
bc AEADName -> AEADName -> AEADName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ Int -> AEADName
forall a. Show a => a -> AEADName
showBytes Int
feedbackSz

xtsMode :: BlockCipherName -> CipherName
xtsMode :: AEADName -> AEADName
xtsMode AEADName
bc = AEADName
bc AEADName -> AEADName -> AEADName
forall a. (IsString a, Semigroup a) => a -> a -> a
// AEADName
forall a. (Eq a, IsString a) => a
BOTAN_CIPHER_MODE_XTS

type AEADName = CipherName

pattern ChaCha20Poly1305 :: CipherName
pattern $bChaCha20Poly1305 :: AEADName
$mChaCha20Poly1305 :: forall {r}. AEADName -> ((# #) -> r) -> ((# #) -> r) -> r
ChaCha20Poly1305 = BOTAN_AEAD_CHACHA20POLY1305

chaCha20Poly1305 :: AEADName
chaCha20Poly1305 :: AEADName
chaCha20Poly1305 = AEADName
forall a. (Eq a, IsString a) => a
BOTAN_AEAD_CHACHA20POLY1305

type AEADMode = ByteString

gcmMode :: BlockCipherName -> AEADName
gcmMode :: AEADName -> AEADName
gcmMode AEADName
bc = AEADName
bc AEADName -> AEADName -> AEADName
forall a. (IsString a, Semigroup a) => a -> a -> a
// AEADName
forall a. (Eq a, IsString a) => a
BOTAN_AEAD_MODE_GCM

gcmModeWith :: BlockCipherName -> Int -> AEADName
gcmModeWith :: AEADName -> Int -> AEADName
gcmModeWith AEADName
bc Int
tagSz = AEADName -> AEADName
gcmMode AEADName
bc AEADName -> AEADName -> AEADName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ Int -> AEADName
forall a. Show a => a -> AEADName
showBytes Int
tagSz

ocbMode :: BlockCipherName -> AEADName
ocbMode :: AEADName -> AEADName
ocbMode AEADName
bc = AEADName
bc AEADName -> AEADName -> AEADName
forall a. (IsString a, Semigroup a) => a -> a -> a
// AEADName
forall a. (Eq a, IsString a) => a
BOTAN_AEAD_MODE_OCB

ocbModeWith :: BlockCipherName -> Int -> AEADName
ocbModeWith :: AEADName -> Int -> AEADName
ocbModeWith AEADName
bc Int
tagSz = AEADName -> AEADName
ocbMode AEADName
bc AEADName -> AEADName -> AEADName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ Int -> AEADName
forall a. Show a => a -> AEADName
showBytes Int
tagSz

eaxMode :: BlockCipherName -> AEADName
eaxMode :: AEADName -> AEADName
eaxMode AEADName
bc = AEADName
bc AEADName -> AEADName -> AEADName
forall a. (IsString a, Semigroup a) => a -> a -> a
// AEADName
forall a. (Eq a, IsString a) => a
BOTAN_AEAD_MODE_EAX

eaxModeWith :: BlockCipherName -> Int -> AEADName
eaxModeWith :: AEADName -> Int -> AEADName
eaxModeWith AEADName
bc Int
tagSz = AEADName -> AEADName
eaxMode AEADName
bc AEADName -> AEADName -> AEADName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ Int -> AEADName
forall a. Show a => a -> AEADName
showBytes Int
tagSz

sivMode :: BlockCipherName -> AEADName
sivMode :: AEADName -> AEADName
sivMode AEADName
bc = AEADName
bc AEADName -> AEADName -> AEADName
forall a. (IsString a, Semigroup a) => a -> a -> a
// AEADName
forall a. (Eq a, IsString a) => a
BOTAN_AEAD_MODE_SIV

ccmMode :: BlockCipherName -> AEADName
ccmMode :: AEADName -> AEADName
ccmMode AEADName
bc = AEADName
bc AEADName -> AEADName -> AEADName
forall a. (IsString a, Semigroup a) => a -> a -> a
// AEADName
forall a. (Eq a, IsString a) => a
BOTAN_AEAD_MODE_CCM

ccmModeWith :: BlockCipherName -> Int -> Int -> AEADName
ccmModeWith :: AEADName -> Int -> Int -> AEADName
ccmModeWith AEADName
bc Int
tagSz Int
l = AEADName -> AEADName
ccmMode AEADName
bc AEADName -> AEADName -> AEADName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ Int -> AEADName
forall a. Show a => a -> AEADName
showBytes Int
tagSz AEADName -> AEADName -> AEADName
forall a. Semigroup a => a -> a -> a
<> AEADName
"," AEADName -> AEADName -> AEADName
forall a. Semigroup a => a -> a -> a
<> Int -> AEADName
forall a. Show a => a -> AEADName
showBytes Int
l

cbcPaddings :: [CBCPaddingName]
cbcPaddings :: [AEADName]
cbcPaddings =
    [ AEADName
PKCS7
    , AEADName
OneAndZeros
    , AEADName
X9_23
    , AEADName
ESP
    , AEADName
CTS
    , AEADName
NoPadding
    ]

cipherModes :: [CipherName]
cipherModes :: [AEADName]
cipherModes = [[AEADName]] -> [AEADName]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
    [ [ AEADName -> AEADName -> AEADName
cbcMode AEADName
bc AEADName
pd | AEADName
bc <- [AEADName]
allBlockCiphers, AEADName
pd <- [AEADName]
cbcPaddings ]
    , [ AEADName -> AEADName
cfbMode AEADName
bc    | AEADName
bc <- [AEADName]
allBlockCiphers ]
    , [ AEADName -> AEADName
xtsMode AEADName
bc    | AEADName
bc <- [AEADName]
allBlockCiphers ]
    ]

aeads :: [AEADName]
aeads :: [AEADName]
aeads = [[AEADName]] -> [AEADName]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
    [ [ AEADName
chaCha20Poly1305 ]
    , [ AEADName -> AEADName
gcmMode AEADName
bc | AEADName
bc <- [AEADName]
blockCipher128s ]
    , [ AEADName -> AEADName
ocbMode AEADName
bc | AEADName
bc <- [AEADName]
blockCipher128s ]
    , [ AEADName -> AEADName
eaxMode AEADName
bc | AEADName
bc <- [AEADName]
blockCiphers ] -- WARNING: Why just blockCiphers, why not allBlockCiphers?
    , [ AEADName -> AEADName
sivMode AEADName
bc | AEADName
bc <- [AEADName]
blockCipher128s ]
    , [ AEADName -> AEADName
ccmMode AEADName
bc | AEADName
bc <- [AEADName]
blockCipher128s ]
    ]

allCiphers :: [CipherName]
allCiphers :: [AEADName]
allCiphers = [AEADName]
cipherModes [AEADName] -> [AEADName] -> [AEADName]
forall a. [a] -> [a] -> [a]
++ [AEADName]
aeads

data CipherInitFlags =
    CipherMaskDirection
  | CipherEncrypt
  | CipherDecrypt
  deriving stock (Int -> CipherInitFlags -> ShowS
[CipherInitFlags] -> ShowS
CipherInitFlags -> String
(Int -> CipherInitFlags -> ShowS)
-> (CipherInitFlags -> String)
-> ([CipherInitFlags] -> ShowS)
-> Show CipherInitFlags
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CipherInitFlags -> ShowS
showsPrec :: Int -> CipherInitFlags -> ShowS
$cshow :: CipherInitFlags -> String
show :: CipherInitFlags -> String
$cshowList :: [CipherInitFlags] -> ShowS
showList :: [CipherInitFlags] -> ShowS
Show, CipherInitFlags -> CipherInitFlags -> Bool
(CipherInitFlags -> CipherInitFlags -> Bool)
-> (CipherInitFlags -> CipherInitFlags -> Bool)
-> Eq CipherInitFlags
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CipherInitFlags -> CipherInitFlags -> Bool
== :: CipherInitFlags -> CipherInitFlags -> Bool
$c/= :: CipherInitFlags -> CipherInitFlags -> Bool
/= :: CipherInitFlags -> CipherInitFlags -> Bool
Eq)

cipherInitFlags :: CipherInitFlags -> Word32
cipherInitFlags :: CipherInitFlags -> Word32
cipherInitFlags CipherInitFlags
CipherMaskDirection = Word32
forall a. (Eq a, Num a) => a
BOTAN_CIPHER_INIT_FLAG_MASK_DIRECTION
cipherInitFlags CipherInitFlags
CipherEncrypt       = Word32
forall a. (Eq a, Num a) => a
BOTAN_CIPHER_INIT_FLAG_ENCRYPT
cipherInitFlags CipherInitFlags
CipherDecrypt       = Word32
forall a. (Eq a, Num a) => a
BOTAN_CIPHER_INIT_FLAG_DECRYPT

data CipherUpdateFlags =
    CipherUpdate
  | CipherFinal
  deriving stock (Int -> CipherUpdateFlags -> ShowS
[CipherUpdateFlags] -> ShowS
CipherUpdateFlags -> String
(Int -> CipherUpdateFlags -> ShowS)
-> (CipherUpdateFlags -> String)
-> ([CipherUpdateFlags] -> ShowS)
-> Show CipherUpdateFlags
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CipherUpdateFlags -> ShowS
showsPrec :: Int -> CipherUpdateFlags -> ShowS
$cshow :: CipherUpdateFlags -> String
show :: CipherUpdateFlags -> String
$cshowList :: [CipherUpdateFlags] -> ShowS
showList :: [CipherUpdateFlags] -> ShowS
Show, CipherUpdateFlags -> CipherUpdateFlags -> Bool
(CipherUpdateFlags -> CipherUpdateFlags -> Bool)
-> (CipherUpdateFlags -> CipherUpdateFlags -> Bool)
-> Eq CipherUpdateFlags
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CipherUpdateFlags -> CipherUpdateFlags -> Bool
== :: CipherUpdateFlags -> CipherUpdateFlags -> Bool
$c/= :: CipherUpdateFlags -> CipherUpdateFlags -> Bool
/= :: CipherUpdateFlags -> CipherUpdateFlags -> Bool
Eq)

cipherUpdateFlags :: CipherUpdateFlags -> Word32
cipherUpdateFlags :: CipherUpdateFlags -> Word32
cipherUpdateFlags CipherUpdateFlags
CipherUpdate = Word32
forall a. (Eq a, Num a) => a
BOTAN_CIPHER_UPDATE_FLAG_NONE
cipherUpdateFlags CipherUpdateFlags
CipherFinal  = Word32
forall a. (Eq a, Num a) => a
BOTAN_CIPHER_UPDATE_FLAG_FINAL

-- |Initialize a cipher object
cipherInit
    :: CipherName       -- ^ __name__
    -> CipherInitFlags  -- ^ __flags__
    -> IO Cipher        -- ^ __cipher__
cipherInit :: AEADName -> CipherInitFlags -> IO Cipher
cipherInit AEADName
name CipherInitFlags
flags = ((Ptr BotanCipher -> IO CInt) -> IO Cipher)
-> (Ptr BotanCipher -> ConstPtr CChar -> Word32 -> IO CInt)
-> AEADName
-> Word32
-> IO Cipher
forall botan object a.
((Ptr botan -> IO CInt) -> IO object)
-> (Ptr botan -> ConstPtr CChar -> a -> IO CInt)
-> AEADName
-> a
-> IO object
mkCreateObjectCString1 (Ptr BotanCipher -> IO CInt) -> IO Cipher
createCipher Ptr BotanCipher -> ConstPtr CChar -> Word32 -> IO CInt
botan_cipher_init AEADName
name (CipherInitFlags -> Word32
cipherInitFlags CipherInitFlags
flags)

-- |Return the name of the cipher object
cipherName
    :: Cipher           -- ^ __cipher__
    -> IO CipherName    -- ^ __name__
cipherName :: Cipher -> IO AEADName
cipherName = WithPtr Cipher BotanCipher
-> GetCString BotanCipher CChar -> Cipher -> IO AEADName
forall typ ptr byte.
WithPtr typ ptr -> GetCString ptr byte -> typ -> IO AEADName
mkGetCString Cipher -> (BotanCipher -> IO a) -> IO a
WithPtr Cipher BotanCipher
withCipher GetCString BotanCipher CChar
botan_cipher_name

-- |Return the output length of this cipher, for a particular input length.
--
--  WARNING: This function is of limited use. From the C++ docs:
--   /**
--   * Returns the size of the output if this transform is used to process a
--   * message with input_length bytes. In most cases the answer is precise.
--   * If it is not possible to precise (namely for CBC decryption) instead an
--   * upper bound is returned.
--   */
--  We need to explicitly calculate padding + tag length
cipherOutputLength
    :: Cipher   -- ^ __cipher__
    -> Int      -- ^ __in_len__
    -> IO Int   -- ^ __out_len__
cipherOutputLength :: Cipher -> Int -> IO Int
cipherOutputLength = WithPtr Cipher BotanCipher
-> GetSize_csize BotanCipher -> Cipher -> Int -> IO Int
forall typ ptr.
WithPtr typ ptr -> GetSize_csize ptr -> typ -> Int -> IO Int
mkGetSize_csize Cipher -> (BotanCipher -> IO a) -> IO a
WithPtr Cipher BotanCipher
withCipher GetSize_csize BotanCipher
botan_cipher_output_length

-- NOTE: Unique function form?
-- |Return if the specified nonce length is valid for this cipher
-- NOTE: This just always seems to return 'True', even for -1 and maxBound
cipherValidNonceLength
    :: Cipher   -- ^ __cipher__
    -> Int      -- ^ __nl__
    -> IO Bool
cipherValidNonceLength :: Cipher -> Int -> IO Bool
cipherValidNonceLength = WithPtr Cipher BotanCipher
-> GetBoolCode_csize BotanCipher -> Cipher -> Int -> IO Bool
forall typ ptr.
WithPtr typ ptr -> GetBoolCode_csize ptr -> typ -> Int -> IO Bool
mkGetBoolCode_csize Cipher -> (BotanCipher -> IO a) -> IO a
WithPtr Cipher BotanCipher
withCipher GetBoolCode_csize BotanCipher
botan_cipher_valid_nonce_length

-- |Get the tag length of the cipher (0 for non-AEAD modes)
cipherGetTagLength
    :: Cipher   -- ^ __cipher__
    -> IO Int   -- ^ __tag_size__
cipherGetTagLength :: Cipher -> IO Int
cipherGetTagLength = WithPtr Cipher BotanCipher
-> GetSize BotanCipher -> Cipher -> IO Int
forall typ ptr. WithPtr typ ptr -> GetSize ptr -> typ -> IO Int
mkGetSize Cipher -> (BotanCipher -> IO a) -> IO a
WithPtr Cipher BotanCipher
withCipher GetSize BotanCipher
botan_cipher_get_tag_length

-- |Get the default nonce length of this cipher
cipherGetDefaultNonceLength
    :: Cipher   -- ^ __cipher__
    -> IO Int   -- ^ __nl__
cipherGetDefaultNonceLength :: Cipher -> IO Int
cipherGetDefaultNonceLength = WithPtr Cipher BotanCipher
-> GetSize BotanCipher -> Cipher -> IO Int
forall typ ptr. WithPtr typ ptr -> GetSize ptr -> typ -> IO Int
mkGetSize Cipher -> (BotanCipher -> IO a) -> IO a
WithPtr Cipher BotanCipher
withCipher GetSize BotanCipher
botan_cipher_get_default_nonce_length

-- |Return the update granularity of the cipher; botan_cipher_update must be
--  called with blocks of this size, except for the final.
cipherGetUpdateGranularity
    :: Cipher   -- ^ __cipher__
    -> IO Int   -- ^ __ug__
cipherGetUpdateGranularity :: Cipher -> IO Int
cipherGetUpdateGranularity = WithPtr Cipher BotanCipher
-> GetSize BotanCipher -> Cipher -> IO Int
forall typ ptr. WithPtr typ ptr -> GetSize ptr -> typ -> IO Int
mkGetSize Cipher -> (BotanCipher -> IO a) -> IO a
WithPtr Cipher BotanCipher
withCipher GetSize BotanCipher
botan_cipher_get_update_granularity

-- |Return the ideal update granularity of the cipher. This is some multiple of the
--  update granularity, reflecting possibilities for optimization.
--
-- Some ciphers (ChaChaPoly, EAX) may consume less input than the reported ideal granularity
cipherGetIdealUpdateGranularity
    :: Cipher   -- ^ __cipher__
    -> IO Int   -- ^ __ug__
cipherGetIdealUpdateGranularity :: Cipher -> IO Int
cipherGetIdealUpdateGranularity = WithPtr Cipher BotanCipher
-> GetSize BotanCipher -> Cipher -> IO Int
forall typ ptr. WithPtr typ ptr -> GetSize ptr -> typ -> IO Int
mkGetSize Cipher -> (BotanCipher -> IO a) -> IO a
WithPtr Cipher BotanCipher
withCipher GetSize BotanCipher
botan_cipher_get_ideal_update_granularity

-- |Get information about the key lengths.
cipherQueryKeylen
    :: Cipher       -- ^ __cipher__
    -> IO (Int,Int) -- ^ __(min,max)__
cipherQueryKeylen :: Cipher -> IO (Int, Int)
cipherQueryKeylen = WithPtr Cipher BotanCipher
-> GetSizes2 BotanCipher -> Cipher -> IO (Int, Int)
forall typ ptr.
WithPtr typ ptr -> GetSizes2 ptr -> typ -> IO (Int, Int)
mkGetSizes2 Cipher -> (BotanCipher -> IO a) -> IO a
WithPtr Cipher BotanCipher
withCipher GetSizes2 BotanCipher
botan_cipher_query_keylen
{-# DEPRECATED cipherQueryKeylen "Prefer cipherGetKeyspec." #-}

-- |Get information about the supported key lengths.
cipherGetKeyspec
    :: Cipher           -- ^ __cipher__
    -> IO (Int,Int,Int) -- ^ __(min,max,mod)__
cipherGetKeyspec :: Cipher -> IO (Int, Int, Int)
cipherGetKeyspec = WithPtr Cipher BotanCipher
-> GetSizes3 BotanCipher -> Cipher -> IO (Int, Int, Int)
forall typ ptr.
WithPtr typ ptr -> GetSizes3 ptr -> typ -> IO (Int, Int, Int)
mkGetSizes3 Cipher -> (BotanCipher -> IO a) -> IO a
WithPtr Cipher BotanCipher
withCipher GetSizes3 BotanCipher
botan_cipher_get_keyspec

-- |Set the key for this cipher object
cipherSetKey
    :: Cipher       -- ^ __cipher__
    -> ByteString   -- ^ __key__
    -> IO ()
cipherSetKey :: Cipher -> AEADName -> IO ()
cipherSetKey = WithPtr Cipher BotanCipher
-> (BotanCipher -> ConstPtr Word8 -> CSize -> IO CInt)
-> Cipher
-> AEADName
-> IO ()
forall object botan.
(forall a. object -> (botan -> IO a) -> IO a)
-> (botan -> ConstPtr Word8 -> CSize -> IO CInt)
-> object
-> AEADName
-> IO ()
mkWithObjectSetterCBytesLen Cipher -> (BotanCipher -> IO a) -> IO a
WithPtr Cipher BotanCipher
withCipher BotanCipher -> ConstPtr Word8 -> CSize -> IO CInt
botan_cipher_set_key

-- |Reset the message specific state for this cipher.
--  Without resetting the keys, this resets the nonce, and any state
--  associated with any message bits that have been processed so far.
--
--  It is conceptually equivalent to calling botan_cipher_clear followed
--  by botan_cipher_set_key with the original key.
cipherReset
    :: Cipher   -- ^ __cipher__
    -> IO ()
cipherReset :: Cipher -> IO ()
cipherReset = WithPtr Cipher BotanCipher -> Action BotanCipher -> Cipher -> IO ()
forall typ ptr. WithPtr typ ptr -> Action ptr -> typ -> IO ()
mkAction Cipher -> (BotanCipher -> IO a) -> IO a
WithPtr Cipher BotanCipher
withCipher Action BotanCipher
botan_cipher_reset

-- |Set the associated data. Will fail if cipher is not an AEAD
cipherSetAssociatedData
    :: Cipher       -- ^ __cipher__
    -> ByteString   -- ^ __ad__
    -> IO ()
cipherSetAssociatedData :: Cipher -> AEADName -> IO ()
cipherSetAssociatedData = WithPtr Cipher BotanCipher
-> (BotanCipher -> ConstPtr Word8 -> CSize -> IO CInt)
-> Cipher
-> AEADName
-> IO ()
forall object botan.
(forall a. object -> (botan -> IO a) -> IO a)
-> (botan -> ConstPtr Word8 -> CSize -> IO CInt)
-> object
-> AEADName
-> IO ()
mkWithObjectSetterCBytesLen Cipher -> (BotanCipher -> IO a) -> IO a
WithPtr Cipher BotanCipher
withCipher BotanCipher -> ConstPtr Word8 -> CSize -> IO CInt
botan_cipher_set_associated_data

-- |Begin processing a new message using the provided nonce
cipherStart
    :: Cipher       -- ^ __cipher__
    -> ByteString   -- ^ __nonce__
    -> IO ()
cipherStart :: Cipher -> AEADName -> IO ()
cipherStart = WithPtr Cipher BotanCipher
-> (BotanCipher -> ConstPtr Word8 -> CSize -> IO CInt)
-> Cipher
-> AEADName
-> IO ()
forall object botan.
(forall a. object -> (botan -> IO a) -> IO a)
-> (botan -> ConstPtr Word8 -> CSize -> IO CInt)
-> object
-> AEADName
-> IO ()
mkWithObjectSetterCBytesLen Cipher -> (BotanCipher -> IO a) -> IO a
WithPtr Cipher BotanCipher
withCipher BotanCipher -> ConstPtr Word8 -> CSize -> IO CInt
botan_cipher_start

-- |"Encrypt some data"
--
--  This function is ill-documented.
--
--  See the source for authoritative details:
--  https://github.com/randombit/botan/blob/72dc18bbf598f2c3bef81a4fb2915e9c3c524ac4/src/lib/ffi/ffi_cipher.cpp#L133
--
-- Some ciphers (ChaChaPoly, EAX) may consume less input than the reported ideal granularity
cipherUpdate ::
     Cipher               -- ^ __cipher__
  -> CipherUpdateFlags    -- ^ __flags__
  -> Int                  -- ^ __output_size__
  -> ByteString           -- ^ __input_bytes[]__
  -> IO (Int,ByteString)  -- ^ __(input_consumed,output[])__
cipherUpdate :: Cipher
-> CipherUpdateFlags -> Int -> AEADName -> IO (Int, AEADName)
cipherUpdate Cipher
ctx CipherUpdateFlags
flags Int
outputSz AEADName
input =
    Cipher -> (BotanCipher -> IO (Int, AEADName)) -> IO (Int, AEADName)
WithPtr Cipher BotanCipher
withCipher Cipher
ctx ((BotanCipher -> IO (Int, AEADName)) -> IO (Int, AEADName))
-> (BotanCipher -> IO (Int, AEADName)) -> IO (Int, AEADName)
forall a b. (a -> b) -> a -> b
$ \ BotanCipher
ctxPtr ->
    AEADName
-> (Ptr Word8 -> CSize -> IO (Int, AEADName)) -> IO (Int, AEADName)
forall byte a. AEADName -> (Ptr byte -> CSize -> IO a) -> IO a
unsafeAsBytesLen AEADName
input ((Ptr Word8 -> CSize -> IO (Int, AEADName)) -> IO (Int, AEADName))
-> (Ptr Word8 -> CSize -> IO (Int, AEADName)) -> IO (Int, AEADName)
forall a b. (a -> b) -> a -> b
$ \ Ptr Word8
inputPtr CSize
inputSz ->
    (Ptr CSize -> IO (Int, AEADName)) -> IO (Int, AEADName)
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CSize -> IO (Int, AEADName)) -> IO (Int, AEADName))
-> (Ptr CSize -> IO (Int, AEADName)) -> IO (Int, AEADName)
forall a b. (a -> b) -> a -> b
$ \ Ptr CSize
consumedPtr ->
    (Ptr CSize -> IO (Int, AEADName)) -> IO (Int, AEADName)
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CSize -> IO (Int, AEADName)) -> IO (Int, AEADName))
-> (Ptr CSize -> IO (Int, AEADName)) -> IO (Int, AEADName)
forall a b. (a -> b) -> a -> b
$ \ Ptr CSize
writtenPtr -> do
      Either InsufficientBufferSpaceException AEADName
eithOutput <-
        IO AEADName
-> IO (Either InsufficientBufferSpaceException AEADName)
forall e a. Exception e => IO a -> IO (Either e a)
try (IO AEADName
 -> IO (Either InsufficientBufferSpaceException AEADName))
-> IO AEADName
-> IO (Either InsufficientBufferSpaceException AEADName)
forall a b. (a -> b) -> a -> b
$ Int -> (Ptr Word8 -> IO ()) -> IO AEADName
forall byte. Int -> (Ptr byte -> IO ()) -> IO AEADName
allocBytes Int
outputSz ((Ptr Word8 -> IO ()) -> IO AEADName)
-> (Ptr Word8 -> IO ()) -> IO AEADName
forall a b. (a -> b) -> a -> b
$ \ Ptr Word8
outputPtr ->do
          HasCallStack => IO CInt -> IO ()
IO CInt -> IO ()
throwBotanIfNegative_ (IO CInt -> IO ()) -> IO CInt -> IO ()
forall a b. (a -> b) -> a -> b
$ BotanCipher
-> Word32
-> Ptr Word8
-> CSize
-> Ptr CSize
-> ConstPtr Word8
-> CSize
-> Ptr CSize
-> IO CInt
botan_cipher_update
              BotanCipher
ctxPtr
              (CipherUpdateFlags -> Word32
cipherUpdateFlags CipherUpdateFlags
flags)
              Ptr Word8
outputPtr
              (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
outputSz)
              Ptr CSize
writtenPtr
              (Ptr Word8 -> ConstPtr Word8
forall a. Ptr a -> ConstPtr a
ConstPtr Ptr Word8
inputPtr)
              CSize
inputSz
              Ptr CSize
consumedPtr
      -- If inssuficient buffer space, try again
      AEADName
output <- case Either InsufficientBufferSpaceException AEADName
eithOutput of
        Left InsufficientBufferSpaceException{} -> do
          CSize
outputSz' <- Ptr CSize -> IO CSize
forall a. Storable a => Ptr a -> IO a
peek Ptr CSize
writtenPtr
          Int -> (Ptr Word8 -> IO ()) -> IO AEADName
forall byte. Int -> (Ptr byte -> IO ()) -> IO AEADName
allocBytes (CSize -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral CSize
outputSz') ((Ptr Word8 -> IO ()) -> IO AEADName)
-> (Ptr Word8 -> IO ()) -> IO AEADName
forall a b. (a -> b) -> a -> b
$ \ Ptr Word8
outputPtr ->do
            HasCallStack => IO CInt -> IO ()
IO CInt -> IO ()
throwBotanIfNegative_ (IO CInt -> IO ()) -> IO CInt -> IO ()
forall a b. (a -> b) -> a -> b
$ BotanCipher
-> Word32
-> Ptr Word8
-> CSize
-> Ptr CSize
-> ConstPtr Word8
-> CSize
-> Ptr CSize
-> IO CInt
botan_cipher_update
                BotanCipher
ctxPtr
                (CipherUpdateFlags -> Word32
cipherUpdateFlags CipherUpdateFlags
flags)
                Ptr Word8
outputPtr
                CSize
outputSz'
                Ptr CSize
writtenPtr
                (Ptr Word8 -> ConstPtr Word8
forall a. Ptr a -> ConstPtr a
ConstPtr Ptr Word8
inputPtr)
                -- No input should be provided on the second try if the first
                -- try had the FINAL flags set
                (if CipherUpdateFlags
flags CipherUpdateFlags -> CipherUpdateFlags -> Bool
forall a. Eq a => a -> a -> Bool
== CipherUpdateFlags
CipherFinal then CSize
0 else CSize
inputSz)
                Ptr CSize
consumedPtr
        Right AEADName
bs -> AEADName -> IO AEADName
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure AEADName
bs
      Int
consumed <- CSize -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CSize -> Int) -> IO CSize -> IO Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr CSize -> IO CSize
forall a. Storable a => Ptr a -> IO a
peek Ptr CSize
consumedPtr
      Int
written <- CSize -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (CSize -> Int) -> IO CSize -> IO Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr CSize -> IO CSize
forall a. Storable a => Ptr a -> IO a
peek Ptr CSize
writtenPtr
      -- NOTE: The safety of this function is suspect - may require deepseq
      let processed :: AEADName
processed = Int -> AEADName -> AEADName
ByteString.take Int
written AEADName
output
          in AEADName
processed AEADName -> IO (Int, AEADName) -> IO (Int, AEADName)
forall a b. a -> b -> b
`seq` (Int, AEADName) -> IO (Int, AEADName)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int
consumed,AEADName
processed)

{- |
Encrypt and finalize a complete piece of data.

This is not a canonical Botan C/C++ function.
-}
cipherEncrypt :: Cipher -> ByteString -> IO ByteString
cipherEncrypt :: Cipher -> AEADName -> IO AEADName
cipherEncrypt = Cipher -> AEADName -> IO AEADName
cipherEncryptOffline

{- |
Encrypt and finalize a complete piece of data.

This is not a canonical Botan C/C++ function.
-}
cipherDecrypt :: Cipher -> ByteString -> IO ByteString
cipherDecrypt :: Cipher -> AEADName -> IO AEADName
cipherDecrypt = Cipher -> AEADName -> IO AEADName
cipherDecryptOffline

-- |Reset the key, nonce, AD and all other state on this cipher object
cipherClear :: Cipher -> IO ()
cipherClear :: Cipher -> IO ()
cipherClear = WithPtr Cipher BotanCipher -> Action BotanCipher -> Cipher -> IO ()
forall typ ptr. WithPtr typ ptr -> Action ptr -> typ -> IO ()
mkAction Cipher -> (BotanCipher -> IO a) -> IO a
WithPtr Cipher BotanCipher
withCipher Action BotanCipher
botan_cipher_clear

{-
Non-standard functions
-}

-- NOTE: out + ug + tag is safe overestimate for encryption
-- NOTE: out + ug - tag may not be a safe overestimate for decryption
{-# DEPRECATED cipherEstimateOutputLength "This will be moved from botan-low to botan" #-}
cipherEstimateOutputLength :: Cipher -> CipherInitFlags -> Int -> IO Int
cipherEstimateOutputLength :: Cipher -> CipherInitFlags -> Int -> IO Int
cipherEstimateOutputLength Cipher
ctx CipherInitFlags
flags Int
input = do
    Int
o <- Cipher -> Int -> IO Int
cipherOutputLength Cipher
ctx Int
input  -- NOTE: Flawed but usable
    Int
u <- Cipher -> IO Int
cipherGetUpdateGranularity Cipher
ctx -- TODO: When u == 1, it should be just input + t, right?
    Int
t <- Cipher -> IO Int
cipherGetTagLength Cipher
ctx
    if CipherInitFlags
flags CipherInitFlags -> CipherInitFlags -> Bool
forall a. Eq a => a -> a -> Bool
== CipherInitFlags
CipherEncrypt
        then Int -> IO Int
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int
o Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
u Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
t)
        else Int -> IO Int
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int
o Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
u Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
t) -- TODO: Maybe just 'o'...

-- NOTE: Offset must be a valid length of the input so far processed
-- NOTE: If (estimated) outputLength input + offset == outputLength (input + offset) then
--  we can just use cipherEstimateOutputLength instead of this
--  However, this may not be completely true due to block alignment requirements
--  For the moment this function exists for clarity.
{-# DEPRECATED cipherEstimateFinalOutputLength "Moving from botan-low to botan" #-}
cipherEstimateFinalOutputLength :: Cipher -> CipherInitFlags -> Int -> Int -> IO Int
cipherEstimateFinalOutputLength :: Cipher -> CipherInitFlags -> Int -> Int -> IO Int
cipherEstimateFinalOutputLength Cipher
ctx CipherInitFlags
flags Int
offset Int
input = do
    Int
len <- Cipher -> CipherInitFlags -> Int -> IO Int
cipherEstimateOutputLength Cipher
ctx CipherInitFlags
flags (Int
offset Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
input)
    Int -> IO Int
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> IO Int) -> Int -> IO Int
forall a b. (a -> b) -> a -> b
$ Int
len Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
offset

-- A better version of cipherUpdate
-- NOTE: It returns (processed,remaining) compared to (consumed,processed)
--  so the processed ciphertext has moved from snd to fst
-- TODO: Use Builder to do this
--  https://hackage.haskell.org/package/bytestring-0.12.0.2/docs/Data-ByteString-Builder.html
-- NOTE: There still is (an efficiency) use for a version that reports only consumed length
--  and defers the computation of the 'remaining' bytestring
{-# DEPRECATED cipherProcess "Moving from botan-low to botan" #-}
cipherProcess :: Cipher -> CipherUpdateFlags -> Int -> ByteString -> IO (ByteString,ByteString)
cipherProcess :: Cipher
-> CipherUpdateFlags -> Int -> AEADName -> IO (AEADName, AEADName)
cipherProcess Cipher
ctx CipherUpdateFlags
flags Int
outputSz AEADName
input = do
    (Int
consumed,AEADName
processed) <- Cipher
-> CipherUpdateFlags -> Int -> AEADName -> IO (Int, AEADName)
cipherUpdate Cipher
ctx CipherUpdateFlags
flags Int
outputSz AEADName
input
    -- NOTE: The safety of this function is suspect - may require deepseq
    let remaining :: AEADName
remaining = Int -> AEADName -> AEADName
ByteString.drop Int
consumed AEADName
input
        in AEADName
processed AEADName -> IO (AEADName, AEADName) -> IO (AEADName, AEADName)
forall a b. a -> b -> b
`seq` AEADName
remaining AEADName -> IO (AEADName, AEADName) -> IO (AEADName, AEADName)
forall a b. a -> b -> b
`seq` (AEADName, AEADName) -> IO (AEADName, AEADName)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (AEADName
processed,AEADName
remaining)

{-# DEPRECATED cipherProcessOffline "Moving from botan-low to botan" #-}
cipherProcessOffline :: Cipher -> CipherInitFlags -> ByteString -> IO ByteString
cipherProcessOffline :: Cipher -> CipherInitFlags -> AEADName -> IO AEADName
cipherProcessOffline Cipher
ctx CipherInitFlags
flags AEADName
msg = do
    Int
o <- Cipher -> CipherInitFlags -> Int -> IO Int
cipherEstimateOutputLength Cipher
ctx CipherInitFlags
flags (AEADName -> Int
ByteString.length AEADName
msg)
    -- snd <$> cipherUpdate ctx BOTAN_CIPHER_UPDATE_FLAG_FINAL o msg
    (AEADName, AEADName) -> AEADName
forall a b. (a, b) -> a
fst ((AEADName, AEADName) -> AEADName)
-> IO (AEADName, AEADName) -> IO AEADName
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Cipher
-> CipherUpdateFlags -> Int -> AEADName -> IO (AEADName, AEADName)
cipherProcess Cipher
ctx CipherUpdateFlags
CipherFinal Int
o AEADName
msg

{-# WARNING cipherEncryptOffline "May be renamed to cipherEncrypt, may be moved to botan" #-}
cipherEncryptOffline :: Cipher -> ByteString -> IO ByteString
cipherEncryptOffline :: Cipher -> AEADName -> IO AEADName
cipherEncryptOffline Cipher
ctx = Cipher -> CipherInitFlags -> AEADName -> IO AEADName
cipherProcessOffline Cipher
ctx CipherInitFlags
CipherEncrypt

{-# WARNING cipherDecryptOffline "May be renamed to cipherDecrypt, may be moved to botan" #-}
cipherDecryptOffline :: Cipher -> ByteString -> IO ByteString
cipherDecryptOffline :: Cipher -> AEADName -> IO AEADName
cipherDecryptOffline Cipher
ctx = Cipher -> CipherInitFlags -> AEADName -> IO AEADName
cipherProcessOffline Cipher
ctx CipherInitFlags
CipherDecrypt

{-
Experiments with online processing
-}

-- cipherEncryptOnline :: Cipher -> ByteString -> IO ByteString
-- cipherEncryptOnline ctx msg = do
--     u <- cipherGetUpdateGranularity ctx
--     t <- cipherGetTagLength ctx
--     g <- cipherGetIdealUpdateGranularity ctx
--     ByteString.concat <$> go 0 u t g msg
--     where
--         go i u t g bs = case ByteString.splitAt g bs of
--             (block,"")      -> do
--                 o <- cipherOutputLength ctx (i + ByteString.length block)  -- NOTE: Flawed but usable
--                 (_,encblock) <- cipherUpdate ctx BOTAN_CIPHER_UPDATE_FLAG_FINAL (o + u + t - i) block
--                 return [encblock]
--             (block,rest)    -> do
--                 (_,encblock) <- cipherUpdate ctx BOTAN_CIPHER_UPDATE_FLAG_NONE g block
--                 encrest <- go (i + g) u t g rest
--                 return $! encblock : encrest

-- TODO: Consolidate online encipher / decipher
-- TODO: Use Builder to do this
--  https://hackage.haskell.org/package/bytestring-0.12.0.2/docs/Data-ByteString-Builder.html
{-# DEPRECATED cipherEncryptOnline "Moving from botan-low to botan" #-}
cipherEncryptOnline :: Cipher -> ByteString -> IO ByteString
cipherEncryptOnline :: Cipher -> AEADName -> IO AEADName
cipherEncryptOnline Cipher
ctx AEADName
msg = do
    Int
g <- Cipher -> IO Int
cipherGetIdealUpdateGranularity Cipher
ctx
    [AEADName] -> AEADName
ByteString.concat ([AEADName] -> AEADName) -> IO [AEADName] -> IO AEADName
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> Int -> AEADName -> IO [AEADName]
go Int
0 Int
g AEADName
msg
    where
        go :: Int -> Int -> AEADName -> IO [AEADName]
go Int
i Int
g AEADName
bs = case Int -> AEADName -> (AEADName, AEADName)
ByteString.splitAt Int
g AEADName
bs of
            (AEADName
block,AEADName
"")      -> do
                Int
o <- Cipher -> CipherInitFlags -> Int -> Int -> IO Int
cipherEstimateFinalOutputLength Cipher
ctx CipherInitFlags
CipherEncrypt Int
i (AEADName -> Int
ByteString.length AEADName
block)
                (AEADName
processed,AEADName
_) <- Cipher
-> CipherUpdateFlags -> Int -> AEADName -> IO (AEADName, AEADName)
cipherProcess Cipher
ctx CipherUpdateFlags
CipherFinal Int
o AEADName
block
                [AEADName] -> IO [AEADName]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return [AEADName
processed]
            (AEADName
block,AEADName
rest)    -> do
                (AEADName
processed,AEADName
remaining) <- Cipher
-> CipherUpdateFlags -> Int -> AEADName -> IO (AEADName, AEADName)
cipherProcess Cipher
ctx CipherUpdateFlags
CipherUpdate Int
g AEADName
block
                (AEADName
processed :) ([AEADName] -> [AEADName]) -> IO [AEADName] -> IO [AEADName]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> Int -> AEADName -> IO [AEADName]
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
g) Int
g (AEADName
remaining AEADName -> AEADName -> AEADName
forall a. Semigroup a => a -> a -> a
<> AEADName
rest)
                -- Though this following version may be more efficient especially with lazy bytestrings
                --  or builder, though note *which* update function it uses - the original
                -- (consumed,processed) <- cipherUpdate ctx BOTAN_CIPHER_UPDATE_FLAG_NONE g block
                -- (processed :) <$> go (i + g) g (ByteString.drop consumed bs)

-- TODO: Consolidate online encipher / decipher
{-# DEPRECATED cipherDecryptOnline "Moving from botan-low to botan" #-}
cipherDecryptOnline :: Cipher -> ByteString -> IO ByteString
cipherDecryptOnline :: Cipher -> AEADName -> IO AEADName
cipherDecryptOnline Cipher
ctx AEADName
msg = do
    Int
g <- Cipher -> IO Int
cipherGetIdealUpdateGranularity Cipher
ctx
    Int
t <- Cipher -> IO Int
cipherGetTagLength Cipher
ctx
    [AEADName] -> AEADName
ByteString.concat ([AEADName] -> AEADName) -> IO [AEADName] -> IO AEADName
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> Int -> Int -> AEADName -> IO [AEADName]
forall {t}. Int -> Int -> t -> AEADName -> IO [AEADName]
go Int
0 Int
g Int
t AEADName
msg
    where
        go :: Int -> Int -> t -> AEADName -> IO [AEADName]
go Int
i Int
g t
t AEADName
bs = case Int -> AEADName -> (AEADName, AEADName)
ByteString.splitAt Int
g AEADName
bs of
            (AEADName
block,AEADName
"")      -> do
                Int
o <- Cipher -> CipherInitFlags -> Int -> Int -> IO Int
cipherEstimateFinalOutputLength Cipher
ctx CipherInitFlags
CipherDecrypt Int
i (AEADName -> Int
ByteString.length AEADName
block)
                (AEADName
processed,AEADName
_) <- Cipher
-> CipherUpdateFlags -> Int -> AEADName -> IO (AEADName, AEADName)
cipherProcess Cipher
ctx CipherUpdateFlags
CipherFinal Int
o AEADName
block
                [AEADName] -> IO [AEADName]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return [AEADName
processed]
            (AEADName
block,AEADName
rest)    -> do
                (AEADName
processed,AEADName
remaining) <- Cipher
-> CipherUpdateFlags -> Int -> AEADName -> IO (AEADName, AEADName)
cipherProcess Cipher
ctx CipherUpdateFlags
CipherUpdate Int
g AEADName
block
                (AEADName
processed :) ([AEADName] -> [AEADName]) -> IO [AEADName] -> IO [AEADName]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> Int -> t -> AEADName -> IO [AEADName]
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
g) Int
g t
t (AEADName
remaining AEADName -> AEADName -> AEADName
forall a. Semigroup a => a -> a -> a
<> AEADName
rest)