-- |
-- 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