raaz-0.3.0: api/aead/Interface.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE RecordWildCards #-}
-- |
--
-- Module : aead-api: Interface
-- Description : Generic interface to authenticated encryption.
-- Copyright : (c) Piyush P Kurur, 2019
-- License : Apache-2.0 OR BSD-3-Clause
-- Maintainer : Piyush P Kurur <ppk@iitpkd.ac.in>
-- Stability : experimental
--
-- | The interface for an aead construction using a stream cipher like
-- chacha20 and authenticator like poly1305.
module Interface( -- * Locking and unlocking stuff
unsafeLock, unlock
-- ** Additional data.
, Locked, AuthTag, Cipher
, unsafeLockWith, unlockWith
, unsafeToNounce, unsafeToCipherText, unsafeToAuthTag
, unsafeLocked
, AEADMem
, name
, description
) where
import Data.ByteString
import System.IO.Unsafe ( unsafePerformIO )
import Raaz.Core
import Raaz.Primitive.AEAD.Internal
import qualified Cipher.Implementation as CI
import qualified Auth.Implementation as AI
import qualified Cipher.Utils as CU
import qualified Auth.Utils as AU
import qualified Cipher.Buffer as CB
-- | The associated cipher.
type Cipher = CI.Prim
-- | The associated message authenticator.
type AuthTag = AI.Prim
-- | The locked message.
type Locked = AEAD Cipher AuthTag
-- | This function takes the plain text and the additional data, and
-- constructs the associated Locked message. A peer who has the right
-- @(key, nounce)@ pair and the `aad` can recover the unencrypted
-- object using the `unlockWith` function.
unsafeLockWith :: (Encodable plain, Encodable aad)
=> aad -- ^ the authenticated additional data.
-> Key Cipher -- ^ The key for the stream cipher
-> Nounce Cipher -- ^ The nounce used by the stream cipher.
-> plain -- ^ the unencrypted object
-> Locked
unsafeLockWith aad k n plain = unsafePerformIO $ withMemory $ \ mem -> do
initialise k mem
initialise n mem
cText <- encrypt plain mem
AEAD n cText <$> computeAuth aad cText mem
-- | Unlock an encrypted authenticated version of the data given the
-- additional data, key, and nounce. An attempt to unlock the element
-- can result in `Nothing` if either of the following is true.
--
-- 1. The key, nounce pair used to encrypt the data is incorrect.
--
-- 2. The Authenticated additional data (@aad@) is incorrect.
--
-- 3. The Locked message is of the wrong type and hence the
-- `fromByteString` failed.
--
-- 4. The Locked message has been tampered.
--
-- The interface provided above makes it impossible to know which of
-- the above errors occurred. This is a deliberate design as revealing
-- the nature of the failure can leak information to a potential
-- attacker.
--
unlockWith :: (Encodable plain, Encodable aad)
=> aad -- ^ the authenticated additional data.
-> Key Cipher -- ^ The key for the stream cipher
-> Locked
-- ^ The encrypted authenticated version of the data.
-> Maybe plain
unlockWith aad k aead = unsafePerformIO $ withMemory $ \ mem -> do
initialise k mem
initialise (unsafeToNounce aead) mem
isSuccess <- verify aad aead mem
if isSuccess then decrypt aead mem else return Nothing
-- | Generate a locked version of an unencrypted object. You will need
-- the exact same key and nounce to unlock the object.
unsafeLock :: Encodable plain
=> Key Cipher
-> Nounce Cipher
-> plain
-> Locked
unsafeLock = unsafeLockWith ()
-- | Unlock the encrypted packet.
unlock :: Encodable plain
=> Key Cipher
-> Locked
-> Maybe plain
unlock = unlockWith ()
-- | The internal memory used for computing the AEAD packet. When using
-- this memory for packet computation, it is important to initalise the
-- memory in the following order.
--
-- 1. Initialise with key either using the `initialise` function or, by using
-- the `WriteAccessible` instance using the `mem.
-- 2. Initialise the nounce
--
-- We are then all set to go.
--
data AEADMem = AEADMem { cipherInternals :: CI.Internals
, authInternals :: AI.Internals
, internBuffer :: CB.Buffer 1
}
instance Memory AEADMem where
memoryAlloc = AEADMem <$> memoryAlloc <*> memoryAlloc <*> memoryAlloc
unsafeToPointer = unsafeToPointer . cipherInternals
-- | Initialise with the key of the cipher.
instance Initialisable AEADMem (Key Cipher) where
initialise k = initialise k . cipherInternals
instance WriteAccessible AEADMem where
writeAccess = writeAccess . cipherInternals
afterWriteAdjustment = afterWriteAdjustment . cipherInternals
-- | Initialise after the key is already initialised.
instance Initialisable AEADMem (Nounce Cipher) where
initialise n AEADMem{..} = do
initialise n cipherInternals
let zeroCount = 0 `blocksOf` (Proxy :: Proxy Cipher)
in initialise zeroCount cipherInternals
--
-- Generate the key stream
--
CB.memsetBuffer 0 internBuffer -- clear the internal buffer
CU.processBuffer internBuffer cipherInternals -- generate the keystream
--
-- Initialise the authenticator from the keystream.
--
memTransfer (destination authInternals) (source internBuffer)
--------------------- Internal functions ---------------------------------
---
-- These are some of the internal functions that are used by various
-- lock unlock functions. One of the constraints that we want to
-- enforce is that unauthenticated input should never be
-- decrypted. Hence, despite their cute names, these functions should
-- not be exposed to the user from this module
-- | Transform the input bytestring with the cipher.
transform :: ByteString -- The plain text associated with the data
-> AEADMem
-> IO ByteString
transform bs = CU.transform bs . cipherInternals
-- | Compute the authenticator
computeAuth :: Encodable aad
=> aad -- ^ The additional data that needs
-- to be authenticated
-> ByteString -- ^ The cipher text.
-> AEADMem
-> IO AuthTag
computeAuth aad cText aeadmem =
AU.processByteSource (toByteString authWr) authMem >> extract authMem
where (aadWr, lAAD) = padAndLen aad
(cWr, lC) = padAndLen cText
authWr = aadWr <> cWr <> write lAAD <> write lC
authMem = authInternals aeadmem
verify :: Encodable aad
=> aad
-> Locked
-> AEADMem
-> IO Bool
verify aad aead = fmap matchTag . computeAuth aad (unsafeToCipherText aead)
where matchTag = (==) (unsafeToAuthTag aead)
-- | Encrypt a plain text object.
encrypt :: Encodable plain
=> plain -- The plain object that needs encryption
-> AEADMem
-> IO ByteString
encrypt plain = transform $ toByteString plain
-- | Decrypt to recover the plain text object. We assume a stream
-- cipher and hence transform is the encryption and decryption
-- routine.
decrypt :: Encodable plain
=> Locked
-> AEADMem
-> IO (Maybe plain)
decrypt aead = fmap fromByteString . transform (unsafeToCipherText aead)
-- | Compute the padded write of an encodable element and its length.
padAndLen :: Encodable a => a -> (WriteTo, LE Word64)
padAndLen a = (padWrite 0 pL aWr, len)
where aWr = writeEncodable a
len = toLen (transferSize aWr)
toLen = toEnum . fromEnum
pL = 1 `blocksOf` (Proxy :: Proxy AuthTag)
-- | Create the locked message from the associated Nounce, cipher
-- text, and the authentication tag.
unsafeLocked :: Nounce Cipher
-> ByteString
-> AuthTag
-> Locked
unsafeLocked = AEAD
name :: String
name = unwords [CI.name,"+", AI.name]
description :: String
description = unwords [ "AEAD implementation based on",name]