-- |
-- 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 = forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
B.convert forall a b. (a -> b) -> a -> b
$ forall bytes. ByteArrayAccess bytes => bytes -> Int -> View bytes
B.takeView (forall ba alg.
(ByteArrayAccess ba, HashAlgorithm alg) =>
alg -> ba -> Digest alg
hashWith SHA1
SHA1 ba
bs) Int
8

iv4adda22c79e82105 :: B.Bytes
iv4adda22c79e82105 :: Bytes
iv4adda22c79e82105 = 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 forall a. Eq a => a -> a -> Bool
== Int
24 = forall a b. b -> Either a b
Right ba
wrapped
    | Bool
otherwise   = forall a b. a -> Either a b
Left
        (String -> StoreError
InvalidInput String
"KeyWrap.TripleDES: invalid length for content encryption key")
  where
    inLen :: Int
inLen    = forall ba. ByteArrayAccess ba => ba -> Int
B.length ba
cek
    Just IV cipher
iv' = forall b c. (ByteArrayAccess b, BlockCipher c) => b -> Maybe (IV c)
makeIV Bytes
iv4adda22c79e82105
    cekicv :: ba
cekicv   = forall bs. ByteArray bs => bs -> bs -> bs
B.append ba
cek (forall ba. ByteArray ba => ba -> ba
checksum ba
cek)
    temp1 :: ba
temp1    = forall cipher ba.
(BlockCipher cipher, ByteArray ba) =>
cipher -> IV cipher -> ba -> ba
cbcEncrypt cipher
cipher IV cipher
iv ba
cekicv
    temp2 :: ba
temp2    = forall bs. ByteArray bs => bs -> bs -> bs
B.append (forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
B.convert IV cipher
iv) ba
temp1
    temp3 :: ba
temp3    = forall ba. ByteArray ba => ba -> ba
reverseBytes ba
temp2
    wrapped :: ba
wrapped  = forall cipher ba.
(BlockCipher cipher, 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 forall a. Eq a => a -> a -> Bool
/= Int
40                  = forall {b}. Either StoreError b
invalid
    | forall bs1 bs2.
(ByteArrayAccess bs1, ByteArrayAccess bs2) =>
bs1 -> bs2 -> Bool
B.constEq ba
icv (forall ba. ByteArray ba => ba -> ba
checksum ba
cek) = forall a b. b -> Either a b
Right ba
cek
    | Bool
otherwise                    = forall {b}. Either StoreError b
invalid
  where
    inLen :: Int
inLen         = forall ba. ByteArrayAccess ba => ba -> Int
B.length ba
wrapped
    Just IV cipher
iv'      = forall b c. (ByteArrayAccess b, BlockCipher c) => b -> Maybe (IV c)
makeIV Bytes
iv4adda22c79e82105
    temp3 :: ba
temp3         = forall cipher ba.
(BlockCipher cipher, ByteArray ba) =>
cipher -> IV cipher -> ba -> ba
cbcDecrypt cipher
cipher IV cipher
iv' ba
wrapped
    temp2 :: ba
temp2         = forall ba. ByteArray ba => ba -> ba
reverseBytes ba
temp3
    (ba
ivBs, ba
temp1) = forall bs. ByteArray bs => Int -> bs -> (bs, bs)
B.splitAt Int
8 ba
temp2
    Just IV cipher
iv       = forall b c. (ByteArrayAccess b, BlockCipher c) => b -> Maybe (IV c)
makeIV ba
ivBs
    cekicv :: ba
cekicv        = forall cipher ba.
(BlockCipher cipher, ByteArray ba) =>
cipher -> IV cipher -> ba -> ba
cbcDecrypt cipher
cipher IV cipher
iv ba
temp1
    (ba
cek, ba
icv)    = forall bs. ByteArray bs => Int -> bs -> (bs, bs)
B.splitAt Int
24 ba
cekicv
    invalid :: Either StoreError b
invalid       = forall a b. a -> Either a b
Left StoreError
BadChecksum