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

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

    import Crypto.Cipher.Types
    import Crypto.Hash

    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 a Triple-DES key with the specified Triple-DES cipher.
    --
    -- Input must be 24 bytes.  A fresh IV should be generated randomly for each
    -- invocation.
    wrap :: (BlockCipher cipher, ByteArray ba)
         => cipher -> IV cipher -> ba -> Either StoreError ba
    wrap :: forall cipher ba.
    (BlockCipher cipher, ByteArray ba) =>
    cipher -> IV cipher -> ba -> Either StoreError ba
    wrap cipher
    cipher IV cipher
    iv ba
    cek
        | Int
    inLen Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    24 = ba -> Either StoreError ba
    forall a b. b -> Either a b
    Right ba
    wrapped
        | Bool
    otherwise   = StoreError -> Either StoreError ba
    forall a b. a -> Either a b
    Left
            (String -> StoreError
    InvalidInput String
    "KeyWrap.TripleDES: invalid length for content encryption key")
      where
        inLen :: Int
    inLen    = ba -> Int
    forall ba. ByteArrayAccess ba => ba -> Int
    B.length ba
    cek
        Just IV cipher
    iv' = Bytes -> Maybe (IV cipher)
    forall b c. (ByteArrayAccess b, BlockCipher c) => b -> Maybe (IV c)
    makeIV Bytes
    iv4adda22c79e82105
        cekicv :: ba
    cekicv   = ba -> ba -> ba
    forall bs. ByteArray bs => bs -> bs -> bs
    B.append ba
    cek (ba -> ba
    forall ba. ByteArray ba => ba -> ba
    checksum ba
    cek)
        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
    cekicv
        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
        wrapped :: ba
    wrapped  = 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 Triple-DES key with the specified Triple-DES 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. Eq a => a -> a -> Bool
    /= Int
    40                  = Either StoreError ba
    forall {b}. Either StoreError b
    invalid
        | 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
    cek) = 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
        cekicv :: ba
    cekicv        = 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
    cek, ba
    icv)    = Int -> ba -> (ba, ba)
    forall bs. ByteArray bs => Int -> bs -> (bs, bs)
    B.splitAt Int
    24 ba
    cekicv
        invalid :: Either StoreError b
    invalid       = StoreError -> Either StoreError b
    forall a b. a -> Either a b
    Left StoreError
    BadChecksum