raaz-0.1.0: Raaz/Cipher/Internal.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ConstraintKinds #-}
-- | This module exposes the low-level internal details of ciphers. Do
-- not import this module unless you want to implement a new cipher or
-- give a new implementation of an existing cipher.
module Raaz.Cipher.Internal
(
-- * Internals of a cipher.
-- $cipherdoc$
Cipher, CipherMode(..)
-- ** Cipher implementation
, CipherI(..), SomeCipherI(..)
-- ** Stream ciphers.
-- $streamcipher$
, StreamCipher, makeCipherI
, transform, transform'
-- ** Unsafe encryption and decryption.
-- $unsafecipher$
--
, unsafeEncrypt, unsafeDecrypt, unsafeEncrypt', unsafeDecrypt'
) where
import Control.Monad.IO.Class (liftIO)
import Data.ByteString.Internal as IB
import Foreign.Ptr (castPtr)
import System.IO.Unsafe (unsafePerformIO)
import Raaz.Core
import Raaz.Core.Util.ByteString as B
-- $cipherdoc$
--
-- Ciphers provide symmetric encryption in the raaz library and are
-- captured by the type class `Cipher`. They are instances of the
-- class `Symmetric` and the associated type `Key` captures the all
-- that is required to determine the encryption and decryption
-- process. In most ciphers, this includes what is know as the
-- _encryption key_ as well as the _initialisation vector_.
--
-- Instances of `Cipher` is only required to provide full block
-- encryption/decryption algorithms. Implementations are captured by
-- two types.
--
-- [`CipherI`:] Values of this type that captures implementations of a
-- cipher. This type is parameterised over the memory element that is
-- used internally by the implementation.
--
-- [`SomeCipherI`:] The existentially quantified version of `CipherI`
-- over its memory element. By wrapping the memory element inside the
-- existential quantifier, values of this type exposes only the
-- interface and not the internals of the implementation. The
-- `Implementation` associated type of a cipher is the type
-- `SomeCipherI`
--
-- To support a new cipher, a developer needs to:
--
-- 1. Define a new type which captures the cipher. This type should be
-- an instance of the class `Cipher`.
--
-- 2. Define an implementation, i.e. a value of the type `SomeCipherI`.
--
-- 3. Define a recommended implementation, i.e. an instance of the
-- type class `Raaz.Core.Primitives.Recommendation`
--
-- $streamcipher$
--
-- Stream ciphers are special class of ciphers which can encrypt
-- messages of any length (not necessarily multiples of block length).
-- Typically, stream ciphers are obtained by xoring the data with a
-- stream of prg values that the stream ciphers generate. As a
-- consequence, the encryption and decryption is the same algorithm.
-- one can also use the stream cipher as a pseudo-random generator.
--
-- We have the class `StreamCipher` that captures valid stream ciphers.
--
-- | Block cipher modes.
data CipherMode = CBC -- ^ Cipher-block chaining
| CTR -- ^ Counter
deriving (Show, Eq)
-- | The implementation of a block cipher.
data CipherI cipher encMem decMem = CipherI
{ cipherIName :: String
, cipherIDescription :: String
-- | The underlying block encryption function.
, encryptBlocks :: Pointer -> BLOCKS cipher -> MT encMem ()
-- | The underlying block decryption function.
, decryptBlocks :: Pointer -> BLOCKS cipher -> MT decMem ()
, cipherStartAlignment :: Alignment
}
-- | Type constraints on the memory of a block cipher implementation.
type CipherM cipher encMem decMem = ( Initialisable encMem (Key cipher)
, Initialisable decMem (Key cipher)
, Primitive cipher
) -- TODO: More need initialisable from buffer.
-- | Some implementation of a block cipher. This type is existentially
-- quantifies over the memory used in the implementation.
data SomeCipherI cipher =
forall encMem decMem . CipherM cipher encMem decMem
=> SomeCipherI (CipherI cipher encMem decMem)
instance BlockAlgorithm (CipherI cipher encMem decMem) where
bufferStartAlignment = cipherStartAlignment
instance Describable (CipherI cipher encMem decMem) where
name = cipherIName
description = cipherIDescription
instance Describable (SomeCipherI cipher) where
name (SomeCipherI cI) = name cI
description (SomeCipherI cI) = description cI
instance BlockAlgorithm (SomeCipherI cipher) where
bufferStartAlignment (SomeCipherI imp) = bufferStartAlignment imp
-- | Class capturing ciphers. The implementation of this class should
-- give an encryption and decryption algorithm for messages of length
-- which is a multiple of the block size. Needless to say, the
-- encryption and decryption should be inverses of each other for such
-- messages.
class (Primitive cipher, Implementation cipher ~ SomeCipherI cipher, Describable cipher)
=> Cipher cipher
-- | Class that captures stream ciphers. An instance of `StreamCipher`
-- should be an instance of `Cipher`, with the following additional
-- constraints.
--
-- 1. The encryption and decryption should be the same algorithm.
--
-- 2. Encryption/decryption can be applied to a messages of length @l@
-- even if @l@ is not a multiple of block length.
--
-- 3. The encryption of a prefix of a length @l@ of a message @m@
-- should be the same as the @l@ length prefix of the encryption of
-- @m@.
--
-- It is the duty of the implementer of the cipher to ensure that the
-- above conditions are true before declaring an instance of a stream
-- cipher.
class Cipher cipher => StreamCipher cipher
-- | Constructs a `CipherI` value out of a stream transformation function. Useful in
-- building a Cipher instance of a stream cipher.
makeCipherI :: String -- ^ name
-> String -- ^ description
-> (Pointer -> BLOCKS prim -> MT mem ()) -- ^ stream transformer
-> Alignment -- ^ buffer starting alignment
-> CipherI prim mem mem
makeCipherI nm des trans = CipherI nm des trans trans
------------------ Unsafe cipher operations ------------------------
-- $unsafecipher$
--
-- We expose some unsafe functions to encrypt and decrypt bytestrings.
-- These function works correctly only if the input byte string has a
-- length which is a multiple of the block size of the cipher and
-- hence are unsafe to use as general methods of encryption and
-- decryption of data. Use these functions for testing and
-- benchmarking and nothing else.
--
-- There are multiple ways to handle arbitrary sized strings like
-- padding, cipher block stealing etc. They are not exposed here
-- though.
-- | Encrypt the given `ByteString`. This function is unsafe because
-- it only works correctly when the input `ByteString` is of length
-- which is a multiple of the block length of the cipher.
unsafeEncrypt' :: Cipher c
=> c -- ^ The cipher to use
-> Implementation c -- ^ The implementation to use
-> Key c -- ^ The key to use
-> ByteString -- ^ The string to encrypt.
-> ByteString
unsafeEncrypt' c simp@(SomeCipherI imp) key bs = IB.unsafeCreate sbytes go
where sz = atMost (B.length bs) `asTypeOf` blocksOf 1 c
BYTES sbytes = inBytes sz
go ptr = allocBufferFor simp sz $ \ buf -> insecurely $ do
initialise key
liftIO $ unsafeNCopyToPointer sz bs buf -- Copy the input to buffer.
encryptBlocks imp buf sz
liftIO $ Raaz.Core.memcpy (destination (castPtr ptr)) (source buf) sz
-- | Transforms a given bytestring using a stream cipher. We use the
-- transform instead of encrypt/decrypt because for stream ciphers
-- these operations are same.
transform' :: StreamCipher c
=> c
-> Implementation c
-> Key c
-> ByteString
-> ByteString
transform' c simp@(SomeCipherI imp) key bs = unsafePerformIO $ IB.createAndTrim (fromEnum $ inBytes blks) action
where blks = atLeast len `asTypeOf` blocksOf 1 c
len = B.length bs
action ptr = allocBufferFor simp blks $ \ buf -> insecurely $ do
initialise key
liftIO $ unsafeCopyToPointer bs buf -- copy data into the buffer
encryptBlocks imp buf blks -- encrypt it
liftIO $ Raaz.Core.memcpy (destination (castPtr ptr)) (source buf) len
-- copy it back to the actual pointer.
return $ fromIntegral len
-- | Transform a given bytestring using the recommended implementation
-- of a stream cipher.
transform :: (StreamCipher c, Recommendation c)
=> c
-> Key c
-> ByteString
-> ByteString
transform c = transform' c $ recommended c
-- | Encrypt using the recommended implementation. This function is
-- unsafe because it only works correctly when the input `ByteString`
-- is of length which is a multiple of the block length of the cipher.
unsafeEncrypt :: (Cipher c, Recommendation c)
=> c -- ^ The cipher
-> Key c -- ^ The key to use
-> ByteString -- ^ The string to encrypt
-> ByteString
unsafeEncrypt c = unsafeEncrypt' c $ recommended c
-- | Decrypts the given `ByteString`. This function is unsafe because
-- it only works correctly when the input `ByteString` is of length
-- which is a multiple of the block length of the cipher.
unsafeDecrypt' :: Cipher c
=> c -- ^ The cipher to use
-> Implementation c -- ^ The implementation to use
-> Key c -- ^ The key to use
-> ByteString -- ^ The string to encrypt.
-> ByteString
unsafeDecrypt' c simp@(SomeCipherI imp) key bs = IB.unsafeCreate sbytes go
where sz = atMost (B.length bs) `asTypeOf` blocksOf 1 c
BYTES sbytes = inBytes sz
go ptr = allocBufferFor simp sz $ \ buf -> insecurely $ do
initialise key
liftIO $ unsafeNCopyToPointer sz bs buf -- Copy the input to buffer.
decryptBlocks imp buf sz
liftIO $ Raaz.Core.memcpy (destination (castPtr ptr)) (source buf) sz
-- | Decrypt using the recommended implementation. This function is
-- unsafe because it only works correctly when the input `ByteString`
-- is of length which is a multiple of the block length of the cipher.
unsafeDecrypt :: (Cipher c, Recommendation c)
=> c -- ^ The cipher
-> Key c -- ^ The key to use
-> ByteString -- ^ The string to encrypt
-> ByteString
unsafeDecrypt c = unsafeDecrypt' c $ recommended c