    -- |
    -- Module      : Crypto.Store.KeyWrap.RC2
    -- License     : BSD-style
    -- Maintainer  : Olivier Chéron <olivier.cheron@gmail.com>
    -- Stability   : experimental
    -- Portability : unknown
    --
    -- RC2 Key Wrap (<https://tools.ietf.org/html/rfc3217 RFC 3217>)
    --
    -- Should be used with a cipher from module "Crypto.Store.Cipher.RC2".
    module Crypto.Store.KeyWrap.RC2
        ( wrap
        , wrap'
        , unwrap
        ) where

    import           Data.ByteArray (ByteArray)
    import qualified Data.ByteArray as B

    import Crypto.Cipher.Types
    import Crypto.Hash
    import Crypto.Random

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

    checksum :: ByteArray ba => ba -> ba
    checksum :: forall ba. ByteArray ba => ba -> ba
    checksum ba
    bs = View (Digest SHA1) -> ba
    forall bin bout.
    (ByteArrayAccess bin, ByteArray bout) =>
    bin -> bout
    B.convert (View (Digest SHA1) -> ba) -> View (Digest SHA1) -> ba
    forall a b. (a -> b) -> a -> b
    $ Digest SHA1 -> Int -> View (Digest SHA1)
    forall bytes. ByteArrayAccess bytes => bytes -> Int -> View bytes
    B.takeView (SHA1 -> ba -> Digest SHA1
    forall ba alg.
    (ByteArrayAccess ba, HashAlgorithm alg) =>
    alg -> ba -> Digest alg
    hashWith SHA1
    SHA1 ba
    bs) Int
    8

    iv4adda22c79e82105 :: B.Bytes
    iv4adda22c79e82105 :: Bytes
    iv4adda22c79e82105 = [Word8] -> Bytes
    forall a. ByteArray a => [Word8] -> a
    B.pack [Word8
    0x4a, Word8
    0xdd, Word8
    0xa2, Word8
    0x2c, Word8
    0x79, Word8
    0xe8, Word8
    0x21, Word8
    0x05]

    -- | Wrap an RC2 key with the specified RC2 cipher.
    --
    -- Input must be between 0 and 255 bytes.  A fresh IV should be generated
    -- randomly for each invocation.
    wrap :: (MonadRandom m, BlockCipher cipher, ByteArray ba)
         => cipher -> IV cipher -> ba -> m (Either StoreError ba)
    wrap :: forall (m :: * -> *) cipher ba.
    (MonadRandom m, BlockCipher cipher, ByteArray ba) =>
    cipher -> IV cipher -> ba -> m (Either StoreError ba)
    wrap = (StoreError -> m (Either StoreError ba))
    -> ((ba -> ba) -> Int -> m (Either StoreError ba))
    -> cipher
    -> IV cipher
    -> ba
    -> m (Either StoreError ba)
    forall ba cipher result.
    (ByteArray ba, BlockCipher cipher) =>
    (StoreError -> result)
    -> ((ba -> ba) -> Int -> result)
    -> cipher
    -> IV cipher
    -> ba
    -> result
    wrap' (Either StoreError ba -> m (Either StoreError ba)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either StoreError ba -> m (Either StoreError ba))
    -> (StoreError -> Either StoreError ba)
    -> StoreError
    -> m (Either StoreError ba)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . StoreError -> Either StoreError ba
    forall a b. a -> Either a b
    Left) (ba -> ba) -> Int -> m (Either StoreError ba)
    forall {f :: * -> *} {a} {b} {a}.
    (MonadRandom f, ByteArray a) =>
    (a -> b) -> Int -> f (Either a b)
    randomPad
      where randomPad :: (a -> b) -> Int -> f (Either a b)
    randomPad a -> b
    f = (a -> Either a b) -> f a -> f (Either a b)
    forall a b. (a -> b) -> f a -> f b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (b -> Either a b
    forall a b. b -> Either a b
    Right (b -> Either a b) -> (a -> b) -> a -> Either a b
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . a -> b
    f) (f a -> f (Either a b)) -> (Int -> f a) -> Int -> f (Either a b)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Int -> f a
    forall byteArray. ByteArray byteArray => Int -> f byteArray
    forall (m :: * -> *) byteArray.
    (MonadRandom m, ByteArray byteArray) =>
    Int -> m byteArray
    getRandomBytes

    -- | Wrap an RC2 key with the specified RC2 cipher, using the given source of
    -- random padding data.
    --
    -- Input must be between 0 and 255 bytes.  A fresh IV should be generated
    -- randomly for each invocation.
    wrap' :: (ByteArray ba, BlockCipher cipher)
          => (StoreError -> result) -> ((ba -> ba) -> Int -> result)
          -> cipher -> IV cipher -> ba -> result
    wrap' :: forall ba cipher result.
    (ByteArray ba, BlockCipher cipher) =>
    (StoreError -> result)
    -> ((ba -> ba) -> Int -> result)
    -> cipher
    -> IV cipher
    -> ba
    -> result
    wrap' StoreError -> result
    failure (ba -> ba) -> Int -> result
    withRandomPad cipher
    cipher IV cipher
    iv ba
    cek
        | Int
    inLen Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    < Int
    256 = (ba -> ba) -> Int -> result
    withRandomPad ba -> ba
    f Int
    padlen
        | Bool
    otherwise   = StoreError -> result
    failure
            (String -> StoreError
    InvalidInput String
    "KeyWrap.RC2: invalid length for content encryption key")
      where
        inLen :: Int
    inLen      = ba -> Int
    forall ba. ByteArrayAccess ba => ba -> Int
    B.length ba
    cek
        padlen :: Int
    padlen     = (Int
    7 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    inLen) Int -> Int -> Int
    forall a. Integral a => a -> a -> a
    `mod` Int
    8

        f :: ba -> ba
    f ba
    pad =
            let lcek :: ba
    lcek       = Word8 -> ba -> ba
    forall a. ByteArray a => Word8 -> a -> a
    B.cons (Int -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    inLen) ba
    cek
                lcekpad :: ba
    lcekpad    = ba -> ba -> ba
    forall bs. ByteArray bs => bs -> bs -> bs
    B.append ba
    lcek ba
    pad
                lcekpadicv :: ba
    lcekpadicv = ba -> ba -> ba
    forall bs. ByteArray bs => bs -> bs -> bs
    B.append ba
    lcekpad (ba -> ba
    forall ba. ByteArray ba => ba -> ba
    checksum ba
    lcekpad)
                temp1 :: ba
    temp1      = cipher -> IV cipher -> ba -> ba
    forall cipher ba.
    (BlockCipher cipher, ByteArray ba) =>
    cipher -> IV cipher -> ba -> ba
    forall ba. ByteArray ba => cipher -> IV cipher -> ba -> ba
    cbcEncrypt cipher
    cipher IV cipher
    iv ba
    lcekpadicv
                temp2 :: ba
    temp2      = ba -> ba -> ba
    forall bs. ByteArray bs => bs -> bs -> bs
    B.append (IV cipher -> ba
    forall bin bout.
    (ByteArrayAccess bin, ByteArray bout) =>
    bin -> bout
    B.convert IV cipher
    iv) ba
    temp1
                temp3 :: ba
    temp3      = ba -> ba
    forall ba. ByteArray ba => ba -> ba
    reverseBytes ba
    temp2
                Just IV cipher
    iv'   = Bytes -> Maybe (IV cipher)
    forall b c. (ByteArrayAccess b, BlockCipher c) => b -> Maybe (IV c)
    makeIV Bytes
    iv4adda22c79e82105
             in cipher -> IV cipher -> ba -> ba
    forall cipher ba.
    (BlockCipher cipher, ByteArray ba) =>
    cipher -> IV cipher -> ba -> ba
    forall ba. ByteArray ba => cipher -> IV cipher -> ba -> ba
    cbcEncrypt cipher
    cipher IV cipher
    iv' ba
    temp3

    -- | Unwrap an encrypted RC2 key with the specified RC2 cipher.
    unwrap :: (BlockCipher cipher, ByteArray ba)
           => cipher -> ba -> Either StoreError ba
    unwrap :: forall cipher ba.
    (BlockCipher cipher, ByteArray ba) =>
    cipher -> ba -> Either StoreError ba
    unwrap cipher
    cipher ba
    wrapped
        | Int
    inLen Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    16        = Either StoreError ba
    forall {b}. Either StoreError b
    invalid
        | Int
    inLen Int -> Int -> Int
    forall a. Integral a => a -> a -> a
    `mod` Int
    8 Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Int
    0 = Either StoreError ba
    forall {b}. Either StoreError b
    invalid
        | Bool
    checksumPadValid   = ba -> Either StoreError ba
    forall a b. b -> Either a b
    Right ba
    cek
        | Bool
    otherwise          = Either StoreError ba
    forall {b}. Either StoreError b
    invalid
      where
        inLen :: Int
    inLen            = ba -> Int
    forall ba. ByteArrayAccess ba => ba -> Int
    B.length ba
    wrapped
        Just IV cipher
    iv'         = Bytes -> Maybe (IV cipher)
    forall b c. (ByteArrayAccess b, BlockCipher c) => b -> Maybe (IV c)
    makeIV Bytes
    iv4adda22c79e82105
        temp3 :: ba
    temp3            = cipher -> IV cipher -> ba -> ba
    forall cipher ba.
    (BlockCipher cipher, ByteArray ba) =>
    cipher -> IV cipher -> ba -> ba
    forall ba. ByteArray ba => cipher -> IV cipher -> ba -> ba
    cbcDecrypt cipher
    cipher IV cipher
    iv' ba
    wrapped
        temp2 :: ba
    temp2            = ba -> ba
    forall ba. ByteArray ba => ba -> ba
    reverseBytes ba
    temp3
        (ba
    ivBs, ba
    temp1)    = Int -> ba -> (ba, ba)
    forall bs. ByteArray bs => Int -> bs -> (bs, bs)
    B.splitAt Int
    8 ba
    temp2
        Just IV cipher
    iv          = ba -> Maybe (IV cipher)
    forall b c. (ByteArrayAccess b, BlockCipher c) => b -> Maybe (IV c)
    makeIV ba
    ivBs
        lcekpadicv :: ba
    lcekpadicv       = cipher -> IV cipher -> ba -> ba
    forall cipher ba.
    (BlockCipher cipher, ByteArray ba) =>
    cipher -> IV cipher -> ba -> ba
    forall ba. ByteArray ba => cipher -> IV cipher -> ba -> ba
    cbcDecrypt cipher
    cipher IV cipher
    iv ba
    temp1
        (ba
    lcekpad, ba
    icv)   = Int -> ba -> (ba, ba)
    forall bs. ByteArray bs => Int -> bs -> (bs, bs)
    B.splitAt (Int
    inLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    16) ba
    lcekpadicv
        Just (Word8
    l, ba
    cekpad) = ba -> Maybe (Word8, ba)
    forall a. ByteArray a => a -> Maybe (Word8, a)
    B.uncons ba
    lcekpad
        len :: Int
    len              = Word8 -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Word8
    l
        padlen :: Int
    padlen           = Int
    inLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    16 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    len Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1
        cek :: ba
    cek              = Int -> ba -> ba
    forall bs. ByteArray bs => Int -> bs -> bs
    B.take Int
    len ba
    cekpad
        invalid :: Either StoreError b
    invalid          = StoreError -> Either StoreError b
    forall a b. a -> Either a b
    Left StoreError
    BadChecksum
        checksumPadValid :: Bool
    checksumPadValid = ba -> ba -> Bool
    forall bs1 bs2.
    (ByteArrayAccess bs1, ByteArrayAccess bs2) =>
    bs1 -> bs2 -> Bool
    B.constEq ba
    icv (ba -> ba
    forall ba. ByteArray ba => ba -> ba
    checksum ba
    lcekpad)
                               Bool -> Bool -> Bool
    &&! Int
    padlen Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    >=Int
    0 Bool -> Bool -> Bool
    &&! Int
    padlen Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    < Int
    8