-- | Implement the encryption scheme used by SDMF.
module Tahoe.SDMF.Internal.Encrypting where

import Crypto.Cipher.AES (AES128)
import Crypto.Cipher.Types (BlockCipher (blockSize), ctrCombine, makeIV, nullIV)
import Crypto.Random (MonadRandom (getRandomBytes))
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as LB
import qualified Tahoe.SDMF.Internal.Keys as Keys

-- | Randomly generate a new IV suitable for use with the block cipher used by SDMF.
randomIV :: MonadRandom m => m (Maybe Keys.SDMF_IV)
randomIV :: m (Maybe SDMF_IV)
randomIV = ((IV AES128 -> SDMF_IV) -> Maybe (IV AES128) -> Maybe SDMF_IV
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap IV AES128 -> SDMF_IV
Keys.SDMF_IV (Maybe (IV AES128) -> Maybe SDMF_IV)
-> (ByteString -> Maybe (IV AES128)) -> ByteString -> Maybe SDMF_IV
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Maybe (IV AES128)
forall b c. (ByteArrayAccess b, BlockCipher c) => b -> Maybe (IV c)
makeIV :: B.ByteString -> Maybe Keys.SDMF_IV) (ByteString -> Maybe SDMF_IV) -> m ByteString -> m (Maybe SDMF_IV)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> m ByteString
forall (m :: * -> *) byteArray.
(MonadRandom m, ByteArray byteArray) =>
Int -> m byteArray
getRandomBytes (AES128 -> Int
forall cipher. BlockCipher cipher => cipher -> Int
blockSize (AES128
forall a. HasCallStack => a
undefined :: AES128))

{- | Encrypt plaintext bytes according to the scheme used for SDMF share
 construction.
-}
encrypt :: Keys.KeyPair -> Keys.SDMF_IV -> LB.ByteString -> LB.ByteString
encrypt :: KeyPair -> SDMF_IV -> ByteString -> ByteString
encrypt KeyPair
keypair SDMF_IV
iv = Data -> ByteString -> ByteString
encryptWithDataKey Data
dataKey
  where
    signatureKey :: Signature
signatureKey = KeyPair -> Signature
Keys.toSignatureKey KeyPair
keypair
    (Just Write
writeKey) = Signature -> Maybe Write
Keys.deriveWriteKey Signature
signatureKey
    (Just Read
readKey) = Write -> Maybe Read
Keys.deriveReadKey Write
writeKey
    (Just Data
dataKey) = SDMF_IV -> Read -> Maybe Data
Keys.deriveDataKey SDMF_IV
iv Read
readKey

{- | Decrypt ciphertext bytes according to the scheme used for SDMF share
 construction.
-}
decrypt :: Keys.Read -> Keys.SDMF_IV -> LB.ByteString -> LB.ByteString
decrypt :: Read -> SDMF_IV -> ByteString -> ByteString
decrypt Read
readKey SDMF_IV
iv = Data -> ByteString -> ByteString
decryptWithDataKey Data
dataKey
  where
    (Just Data
dataKey) = SDMF_IV -> Read -> Maybe Data
Keys.deriveDataKey SDMF_IV
iv Read
readKey

{- | Encrypt plaintext bytes according to the scheme used for SDMF share
 construction using a pre-computed data encryption key.
-}
encryptWithDataKey :: Keys.Data -> LB.ByteString -> LB.ByteString
encryptWithDataKey :: Data -> ByteString -> ByteString
encryptWithDataKey Keys.Data{AES128
unData :: Data -> AES128
unData :: AES128
unData} = ByteString -> ByteString
LB.fromStrict (ByteString -> ByteString)
-> (ByteString -> ByteString) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AES128 -> IV AES128 -> ByteString -> ByteString
forall cipher ba.
(BlockCipher cipher, ByteArray ba) =>
cipher -> IV cipher -> ba -> ba
ctrCombine AES128
unData IV AES128
forall c. BlockCipher c => IV c
nullIV (ByteString -> ByteString)
-> (ByteString -> ByteString) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
LB.toStrict

{- | Decrypt ciphertext bytes according to the scheme used for SDMF share
 construction using a pre-computed data encryption key.
-}
decryptWithDataKey :: Keys.Data -> LB.ByteString -> LB.ByteString
decryptWithDataKey :: Data -> ByteString -> ByteString
decryptWithDataKey = Data -> ByteString -> ByteString
encryptWithDataKey