{-# 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