    -- |
    -- Module      : Crypto.Store.KeyWrap.AES
    -- License     : BSD-style
    -- Maintainer  : Olivier Chéron <olivier.cheron@gmail.com>
    -- Stability   : experimental
    -- Portability : unknown
    --
    -- AES Key Wrap (<https://tools.ietf.org/html/rfc3394 RFC 3394>) and Extended
    -- Key Wrap (<https://tools.ietf.org/html/rfc5649 RFC 5649>)
    --
    -- Should be used with a cipher from module "Crypto.Cipher.AES".
    {-# LANGUAGE BangPatterns #-}
    module Crypto.Store.KeyWrap.AES
        ( wrap
        , unwrap
        , wrapPad
        , unwrapPad
        ) where

    import           Data.Bits
    import           Data.ByteArray (ByteArray, ByteArrayAccess, Bytes)
    import qualified Data.ByteArray as B
    import           Data.List
    import           Data.Word

    import Crypto.Cipher.Types

    import Foreign.Storable

    import Crypto.Store.Error
    import Crypto.Store.Util

    type Chunked ba = [ba]
    type Pair ba = (ba, ba)

    -- TODO: should use a low-level AES implementation to reduce allocations

    aes' :: (BlockCipher aes, ByteArray ba) => aes -> Pair ba -> ba
    aes' :: forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> Pair ba -> ba
    aes' aes
    cipher (ba
    msb, ba
    lsb) = aes -> ba -> ba
    forall cipher ba.
    (BlockCipher cipher, ByteArray ba) =>
    cipher -> ba -> ba
    forall ba. ByteArray ba => aes -> ba -> ba
    ecbEncrypt aes
    cipher (ba -> ba -> ba
    forall bs. ByteArray bs => bs -> bs -> bs
    B.append ba
    msb ba
    lsb)

    aes :: (BlockCipher aes, ByteArray ba) => aes -> Pair ba -> Pair ba
    aes :: forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> Pair ba -> Pair ba
    aes aes
    cipher = Int -> ba -> (ba, ba)
    forall bs. ByteArray bs => Int -> bs -> (bs, bs)
    B.splitAt Int
    8 (ba -> (ba, ba)) -> ((ba, ba) -> ba) -> (ba, ba) -> (ba, ba)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . aes -> (ba, ba) -> ba
    forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> Pair ba -> ba
    aes' aes
    cipher

    aesrev' :: (BlockCipher aes, ByteArray ba) => aes -> ba -> Pair ba
    aesrev' :: forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> ba -> Pair ba
    aesrev' aes
    cipher = Int -> ba -> (ba, ba)
    forall bs. ByteArray bs => Int -> bs -> (bs, bs)
    B.splitAt Int
    8 (ba -> (ba, ba)) -> (ba -> ba) -> ba -> (ba, ba)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . aes -> ba -> ba
    forall cipher ba.
    (BlockCipher cipher, ByteArray ba) =>
    cipher -> ba -> ba
    forall ba. ByteArray ba => aes -> ba -> ba
    ecbDecrypt aes
    cipher

    aesrev :: (BlockCipher aes, ByteArray ba) => aes -> Pair ba -> Pair ba
    aesrev :: forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> Pair ba -> Pair ba
    aesrev aes
    cipher (ba
    msb, ba
    lsb) = aes -> ba -> (ba, ba)
    forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> ba -> Pair ba
    aesrev' aes
    cipher (ba -> ba -> ba
    forall bs. ByteArray bs => bs -> bs -> bs
    B.append ba
    msb ba
    lsb)

    wrapc :: (BlockCipher aes, ByteArray ba)
          => aes -> ba -> Chunked ba -> Chunked ba
    wrapc :: forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> ba -> Chunked ba -> Chunked ba
    wrapc aes
    cipher ba
    iiv Chunked ba
    list = (ba -> Chunked ba -> Chunked ba) -> (ba, Chunked ba) -> Chunked ba
    forall a b c. (a -> b -> c) -> (a, b) -> c
    uncurry (:) ((ba, Chunked ba) -> Chunked ba) -> (ba, Chunked ba) -> Chunked ba
    forall a b. (a -> b) -> a -> b
    $ ((ba, Chunked ba) -> Word64 -> (ba, Chunked ba))
    -> (ba, Chunked ba) -> [Word64] -> (ba, Chunked ba)
    forall b a. (b -> a -> b) -> b -> [a] -> b
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl' (ba, Chunked ba) -> Word64 -> (ba, Chunked ba)
    forall {t}. ByteArray t => (t, [t]) -> Word64 -> (t, [t])
    pass (ba
    iiv, Chunked ba
    list) [Word64
    0 .. Word64
    5]
      where
        !n :: Word64
    n = Int -> Word64
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Chunked ba -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length Chunked ba
    list)
        pass :: (t, [t]) -> Word64 -> (t, [t])
    pass (t
    a, [t]
    l) Word64
    j = t -> Word64 -> [t] -> (t, [t])
    forall {t}. ByteArray t => t -> Word64 -> [t] -> (t, [t])
    go t
    a (Word64
    n Word64 -> Word64 -> Word64
    forall a. Num a => a -> a -> a
    * Word64
    j Word64 -> Word64 -> Word64
    forall a. Num a => a -> a -> a
    + Word64
    1) [t]
    l
        go :: t -> Word64 -> [t] -> (t, [t])
    go t
    a !Word64
    _ [] = (t
    a, [])
        go t
    a !Word64
    i (t
    r : [t]
    rs) =
            let (t
    a', t
    t) = aes -> (t, t) -> (t, t)
    forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> Pair ba -> Pair ba
    aes aes
    cipher (t
    a, t
    r)
             in (t
    t t -> [t] -> [t]
    forall a. a -> [a] -> [a]
    :) ([t] -> [t]) -> (t, [t]) -> (t, [t])
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> t -> Word64 -> [t] -> (t, [t])
    go (t -> Word64 -> t
    forall bin bout.
    (ByteArrayAccess bin, ByteArray bout) =>
    bin -> Word64 -> bout
    xorWith t
    a' Word64
    i) (Word64 -> Word64
    forall a. Enum a => a -> a
    succ Word64
    i) [t]
    rs

    unwrapc :: (BlockCipher aes, ByteArray ba)
            => aes -> Chunked ba -> Either StoreError (ba, Chunked ba)
    unwrapc :: forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> Chunked ba -> Either StoreError (ba, Chunked ba)
    unwrapc aes
    _      []         = StoreError -> Either StoreError (ba, [ba])
    forall a b. a -> Either a b
    Left (String -> StoreError
    InvalidInput String
    "KeyWrap.AES: input too short")
    unwrapc aes
    cipher (ba
    iv:[ba]
    list)  = (ba, [ba]) -> Either StoreError (ba, [ba])
    forall a b. b -> Either a b
    Right (ba
    iiv, [ba] -> [ba]
    forall a. [a] -> [a]
    reverse [ba]
    out)
      where
        (ba
    iiv, [ba]
    out) = ((ba, [ba]) -> Word64 -> (ba, [ba]))
    -> (ba, [ba]) -> [Word64] -> (ba, [ba])
    forall b a. (b -> a -> b) -> b -> [a] -> b
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl' (ba, [ba]) -> Word64 -> (ba, [ba])
    forall {t}. ByteArray t => (t, [t]) -> Word64 -> (t, [t])
    pass (ba
    iv, [ba] -> [ba]
    forall a. [a] -> [a]
    reverse [ba]
    list) ([Word64] -> [Word64]
    forall a. [a] -> [a]
    reverse [Word64
    0 .. Word64
    5])
        !n :: Word64
    n = Int -> Word64
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral ([ba] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [ba]
    list)
        pass :: (t, [t]) -> Word64 -> (t, [t])
    pass (t
    a, [t]
    l) Word64
    j = t -> Word64 -> [t] -> (t, [t])
    forall {t}. ByteArray t => t -> Word64 -> [t] -> (t, [t])
    go t
    a (Word64
    n Word64 -> Word64 -> Word64
    forall a. Num a => a -> a -> a
    * Word64
    j Word64 -> Word64 -> Word64
    forall a. Num a => a -> a -> a
    + Word64
    n) [t]
    l
        go :: t -> Word64 -> [t] -> (t, [t])
    go t
    a !Word64
    _ [] = (t
    a, [])
        go t
    a !Word64
    i (t
    r : [t]
    rs) =
            let (t
    a', t
    t) = aes -> (t, t) -> (t, t)
    forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> Pair ba -> Pair ba
    aesrev aes
    cipher (t -> Word64 -> t
    forall bin bout.
    (ByteArrayAccess bin, ByteArray bout) =>
    bin -> Word64 -> bout
    xorWith t
    a Word64
    i, t
    r)
             in (t
    t t -> [t] -> [t]
    forall a. a -> [a] -> [a]
    :) ([t] -> [t]) -> (t, [t]) -> (t, [t])
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> t -> Word64 -> [t] -> (t, [t])
    go t
    a' (Word64 -> Word64
    forall a. Enum a => a -> a
    pred Word64
    i) [t]
    rs

    -- | Wrap a key with the specified AES cipher.
    wrap :: (BlockCipher aes, ByteArray ba) => aes -> ba -> Either StoreError ba
    wrap :: forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> ba -> Either StoreError ba
    wrap aes
    cipher ba
    bs = Chunked ba -> ba
    forall ba. ByteArray ba => Chunked ba -> ba
    unchunks (Chunked ba -> ba)
    -> (Chunked ba -> Chunked ba) -> Chunked ba -> ba
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . aes -> ba -> Chunked ba -> Chunked ba
    forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> ba -> Chunked ba -> Chunked ba
    wrapc aes
    cipher ba
    iiv (Chunked ba -> ba)
    -> Either StoreError (Chunked ba) -> Either StoreError ba
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> ba -> Either StoreError (Chunked ba)
    forall ba. ByteArray ba => ba -> Either StoreError (Chunked ba)
    chunks ba
    bs
      where iiv :: ba
    iiv = Int -> Word8 -> ba
    forall ba. ByteArray ba => Int -> Word8 -> ba
    B.replicate Int
    8 Word8
    0xA6

    -- | Unwrap an encrypted key with the specified AES cipher.
    unwrap :: (BlockCipher aes, ByteArray ba) => aes -> ba -> Either StoreError ba
    unwrap :: forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> ba -> Either StoreError ba
    unwrap aes
    cipher ba
    bs = Chunked ba -> ba
    forall ba. ByteArray ba => Chunked ba -> ba
    unchunks (Chunked ba -> ba)
    -> Either StoreError (Chunked ba) -> Either StoreError ba
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> ((ba, Chunked ba) -> Either StoreError (Chunked ba)
    forall {ba} {b}.
    ByteArrayAccess ba =>
    (ba, b) -> Either StoreError b
    check ((ba, Chunked ba) -> Either StoreError (Chunked ba))
    -> Either StoreError (ba, Chunked ba)
    -> Either StoreError (Chunked ba)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< aes -> Chunked ba -> Either StoreError (ba, Chunked ba)
    forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> Chunked ba -> Either StoreError (ba, Chunked ba)
    unwrapc aes
    cipher (Chunked ba -> Either StoreError (ba, Chunked ba))
    -> Either StoreError (Chunked ba)
    -> Either StoreError (ba, Chunked ba)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< ba -> Either StoreError (Chunked ba)
    forall ba. ByteArray ba => ba -> Either StoreError (Chunked ba)
    chunks ba
    bs)
      where
        check :: (ba, b) -> Either StoreError b
    check (ba
    iiv, b
    out)
            | Word8 -> ba -> Bool
    forall ba. ByteArrayAccess ba => Word8 -> ba -> Bool
    constAllEq Word8
    0xA6 ba
    iiv = b -> Either StoreError b
    forall a b. b -> Either a b
    Right b
    out
            | Bool
    otherwise           = StoreError -> Either StoreError b
    forall a b. a -> Either a b
    Left StoreError
    BadChecksum

    chunks :: ByteArray ba => ba -> Either StoreError (Chunked ba)
    chunks :: forall ba. ByteArray ba => ba -> Either StoreError (Chunked ba)
    chunks ba
    bs | ba -> Bool
    forall a. ByteArrayAccess a => a -> Bool
    B.null ba
    bs       = Chunked ba -> Either StoreError (Chunked ba)
    forall a b. b -> Either a b
    Right []
              | ba -> Int
    forall ba. ByteArrayAccess ba => ba -> Int
    B.length ba
    bs Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    < Int
    8 = StoreError -> Either StoreError (Chunked ba)
    forall a b. a -> Either a b
    Left (String -> StoreError
    InvalidInput String
    "KeyWrap.AES: input is not multiple of 8 bytes")
              | Bool
    otherwise       = let (ba
    a, ba
    b) = Int -> ba -> (ba, ba)
    forall bs. ByteArray bs => Int -> bs -> (bs, bs)
    B.splitAt Int
    8 ba
    bs in (ba
    a ba -> Chunked ba -> Chunked ba
    forall a. a -> [a] -> [a]
    :) (Chunked ba -> Chunked ba)
    -> Either StoreError (Chunked ba) -> Either StoreError (Chunked ba)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> ba -> Either StoreError (Chunked ba)
    forall ba. ByteArray ba => ba -> Either StoreError (Chunked ba)
    chunks ba
    b

    unchunks :: ByteArray ba => Chunked ba -> ba
    unchunks :: forall ba. ByteArray ba => Chunked ba -> ba
    unchunks = [ba] -> ba
    forall bin bout.
    (ByteArrayAccess bin, ByteArray bout) =>
    [bin] -> bout
    B.concat

    padMask :: Bytes
    padMask :: Bytes
    padMask = [Word8] -> Bytes
    forall a. ByteArray a => [Word8] -> a
    B.pack [Word8
    0xA6, Word8
    0x59, Word8
    0x59, Word8
    0xA6, Word8
    0x00, Word8
    0x00, Word8
    0x00, Word8
    0x00]

    pad :: ByteArray ba => Int -> ba -> Either StoreError (Pair ba)
    pad :: forall ba. ByteArray ba => Int -> ba -> Either StoreError (Pair ba)
    pad Int
    inlen ba
    bs | Int
    inlen  Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    0 = StoreError -> Either StoreError (Pair ba)
    forall a b. a -> Either a b
    Left (String -> StoreError
    InvalidInput String
    "KeyWrap.AES: input is empty")
                 | Int
    padlen Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    8 = Pair ba -> Either StoreError (Pair ba)
    forall a b. b -> Either a b
    Right (ba
    aiv, ba
    bs)
                 | Bool
    otherwise   = Pair ba -> Either StoreError (Pair ba)
    forall a b. b -> Either a b
    Right (ba
    aiv, ba
    bs ba -> ba -> ba
    forall bs. ByteArray bs => bs -> bs -> bs
    `B.append` Int -> ba
    forall ba. ByteArray ba => Int -> ba
    B.zero Int
    padlen)
      where padlen :: Int
    padlen = Int
    8 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int -> Int -> Int
    forall a. Integral a => a -> a -> a
    mod Int
    inlen Int
    8
            aiv :: ba
    aiv    = Bytes -> Word64 -> ba
    forall bin bout.
    (ByteArrayAccess bin, ByteArray bout) =>
    bin -> Word64 -> bout
    xorWith Bytes
    padMask (Int -> Word64
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    inlen)

    unpad :: ByteArray ba => Int -> Pair ba -> Either StoreError ba
    unpad :: forall ba. ByteArray ba => Int -> Pair ba -> Either StoreError ba
    unpad Int
    inlen (ba
    aiv, ba
    b)
        | Bool
    badlen         = StoreError -> Either StoreError ba
    forall a b. a -> Either a b
    Left StoreError
    BadChecksum
        | Word8 -> ba -> Bool
    forall ba. ByteArrayAccess ba => Word8 -> ba -> Bool
    constAllEq Word8
    0 ba
    p = ba -> Either StoreError ba
    forall a b. b -> Either a b
    Right ba
    bs
        | Bool
    otherwise      = StoreError -> Either StoreError ba
    forall a b. a -> Either a b
    Left StoreError
    BadChecksum
      where aivlen :: Int
    aivlen = Word64 -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Bytes -> ba -> Word64
    forall bx by.
    (ByteArrayAccess bx, ByteArrayAccess by) =>
    bx -> by -> Word64
    unxor Bytes
    padMask ba
    aiv)
            badlen :: Bool
    badlen = Int
    inlen Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    < Int
    aivlen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    8 Bool -> Bool -> Bool
    || Int
    inlen Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Int
    aivlen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    16
            (ba
    bs, ba
    p) = Int -> ba -> (ba, ba)
    forall bs. ByteArray bs => Int -> bs -> (bs, bs)
    B.splitAt Int
    aivlen ba
    b

    -- | Pad and wrap a key with the specified AES cipher.
    wrapPad :: (BlockCipher aes, ByteArray ba) => aes -> ba -> Either StoreError ba
    wrapPad :: forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> ba -> Either StoreError ba
    wrapPad aes
    cipher ba
    bs = (ba, ba) -> Either StoreError ba
    forall {b}. ByteArray b => (b, b) -> Either StoreError b
    doWrap ((ba, ba) -> Either StoreError ba)
    -> Either StoreError (ba, ba) -> Either StoreError ba
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< Int -> ba -> Either StoreError (ba, ba)
    forall ba. ByteArray ba => Int -> ba -> Either StoreError (Pair ba)
    pad Int
    inlen ba
    bs
      where
        inlen :: Int
    inlen = ba -> Int
    forall ba. ByteArrayAccess ba => ba -> Int
    B.length ba
    bs
        doWrap :: (b, b) -> Either StoreError b
    doWrap (b
    aiv, b
    b)
            | Int
    inlen Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    8 = b -> Either StoreError b
    forall a b. b -> Either a b
    Right (b -> Either StoreError b) -> b -> Either StoreError b
    forall a b. (a -> b) -> a -> b
    $ aes -> (b, b) -> b
    forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> Pair ba -> ba
    aes' aes
    cipher (b
    aiv, b
    b)
            | Bool
    otherwise  = Chunked b -> b
    forall ba. ByteArray ba => Chunked ba -> ba
    unchunks (Chunked b -> b) -> (Chunked b -> Chunked b) -> Chunked b -> b
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . aes -> b -> Chunked b -> Chunked b
    forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> ba -> Chunked ba -> Chunked ba
    wrapc aes
    cipher b
    aiv (Chunked b -> b)
    -> Either StoreError (Chunked b) -> Either StoreError b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> b -> Either StoreError (Chunked b)
    forall ba. ByteArray ba => ba -> Either StoreError (Chunked ba)
    chunks b
    b

    -- | Unwrap and unpad an encrypted key with the specified AES cipher.
    unwrapPad :: (BlockCipher aes, ByteArray ba) => aes -> ba -> Either StoreError ba
    unwrapPad :: forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> ba -> Either StoreError ba
    unwrapPad aes
    cipher ba
    bs = Int -> Pair ba -> Either StoreError ba
    forall ba. ByteArray ba => Int -> Pair ba -> Either StoreError ba
    unpad Int
    inlen (Pair ba -> Either StoreError ba)
    -> Either StoreError (Pair ba) -> Either StoreError ba
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< Either StoreError (Pair ba)
    doUnwrap
      where
        inlen :: Int
    inlen = ba -> Int
    forall ba. ByteArrayAccess ba => ba -> Int
    B.length ba
    bs
        doUnwrap :: Either StoreError (Pair ba)
    doUnwrap
            | Int
    inlen Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    16 = let (ba
    aiv, ba
    b) = aes -> ba -> Pair ba
    forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> ba -> Pair ba
    aesrev' aes
    cipher ba
    bs in Pair ba -> Either StoreError (Pair ba)
    forall a b. b -> Either a b
    Right (ba
    aiv, ba
    b)
            | Bool
    otherwise   = (Chunked ba -> ba) -> (ba, Chunked ba) -> Pair ba
    forall a b. (a -> b) -> (ba, a) -> (ba, b)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Chunked ba -> ba
    forall ba. ByteArray ba => Chunked ba -> ba
    unchunks ((ba, Chunked ba) -> Pair ba)
    -> Either StoreError (ba, Chunked ba)
    -> Either StoreError (Pair ba)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> (aes -> Chunked ba -> Either StoreError (ba, Chunked ba)
    forall aes ba.
    (BlockCipher aes, ByteArray ba) =>
    aes -> Chunked ba -> Either StoreError (ba, Chunked ba)
    unwrapc aes
    cipher (Chunked ba -> Either StoreError (ba, Chunked ba))
    -> Either StoreError (Chunked ba)
    -> Either StoreError (ba, Chunked ba)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< ba -> Either StoreError (Chunked ba)
    forall ba. ByteArray ba => ba -> Either StoreError (Chunked ba)
    chunks ba
    bs)

    xorWith :: (ByteArrayAccess bin, ByteArray bout) => bin -> Word64 -> bout
    xorWith :: forall bin bout.
    (ByteArrayAccess bin, ByteArray bout) =>
    bin -> Word64 -> bout
    xorWith bin
    bs !Word64
    i = bin -> (Ptr Any -> IO ()) -> bout
    forall bs1 bs2 p.
    (ByteArrayAccess bs1, ByteArray bs2) =>
    bs1 -> (Ptr p -> IO ()) -> bs2
    B.copyAndFreeze bin
    bs ((Ptr Any -> IO ()) -> bout) -> (Ptr Any -> IO ()) -> bout
    forall a b. (a -> b) -> a -> b
    $ \Ptr Any
    dst -> Ptr Any -> Int -> Word64 -> IO ()
    forall {t} {b}. (Bits t, Integral t) => Ptr b -> Int -> t -> IO ()
    loop Ptr Any
    dst Int
    len Word64
    i
      where !len :: Int
    len = bin -> Int
    forall ba. ByteArrayAccess ba => ba -> Int
    B.length bin
    bs
            loop :: Ptr b -> Int -> t -> IO ()
    loop Ptr b
    _ Int
    0 !t
    _ = () -> IO ()
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
            loop Ptr b
    _ Int
    _ t
    0  = () -> IO ()
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return () -- return early (constant-time not needed)
            loop Ptr b
    p Int
    n t
    j  = do
                Word8
    b <- Ptr b -> Int -> IO Word8
    forall b. Ptr b -> Int -> IO Word8
    forall a b. Storable a => Ptr b -> Int -> IO a
    peekByteOff Ptr b
    p (Int
    n Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1)
                let mask :: Word8
    mask = t -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral t
    j :: Word8
                Ptr b -> Int -> Word8 -> IO ()
    forall b. Ptr b -> Int -> Word8 -> IO ()
    forall a b. Storable a => Ptr b -> Int -> a -> IO ()
    pokeByteOff Ptr b
    p (Int
    n Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1) (Word8 -> Word8 -> Word8
    forall a. Bits a => a -> a -> a
    xor Word8
    b Word8
    mask)
                Ptr b -> Int -> t -> IO ()
    loop Ptr b
    p (Int
    n Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1) (t -> Int -> t
    forall a. Bits a => a -> Int -> a
    shiftR t
    j Int
    8)

    unxor :: (ByteArrayAccess bx, ByteArrayAccess by) => bx -> by -> Word64
    unxor :: forall bx by.
    (ByteArrayAccess bx, ByteArrayAccess by) =>
    bx -> by -> Word64
    unxor bx
    x by
    y = (Word64 -> Word8 -> Word64) -> Word64 -> [Word8] -> Word64
    forall b a. (b -> a -> b) -> b -> [a] -> b
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl' Word64 -> Word8 -> Word64
    forall {a} {a}. (Bits a, Integral a, Num a) => a -> a -> a
    f Word64
    0 ([Word8] -> Word64) -> [Word8] -> Word64
    forall a b. (a -> b) -> a -> b
    $ (Word8 -> Word8 -> Word8) -> [Word8] -> [Word8] -> [Word8]
    forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
    zipWith Word8 -> Word8 -> Word8
    forall a. Bits a => a -> a -> a
    xor (bx -> [Word8]
    forall a. ByteArrayAccess a => a -> [Word8]
    B.unpack bx
    x) (by -> [Word8]
    forall a. ByteArrayAccess a => a -> [Word8]
    B.unpack by
    y)
      where f :: a -> a -> a
    f a
    acc a
    z = a -> Int -> a
    forall a. Bits a => a -> Int -> a
    shiftL a
    acc Int
    8 a -> a -> a
    forall a. Num a => a -> a -> a
    + a -> a
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral a
    z