-- |
-- Module      : Crypto.Cipher.Types.Block
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : Stable
-- Portability : Excellent
--
-- Block cipher basic types
--
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE Rank2Types #-}
module Crypto.Cipher.Types.Block
    (
    -- * BlockCipher
      BlockCipher(..)
    , BlockCipher128(..)
    -- * Initialization vector (IV)
    , IV(..)
    , makeIV
    , nullIV
    , ivAdd
    -- * XTS
    , XTS
    -- * AEAD
    , AEAD(..)
    -- , AEADState(..)
    , AEADModeImpl(..)
    , aeadAppendHeader
    , aeadEncrypt
    , aeadDecrypt
    , aeadFinalize
    -- * CFB 8 bits
    --, cfb8Encrypt
    --, cfb8Decrypt
    ) where

import           Data.Word
import           Crypto.Error
import           Crypto.Cipher.Types.Base
import           Crypto.Cipher.Types.GF
import           Crypto.Cipher.Types.AEAD
import           Crypto.Cipher.Types.Utils

import           Crypto.Internal.ByteArray (ByteArrayAccess, ByteArray, withByteArray, Bytes)
import qualified Crypto.Internal.ByteArray as B

import           Foreign.Ptr
import           Foreign.Storable

-- | an IV parametrized by the cipher
data IV c = forall byteArray . ByteArray byteArray => IV byteArray

instance BlockCipher c => ByteArrayAccess (IV c) where
    withByteArray :: IV c -> (Ptr p -> IO a) -> IO a
withByteArray (IV byteArray
z) Ptr p -> IO a
f = byteArray -> (Ptr p -> IO a) -> IO a
forall ba p a. ByteArrayAccess ba => ba -> (Ptr p -> IO a) -> IO a
withByteArray byteArray
z Ptr p -> IO a
f
    length :: IV c -> Int
length (IV byteArray
z) = byteArray -> Int
forall ba. ByteArrayAccess ba => ba -> Int
B.length byteArray
z
instance Eq (IV c) where
    (IV byteArray
a) == :: IV c -> IV c -> Bool
== (IV byteArray
b) = byteArray -> byteArray -> Bool
forall bs1 bs2.
(ByteArrayAccess bs1, ByteArrayAccess bs2) =>
bs1 -> bs2 -> Bool
B.eq byteArray
a byteArray
b

-- | XTS callback
type XTS ba cipher = (cipher, cipher)
                  -> IV cipher        -- ^ Usually represent the Data Unit (e.g. disk sector)
                  -> DataUnitOffset   -- ^ Offset in the data unit in number of blocks
                  -> ba               -- ^ Data
                  -> ba               -- ^ Processed Data

-- | Symmetric block cipher class
class Cipher cipher => BlockCipher cipher where
    -- | Return the size of block required for this block cipher
    blockSize    :: cipher -> Int

    -- | Encrypt blocks
    --
    -- the input string need to be multiple of the block size
    ecbEncrypt :: ByteArray ba => cipher -> ba -> ba

    -- | Decrypt blocks
    --
    -- the input string need to be multiple of the block size
    ecbDecrypt :: ByteArray ba => cipher -> ba -> ba

    -- | encrypt using the CBC mode.
    --
    -- input need to be a multiple of the blocksize
    cbcEncrypt :: ByteArray ba => cipher -> IV cipher -> ba -> ba
    cbcEncrypt = cipher -> IV cipher -> ba -> ba
forall ba cipher.
(ByteArray ba, BlockCipher cipher) =>
cipher -> IV cipher -> ba -> ba
cbcEncryptGeneric
    -- | decrypt using the CBC mode.
    --
    -- input need to be a multiple of the blocksize
    cbcDecrypt :: ByteArray ba => cipher -> IV cipher -> ba -> ba
    cbcDecrypt = cipher -> IV cipher -> ba -> ba
forall ba cipher.
(ByteArray ba, BlockCipher cipher) =>
cipher -> IV cipher -> ba -> ba
cbcDecryptGeneric

    -- | encrypt using the CFB mode.
    --
    -- input need to be a multiple of the blocksize
    cfbEncrypt :: ByteArray ba => cipher -> IV cipher -> ba -> ba
    cfbEncrypt = cipher -> IV cipher -> ba -> ba
forall ba cipher.
(ByteArray ba, BlockCipher cipher) =>
cipher -> IV cipher -> ba -> ba
cfbEncryptGeneric
    -- | decrypt using the CFB mode.
    --
    -- input need to be a multiple of the blocksize
    cfbDecrypt :: ByteArray ba => cipher -> IV cipher -> ba -> ba
    cfbDecrypt = cipher -> IV cipher -> ba -> ba
forall ba cipher.
(ByteArray ba, BlockCipher cipher) =>
cipher -> IV cipher -> ba -> ba
cfbDecryptGeneric

    -- | combine using the CTR mode.
    --
    -- CTR mode produce a stream of randomized data that is combined
    -- (by XOR operation) with the input stream.
    --
    -- encryption and decryption are the same operation.
    --
    -- input can be of any size
    ctrCombine :: ByteArray ba => cipher -> IV cipher -> ba -> ba
    ctrCombine = cipher -> IV cipher -> ba -> ba
forall ba cipher.
(ByteArray ba, BlockCipher cipher) =>
cipher -> IV cipher -> ba -> ba
ctrCombineGeneric

    -- | Initialize a new AEAD State
    --
    -- When Nothing is returns, it means the mode is not handled.
    aeadInit :: ByteArrayAccess iv => AEADMode -> cipher -> iv -> CryptoFailable (AEAD cipher)
    aeadInit AEADMode
_ cipher
_ iv
_ = CryptoError -> CryptoFailable (AEAD cipher)
forall a. CryptoError -> CryptoFailable a
CryptoFailed CryptoError
CryptoError_AEADModeNotSupported

-- | class of block cipher with a 128 bits block size
class BlockCipher cipher => BlockCipher128 cipher where
    -- | encrypt using the XTS mode.
    --
    -- input need to be a multiple of the blocksize, and the cipher
    -- need to process 128 bits block only
    xtsEncrypt :: ByteArray ba
               => (cipher, cipher)
               -> IV cipher        -- ^ Usually represent the Data Unit (e.g. disk sector)
               -> DataUnitOffset   -- ^ Offset in the data unit in number of blocks
               -> ba               -- ^ Plaintext
               -> ba               -- ^ Ciphertext
    xtsEncrypt = (cipher, cipher) -> IV cipher -> DataUnitOffset -> ba -> ba
forall ba cipher.
(ByteArray ba, BlockCipher128 cipher) =>
XTS ba cipher
xtsEncryptGeneric

    -- | decrypt using the XTS mode.
    --
    -- input need to be a multiple of the blocksize, and the cipher
    -- need to process 128 bits block only
    xtsDecrypt :: ByteArray ba
               => (cipher, cipher)
               -> IV cipher        -- ^ Usually represent the Data Unit (e.g. disk sector)
               -> DataUnitOffset   -- ^ Offset in the data unit in number of blocks
               -> ba               -- ^ Ciphertext
               -> ba               -- ^ Plaintext
    xtsDecrypt = (cipher, cipher) -> IV cipher -> DataUnitOffset -> ba -> ba
forall ba cipher.
(ByteArray ba, BlockCipher128 cipher) =>
XTS ba cipher
xtsDecryptGeneric

-- | Create an IV for a specified block cipher
makeIV :: (ByteArrayAccess b, BlockCipher c) => b -> Maybe (IV c)
makeIV :: b -> Maybe (IV c)
makeIV b
b = c -> Maybe (IV c)
forall c. BlockCipher c => c -> Maybe (IV c)
toIV c
forall a. HasCallStack => a
undefined
  where toIV :: BlockCipher c => c -> Maybe (IV c)
        toIV :: c -> Maybe (IV c)
toIV c
cipher
          | b -> Int
forall ba. ByteArrayAccess ba => ba -> Int
B.length b
b Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
sz = IV c -> Maybe (IV c)
forall a. a -> Maybe a
Just (IV c -> Maybe (IV c)) -> IV c -> Maybe (IV c)
forall a b. (a -> b) -> a -> b
$ Bytes -> IV c
forall c byteArray. ByteArray byteArray => byteArray -> IV c
IV (b -> Bytes
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
B.convert b
b :: Bytes)
          | Bool
otherwise        = Maybe (IV c)
forall a. Maybe a
Nothing
          where sz :: Int
sz = c -> Int
forall cipher. BlockCipher cipher => cipher -> Int
blockSize c
cipher

-- | Create an IV that is effectively representing the number 0
nullIV :: BlockCipher c => IV c
nullIV :: IV c
nullIV = c -> IV c
forall c. BlockCipher c => c -> IV c
toIV c
forall a. HasCallStack => a
undefined
  where toIV :: BlockCipher c => c -> IV c
        toIV :: c -> IV c
toIV c
cipher = Bytes -> IV c
forall c byteArray. ByteArray byteArray => byteArray -> IV c
IV (Int -> Bytes
forall ba. ByteArray ba => Int -> ba
B.zero (c -> Int
forall cipher. BlockCipher cipher => cipher -> Int
blockSize c
cipher) :: Bytes)

-- | Increment an IV by a number.
--
-- Assume the IV is in Big Endian format.
ivAdd :: IV c -> Int -> IV c
ivAdd :: IV c -> Int -> IV c
ivAdd (IV byteArray
b) Int
i = byteArray -> IV c
forall c byteArray. ByteArray byteArray => byteArray -> IV c
IV (byteArray -> IV c) -> byteArray -> IV c
forall a b. (a -> b) -> a -> b
$ byteArray -> byteArray
forall bs. ByteArray bs => bs -> bs
copy byteArray
b
  where copy :: ByteArray bs => bs -> bs
        copy :: bs -> bs
copy bs
bs = bs -> (Ptr Word8 -> IO ()) -> bs
forall bs1 bs2 p.
(ByteArrayAccess bs1, ByteArray bs2) =>
bs1 -> (Ptr p -> IO ()) -> bs2
B.copyAndFreeze bs
bs ((Ptr Word8 -> IO ()) -> bs) -> (Ptr Word8 -> IO ()) -> bs
forall a b. (a -> b) -> a -> b
$ Int -> Int -> Ptr Word8 -> IO ()
loop Int
i (bs -> Int
forall ba. ByteArrayAccess ba => ba -> Int
B.length bs
bs Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)

        loop :: Int -> Int -> Ptr Word8 -> IO ()
        loop :: Int -> Int -> Ptr Word8 -> IO ()
loop Int
acc Int
ofs Ptr Word8
p
            | Int
ofs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0   = () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
            | Bool
otherwise = do
                Word8
v <- Ptr Word8 -> IO Word8
forall a. Storable a => Ptr a -> IO a
peek (Ptr Word8
p Ptr Word8 -> Int -> Ptr Word8
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
ofs) :: IO Word8
                let accv :: Int
accv    = Int
acc Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
v
                    (Int
hi,Int
lo) = Int
accv Int -> Int -> (Int, Int)
forall a. Integral a => a -> a -> (a, a)
`divMod` Int
256
                Ptr Word8 -> Word8 -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke (Ptr Word8
p Ptr Word8 -> Int -> Ptr Word8
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
ofs) (Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
lo :: Word8)
                Int -> Int -> Ptr Word8 -> IO ()
loop Int
hi (Int
ofs Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Ptr Word8
p

cbcEncryptGeneric :: (ByteArray ba, BlockCipher cipher) => cipher -> IV cipher -> ba -> ba
cbcEncryptGeneric :: cipher -> IV cipher -> ba -> ba
cbcEncryptGeneric cipher
cipher IV cipher
ivini ba
input = [ba] -> ba
forall a. Monoid a => [a] -> a
mconcat ([ba] -> ba) -> [ba] -> ba
forall a b. (a -> b) -> a -> b
$ IV cipher -> [ba] -> [ba]
forall a c a.
(ByteArray a, BlockCipher c, ByteArrayAccess a) =>
IV c -> [a] -> [a]
doEnc IV cipher
ivini ([ba] -> [ba]) -> [ba] -> [ba]
forall a b. (a -> b) -> a -> b
$ Int -> ba -> [ba]
forall b. ByteArray b => Int -> b -> [b]
chunk (cipher -> Int
forall cipher. BlockCipher cipher => cipher -> Int
blockSize cipher
cipher) ba
input
  where doEnc :: IV c -> [a] -> [a]
doEnc IV c
_  []     = []
        doEnc IV c
iv (a
i:[a]
is) =
            let o :: a
o = cipher -> a -> a
forall cipher ba.
(BlockCipher cipher, ByteArray ba) =>
cipher -> ba -> ba
ecbEncrypt cipher
cipher (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ IV c -> a -> a
forall a b c.
(ByteArrayAccess a, ByteArrayAccess b, ByteArray c) =>
a -> b -> c
B.xor IV c
iv a
i
             in a
o a -> [a] -> [a]
forall a. a -> [a] -> [a]
: IV c -> [a] -> [a]
doEnc (a -> IV c
forall c byteArray. ByteArray byteArray => byteArray -> IV c
IV a
o) [a]
is

cbcDecryptGeneric :: (ByteArray ba, BlockCipher cipher) => cipher -> IV cipher -> ba -> ba
cbcDecryptGeneric :: cipher -> IV cipher -> ba -> ba
cbcDecryptGeneric cipher
cipher IV cipher
ivini ba
input = [ba] -> ba
forall a. Monoid a => [a] -> a
mconcat ([ba] -> ba) -> [ba] -> ba
forall a b. (a -> b) -> a -> b
$ IV cipher -> [ba] -> [ba]
forall byteArray a c.
(ByteArray a, ByteArray byteArray, BlockCipher c) =>
IV c -> [byteArray] -> [a]
doDec IV cipher
ivini ([ba] -> [ba]) -> [ba] -> [ba]
forall a b. (a -> b) -> a -> b
$ Int -> ba -> [ba]
forall b. ByteArray b => Int -> b -> [b]
chunk (cipher -> Int
forall cipher. BlockCipher cipher => cipher -> Int
blockSize cipher
cipher) ba
input
  where
        doDec :: IV c -> [byteArray] -> [a]
doDec IV c
_  []     = []
        doDec IV c
iv (byteArray
i:[byteArray]
is) =
            let o :: a
o = IV c -> byteArray -> a
forall a b c.
(ByteArrayAccess a, ByteArrayAccess b, ByteArray c) =>
a -> b -> c
B.xor IV c
iv (byteArray -> a) -> byteArray -> a
forall a b. (a -> b) -> a -> b
$ cipher -> byteArray -> byteArray
forall cipher ba.
(BlockCipher cipher, ByteArray ba) =>
cipher -> ba -> ba
ecbDecrypt cipher
cipher byteArray
i
             in a
o a -> [a] -> [a]
forall a. a -> [a] -> [a]
: IV c -> [byteArray] -> [a]
doDec (byteArray -> IV c
forall c byteArray. ByteArray byteArray => byteArray -> IV c
IV byteArray
i) [byteArray]
is

cfbEncryptGeneric :: (ByteArray ba, BlockCipher cipher) => cipher -> IV cipher -> ba -> ba
cfbEncryptGeneric :: cipher -> IV cipher -> ba -> ba
cfbEncryptGeneric cipher
cipher IV cipher
ivini ba
input = [ba] -> ba
forall a. Monoid a => [a] -> a
mconcat ([ba] -> ba) -> [ba] -> ba
forall a b. (a -> b) -> a -> b
$ IV cipher -> [ba] -> [ba]
forall a a c.
(ByteArray a, ByteArrayAccess a) =>
IV c -> [a] -> [a]
doEnc IV cipher
ivini ([ba] -> [ba]) -> [ba] -> [ba]
forall a b. (a -> b) -> a -> b
$ Int -> ba -> [ba]
forall b. ByteArray b => Int -> b -> [b]
chunk (cipher -> Int
forall cipher. BlockCipher cipher => cipher -> Int
blockSize cipher
cipher) ba
input
  where
        doEnc :: IV c -> [a] -> [a]
doEnc IV c
_  []     = []
        doEnc (IV byteArray
iv) (a
i:[a]
is) =
            let o :: a
o = a -> byteArray -> a
forall a b c.
(ByteArrayAccess a, ByteArrayAccess b, ByteArray c) =>
a -> b -> c
B.xor a
i (byteArray -> a) -> byteArray -> a
forall a b. (a -> b) -> a -> b
$ cipher -> byteArray -> byteArray
forall cipher ba.
(BlockCipher cipher, ByteArray ba) =>
cipher -> ba -> ba
ecbEncrypt cipher
cipher byteArray
iv
             in a
o a -> [a] -> [a]
forall a. a -> [a] -> [a]
: IV c -> [a] -> [a]
doEnc (a -> IV c
forall c byteArray. ByteArray byteArray => byteArray -> IV c
IV a
o) [a]
is

cfbDecryptGeneric :: (ByteArray ba, BlockCipher cipher) => cipher -> IV cipher -> ba -> ba
cfbDecryptGeneric :: cipher -> IV cipher -> ba -> ba
cfbDecryptGeneric cipher
cipher IV cipher
ivini ba
input = [ba] -> ba
forall a. Monoid a => [a] -> a
mconcat ([ba] -> ba) -> [ba] -> ba
forall a b. (a -> b) -> a -> b
$ IV cipher -> [ba] -> [ba]
forall a a c. (ByteArray a, ByteArray a) => IV c -> [a] -> [a]
doDec IV cipher
ivini ([ba] -> [ba]) -> [ba] -> [ba]
forall a b. (a -> b) -> a -> b
$ Int -> ba -> [ba]
forall b. ByteArray b => Int -> b -> [b]
chunk (cipher -> Int
forall cipher. BlockCipher cipher => cipher -> Int
blockSize cipher
cipher) ba
input
  where
        doDec :: IV c -> [a] -> [a]
doDec IV c
_  []     = []
        doDec (IV byteArray
iv) (a
i:[a]
is) =
            let o :: a
o = a -> byteArray -> a
forall a b c.
(ByteArrayAccess a, ByteArrayAccess b, ByteArray c) =>
a -> b -> c
B.xor a
i (byteArray -> a) -> byteArray -> a
forall a b. (a -> b) -> a -> b
$ cipher -> byteArray -> byteArray
forall cipher ba.
(BlockCipher cipher, ByteArray ba) =>
cipher -> ba -> ba
ecbEncrypt cipher
cipher byteArray
iv
             in a
o a -> [a] -> [a]
forall a. a -> [a] -> [a]
: IV c -> [a] -> [a]
doDec (a -> IV c
forall c byteArray. ByteArray byteArray => byteArray -> IV c
IV a
i) [a]
is

ctrCombineGeneric :: (ByteArray ba, BlockCipher cipher) => cipher -> IV cipher -> ba -> ba
ctrCombineGeneric :: cipher -> IV cipher -> ba -> ba
ctrCombineGeneric cipher
cipher IV cipher
ivini ba
input = [ba] -> ba
forall a. Monoid a => [a] -> a
mconcat ([ba] -> ba) -> [ba] -> ba
forall a b. (a -> b) -> a -> b
$ IV cipher -> [ba] -> [ba]
forall a a c.
(ByteArray a, ByteArrayAccess a) =>
IV c -> [a] -> [a]
doCnt IV cipher
ivini ([ba] -> [ba]) -> [ba] -> [ba]
forall a b. (a -> b) -> a -> b
$ Int -> ba -> [ba]
forall b. ByteArray b => Int -> b -> [b]
chunk (cipher -> Int
forall cipher. BlockCipher cipher => cipher -> Int
blockSize cipher
cipher) ba
input
  where doCnt :: IV c -> [a] -> [a]
doCnt IV c
_  [] = []
        doCnt iv :: IV c
iv@(IV byteArray
ivd) (a
i:[a]
is) =
            let ivEnc :: byteArray
ivEnc = cipher -> byteArray -> byteArray
forall cipher ba.
(BlockCipher cipher, ByteArray ba) =>
cipher -> ba -> ba
ecbEncrypt cipher
cipher byteArray
ivd
             in a -> byteArray -> a
forall a b c.
(ByteArrayAccess a, ByteArrayAccess b, ByteArray c) =>
a -> b -> c
B.xor a
i byteArray
ivEnc a -> [a] -> [a]
forall a. a -> [a] -> [a]
: IV c -> [a] -> [a]
doCnt (IV c -> Int -> IV c
forall c. IV c -> Int -> IV c
ivAdd IV c
iv Int
1) [a]
is

xtsEncryptGeneric :: (ByteArray ba, BlockCipher128 cipher) => XTS ba cipher
xtsEncryptGeneric :: XTS ba cipher
xtsEncryptGeneric = (cipher -> ba -> ba) -> XTS ba cipher
forall ba cipher.
(ByteArray ba, BlockCipher128 cipher) =>
(cipher -> ba -> ba)
-> (cipher, cipher) -> IV cipher -> DataUnitOffset -> ba -> ba
xtsGeneric cipher -> ba -> ba
forall cipher ba.
(BlockCipher cipher, ByteArray ba) =>
cipher -> ba -> ba
ecbEncrypt

xtsDecryptGeneric :: (ByteArray ba, BlockCipher128 cipher) => XTS ba cipher
xtsDecryptGeneric :: XTS ba cipher
xtsDecryptGeneric = (cipher -> ba -> ba) -> XTS ba cipher
forall ba cipher.
(ByteArray ba, BlockCipher128 cipher) =>
(cipher -> ba -> ba)
-> (cipher, cipher) -> IV cipher -> DataUnitOffset -> ba -> ba
xtsGeneric cipher -> ba -> ba
forall cipher ba.
(BlockCipher cipher, ByteArray ba) =>
cipher -> ba -> ba
ecbDecrypt

xtsGeneric :: (ByteArray ba, BlockCipher128 cipher)
           => (cipher -> ba -> ba)
           -> (cipher, cipher)
           -> IV cipher
           -> DataUnitOffset
           -> ba
           -> ba
xtsGeneric :: (cipher -> ba -> ba)
-> (cipher, cipher) -> IV cipher -> DataUnitOffset -> ba -> ba
xtsGeneric cipher -> ba -> ba
f (cipher
cipher, cipher
tweakCipher) (IV byteArray
iv) DataUnitOffset
sPoint ba
input =
    [ba] -> ba
forall a. Monoid a => [a] -> a
mconcat ([ba] -> ba) -> [ba] -> ba
forall a b. (a -> b) -> a -> b
$ byteArray -> [ba] -> [ba]
forall b a a.
(ByteArray b, ByteArray a, ByteArrayAccess a) =>
b -> [a] -> [a]
doXts byteArray
iniTweak ([ba] -> [ba]) -> [ba] -> [ba]
forall a b. (a -> b) -> a -> b
$ Int -> ba -> [ba]
forall b. ByteArray b => Int -> b -> [b]
chunk (cipher -> Int
forall cipher. BlockCipher cipher => cipher -> Int
blockSize cipher
cipher) ba
input
  where encTweak :: byteArray
encTweak = cipher -> byteArray -> byteArray
forall cipher ba.
(BlockCipher cipher, ByteArray ba) =>
cipher -> ba -> ba
ecbEncrypt cipher
tweakCipher byteArray
iv
        iniTweak :: byteArray
iniTweak = (byteArray -> byteArray) -> byteArray -> [byteArray]
forall a. (a -> a) -> a -> [a]
iterate byteArray -> byteArray
forall bs. ByteArray bs => bs -> bs
xtsGFMul byteArray
encTweak [byteArray] -> Int -> byteArray
forall a. [a] -> Int -> a
!! DataUnitOffset -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral DataUnitOffset
sPoint
        doXts :: b -> [a] -> [a]
doXts b
_     []     = []
        doXts b
tweak (a
i:[a]
is) =
            let o :: a
o = ba -> b -> a
forall a b c.
(ByteArrayAccess a, ByteArrayAccess b, ByteArray c) =>
a -> b -> c
B.xor (cipher -> ba -> ba
f cipher
cipher (ba -> ba) -> ba -> ba
forall a b. (a -> b) -> a -> b
$ a -> b -> ba
forall a b c.
(ByteArrayAccess a, ByteArrayAccess b, ByteArray c) =>
a -> b -> c
B.xor a
i b
tweak) b
tweak
             in a
o a -> [a] -> [a]
forall a. a -> [a] -> [a]
: b -> [a] -> [a]
doXts (b -> b
forall bs. ByteArray bs => bs -> bs
xtsGFMul b
tweak) [a]
is

{-
-- | Encrypt using CFB mode in 8 bit output
--
-- Effectively turn a Block cipher in CFB mode into a Stream cipher
cfb8Encrypt :: BlockCipher a => a -> IV a -> B.byteString -> B.byteString
cfb8Encrypt ctx origIv msg = B.unsafeCreate (B.length msg) $ \dst -> loop dst origIv msg
  where loop d iv@(IV i) m
            | B.null m  = return ()
            | otherwise = poke d out >> loop (d `plusPtr` 1) ni (B.drop 1 m)
          where m'  = if B.length m < blockSize ctx
                            then m `B.append` B.replicate (blockSize ctx - B.length m) 0
                            else B.take (blockSize ctx) m
                r   = cfbEncrypt ctx iv m'
                out = B.head r
                ni  = IV (B.drop 1 i `B.snoc` out)

-- | Decrypt using CFB mode in 8 bit output
--
-- Effectively turn a Block cipher in CFB mode into a Stream cipher
cfb8Decrypt :: BlockCipher a => a -> IV a -> B.byteString -> B.byteString
cfb8Decrypt ctx origIv msg = B.unsafeCreate (B.length msg) $ \dst -> loop dst origIv msg
  where loop d iv@(IV i) m
            | B.null m  = return ()
            | otherwise = poke d out >> loop (d `plusPtr` 1) ni (B.drop 1 m)
          where m'  = if B.length m < blockSize ctx
                            then m `B.append` B.replicate (blockSize ctx - B.length m) 0
                            else B.take (blockSize ctx) m
                r   = cfbDecrypt ctx iv m'
                out = B.head r
                ni  = IV (B.drop 1 i `B.snoc` B.head m')
-}