From d7186b9a59a7381c46c7765ab610a2403349d956 Mon Sep 17 00:00:00 2001 From: Vincent Hanquez Date: Mon, 13 Apr 2015 10:15:55 +0100 Subject: [PATCH] wip AES merging --- Crypto/Cipher/AES/Internal.hs | 39 ++- Crypto/Cipher/AES/Primitive.hs | 578 +++++++++++++++++++++++++++++++++ Crypto/Cipher/Types.hs | 1 + Crypto/Cipher/Types/Block.hs | 1 + cryptonite.cabal | 1 + 5 files changed, 615 insertions(+), 5 deletions(-) create mode 100644 Crypto/Cipher/AES/Primitive.hs diff --git a/Crypto/Cipher/AES/Internal.hs b/Crypto/Cipher/AES/Internal.hs index d75bbe6..e385103 100644 --- a/Crypto/Cipher/AES/Internal.hs +++ b/Crypto/Cipher/AES/Internal.hs @@ -6,11 +6,32 @@ -- Portability : good -- {-# LANGUAGE ForeignFunctionInterface #-} -{-# LANGUAGE EmptyDataDecls #-} {-# OPTIONS_GHC -fno-warn-unused-binds #-} {-# OPTIONS_GHC -fno-warn-unused-matches #-} module Crypto.Cipher.AES.Internal - ( + ( AES(..) + , AESGCM(..) + , AESOCB(..) + , c_aes_init + , c_aes_encrypt_ecb + , c_aes_encrypt_cbc + , c_aes_encrypt_xts + , c_aes_decrypt_ecb + , c_aes_decrypt_cbc + , c_aes_decrypt_xts + , c_aes_encrypt_ctr + , c_aes_gen_ctr + , c_aes_gen_ctr_cont + , c_aes_gcm_init + , c_aes_gcm_aad + , c_aes_gcm_encrypt + , c_aes_gcm_decrypt + , c_aes_gcm_finish + , c_aes_ocb_init + , c_aes_ocb_aad + , c_aes_ocb_encrypt + , c_aes_ocb_decrypt + , c_aes_ocb_finish ) where import Data.Word @@ -18,9 +39,17 @@ import Foreign.Ptr import Foreign.C.Types import Foreign.C.String -data AES -data AESOCB -data AESGCM +import Crypto.Internal.Memory + +-- | AES Context (pre-processed key) +newtype AES = AES SecureBytes + +-- | AESGCM State +newtype AESGCM = AESGCM SecureBytes + +-- | AESOCB State +newtype AESOCB = AESOCB SecureBytes + ------------------------------------------------------------------------ foreign import ccall "cryptonite_aes.h cryptonite_aes_initkey" c_aes_init :: Ptr AES -> CString -> CUInt -> IO () diff --git a/Crypto/Cipher/AES/Primitive.hs b/Crypto/Cipher/AES/Primitive.hs new file mode 100644 index 0000000..3a9a0ce --- /dev/null +++ b/Crypto/Cipher/AES/Primitive.hs @@ -0,0 +1,578 @@ +{-# LANGUAGE ForeignFunctionInterface #-} +{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +-- | +-- Module : Crypto.Cipher.AES +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : stable +-- Portability : good +-- +module Crypto.Cipher.AES.Primitive + ( + -- * block cipher data types + AES + , AES128 + , AES192 + , AES256 + + -- * Authenticated encryption block cipher types + , AESGCM + + -- * creation + , initAES + + -- * misc + , genCTR + , genCounter + + -- * encryption + , encryptECB + , encryptCBC + , encryptCTR + , encryptXTS + , encryptGCM + , encryptOCB + + -- * decryption + , decryptECB + , decryptCBC + , decryptCTR + , decryptXTS + , decryptGCM + , decryptOCB + ) where + +import Data.Word +import Foreign.Ptr +import Foreign.ForeignPtr +import Foreign.C.Types +import Foreign.C.String +import Data.ByteString.Internal +import Data.ByteString.Unsafe +import qualified Data.ByteString as B +import qualified Data.ByteString.Internal as B (ByteString(PS), mallocByteString, memcpy) +import System.IO.Unsafe (unsafePerformIO) + +import Crypto.Error +import Crypto.Cipher.Types +import Crypto.Cipher.AES.Internal +import Crypto.Internal.ByteArray +import Crypto.Cipher.Types.Block (IV(..)) + +import Data.SecureMem + + +-- | AES with 128 bit key +newtype AES128 = AES128 AES + +-- | AES with 192 bit key +newtype AES192 = AES192 AES + +-- | AES with 256 bit key +newtype AES256 = AES256 AES + +instance Cipher AES where + cipherName _ = "AES" + cipherKeySize _ = KeySizeEnum [16,24,32] + cipherInit k = initAES k + +instance Cipher AES128 where + cipherName _ = "AES128" + cipherKeySize _ = KeySizeFixed 16 + cipherInit k = AES128 `fmap` initAES k + +instance Cipher AES192 where + cipherName _ = "AES192" + cipherKeySize _ = KeySizeFixed 24 + cipherInit k = AES192 `fmap` initAES k + +instance Cipher AES256 where + cipherName _ = "AES256" + cipherKeySize _ = KeySizeFixed 32 + cipherInit k = AES256 `fmap` initAES k + +instance BlockCipher AES where + blockSize _ = 16 + ecbEncrypt = encryptECB + ecbDecrypt = decryptECB + cbcEncrypt = encryptCBC + cbcDecrypt = decryptCBC + ctrCombine = encryptCTR + {- + aeadInit AEAD_GCM aes iv = Just $ AEAD aes $ AEADState $ gcmInit aes iv + aeadInit AEAD_OCB aes iv = Just $ AEAD aes $ AEADState $ ocbInit aes iv + aeadInit _ _ _ = Nothing + -} +instance BlockCipher128 AES where + xtsEncrypt = encryptXTS + xtsDecrypt = decryptXTS + +{-} +instance AEADModeImpl AES AESGCM where + aeadStateAppendHeader _ = gcmAppendAAD + aeadStateEncrypt = gcmAppendEncrypt + aeadStateDecrypt = gcmAppendDecrypt + aeadStateFinalize = gcmFinish + +instance AEADModeImpl AES AESOCB where + aeadStateAppendHeader = ocbAppendAAD + aeadStateEncrypt = ocbAppendEncrypt + aeadStateDecrypt = ocbAppendDecrypt + aeadStateFinalize = ocbFinish + -} + +#define INSTANCE_BLOCKCIPHER(CSTR) \ +instance BlockCipher CSTR where \ + { blockSize _ = 16 \ + ; ecbEncrypt (CSTR aes) = ecbEncryptLegacy encryptECB aes \ + ; ecbDecrypt (CSTR aes) = ecbDecryptLegacy decryptECB aes \ + ; cbcEncrypt (CSTR aes) = encryptCBC aes \ + ; cbcDecrypt (CSTR aes) = decryptCBC aes \ + ; ctrCombine (CSTR aes) = encryptCTR aes \ + ; aeadInit AEAD_GCM cipher@(CSTR aes) iv = Just $ AEAD cipher $ AEADState $ gcmInit aes iv \ + ; aeadInit AEAD_OCB cipher@(CSTR aes) iv = Just $ AEAD cipher $ AEADState $ ocbInit aes iv \ + ; aeadInit _ _ _ = Nothing \ + }; \ +instance BlockCipher128 CSTR where \ + { xtsEncrypt (CSTR aes1, CSTR aes2) = encryptXTS (aes1,aes2) \ + ; xtsDecrypt (CSTR aes1, CSTR aes2) = decryptXTS (aes1,aes2) \ + }; \ +\ +instance AEADModeImpl CSTR AESGCM where \ + { aeadStateAppendHeader (CSTR _) gcmState bs = gcmAppendAAD gcmState bs \ + ; aeadStateEncrypt (CSTR aes) gcmState input = gcmAppendEncrypt aes gcmState input \ + ; aeadStateDecrypt (CSTR aes) gcmState input = gcmAppendDecrypt aes gcmState input \ + ; aeadStateFinalize (CSTR aes) gcmState len = gcmFinish aes gcmState len \ + }; \ +\ +{-instance AEADModeImpl CSTR AESOCB where \ + { aeadStateAppendHeader (CSTR aes) ocbState bs = ocbAppendAAD aes ocbState bs \ + ; aeadStateEncrypt (CSTR aes) ocbState input = ocbAppendEncrypt aes ocbState input \ + ; aeadStateDecrypt (CSTR aes) ocbState input = ocbAppendDecrypt aes ocbState input \ + ; aeadStateFinalize (CSTR aes) ocbState len = ocbFinish aes ocbState len \ + }-} + +--INSTANCE_BLOCKCIPHER(AES128) +--INSTANCE_BLOCKCIPHER(AES192) +--INSTANCE_BLOCKCIPHER(AES256) + +sizeGCM :: Int +sizeGCM = 80 + +sizeOCB :: Int +sizeOCB = 160 + +keyToPtr :: AES -> (Ptr AES -> IO a) -> IO a +keyToPtr (AES b) f = withByteArray b (f . castPtr) + +ivToPtr :: ByteArrayAccess iv => iv -> (Ptr Word8 -> IO a) -> IO a +ivToPtr iv f = withByteArray iv (f . castPtr) + +{- +ivCopyPtr :: IV AES -> (Ptr Word8 -> IO ()) -> IO (IV AES) +ivCopyPtr (IV iv) f = do + newIV <- byteArrayAlloc 16 $ \newPtr -> do + withByteArray iv $ \ivPtr -> B.memcpy newPtr ivPtr 16 + withByteArray newIV $ f + return $! IV newIV +-} + +withKeyAndIV :: ByteArrayAccess iv => AES -> iv -> (Ptr AES -> Ptr Word8 -> IO a) -> IO a +withKeyAndIV ctx iv f = keyToPtr ctx $ \kptr -> ivToPtr iv $ \ivp -> f kptr ivp + +withKey2AndIV :: ByteArrayAccess iv => AES -> AES -> iv -> (Ptr AES -> Ptr AES -> Ptr Word8 -> IO a) -> IO a +withKey2AndIV key1 key2 iv f = + keyToPtr key1 $ \kptr1 -> keyToPtr key2 $ \kptr2 -> ivToPtr iv $ \ivp -> f kptr1 kptr2 ivp + +withGCMKeyAndCopySt :: AES -> AESGCM -> (Ptr AESGCM -> Ptr AES -> IO a) -> IO (a, AESGCM) +withGCMKeyAndCopySt aes (AESGCM gcmSt) f = + keyToPtr aes $ \aesPtr -> do + newSt <- secureMemCopy gcmSt + a <- withSecureMemPtr newSt $ \gcmStPtr -> f (castPtr gcmStPtr) aesPtr + return (a, AESGCM newSt) + +withNewGCMSt :: AESGCM -> (Ptr AESGCM -> IO ()) -> IO AESGCM +withNewGCMSt (AESGCM gcmSt) f = withSecureMemCopy gcmSt (f . castPtr) >>= \sm2 -> return (AESGCM sm2) + +withOCBKeyAndCopySt :: AES -> AESOCB -> (Ptr AESOCB -> Ptr AES -> IO a) -> IO (a, AESOCB) +withOCBKeyAndCopySt aes (AESOCB gcmSt) f = + keyToPtr aes $ \aesPtr -> do + newSt <- secureMemCopy gcmSt + a <- withSecureMemPtr newSt $ \gcmStPtr -> f (castPtr gcmStPtr) aesPtr + return (a, AESOCB newSt) + +-- | Initialize a new context with a key +-- +-- Key need to be of length 16, 24 or 32 bytes. any other values will cause undefined behavior +initAES :: ByteArrayAccess key => key -> CryptoFailable AES +initAES k + | len == 16 = CryptoPassed $ initWithRounds 10 + | len == 24 = CryptoPassed $ initWithRounds 12 + | len == 32 = CryptoPassed $ initWithRounds 14 + | otherwise = CryptoFailed CryptoError_KeySizeInvalid + where len = byteArrayLength k + initWithRounds nbR = AES $ unsafeCreateSecureMem (16+2*2*16*nbR) aesInit + aesInit ptr = withByteArray k $ \ikey -> + c_aes_init (castPtr ptr) (castPtr ikey) (fromIntegral len) + +-- | encrypt using Electronic Code Book (ECB) +{-# NOINLINE encryptECB #-} +encryptECB :: ByteArray ba => AES -> ba -> ba +encryptECB = doECB c_aes_encrypt_ecb + +-- | encrypt using Cipher Block Chaining (CBC) +{-# NOINLINE encryptCBC #-} +encryptCBC :: ByteArray ba + => AES -- ^ AES Context + -> IV AES -- ^ Initial vector of AES block size + -> ba -- ^ plaintext + -> ba -- ^ ciphertext +encryptCBC = doCBC c_aes_encrypt_cbc + +-- | generate a counter mode pad. this is generally xor-ed to an input +-- to make the standard counter mode block operations. +-- +-- if the length requested is not a multiple of the block cipher size, +-- more data will be returned, so that the returned bytestring is +-- a multiple of the block cipher size. +{-# NOINLINE genCTR #-} +genCTR :: ByteArray ba + => AES -- ^ Cipher Key. + -> IV AES -- ^ usually a 128 bit integer. + -> Int -- ^ length of bytes required. + -> ba +genCTR ctx (IV iv) len + | len <= 0 = empty + | otherwise = byteArrayAllocAndFreeze (nbBlocks * 16) generate + where generate o = withKeyAndIV ctx iv $ \k i -> c_aes_gen_ctr (castPtr o) k i (fromIntegral nbBlocks) + (nbBlocks',r) = len `quotRem` 16 + nbBlocks = if r == 0 then nbBlocks' else nbBlocks' + 1 + +-- | generate a counter mode pad. this is generally xor-ed to an input +-- to make the standard counter mode block operations. +-- +-- if the length requested is not a multiple of the block cipher size, +-- more data will be returned, so that the returned bytestring is +-- a multiple of the block cipher size. +-- +-- Similiar to 'genCTR' but also return the next IV for continuation +{-# NOINLINE genCounter #-} +genCounter :: AES + -> IV AES + -> Int + -> (ByteString, IV AES) +genCounter ctx iv len + | len <= 0 = (B.empty, iv) + | otherwise = unsafePerformIO $ do + undefined + {- + fptr <- B.mallocByteString outputLength + newIv <- withForeignPtr fptr $ \o -> + keyToPtr ctx $ \k -> + ivCopyPtr iv $ \i -> do + c_aes_gen_ctr_cont (castPtr o) k i (fromIntegral nbBlocks) + let !out = B.PS fptr 0 outputLength + return $! (out `seq` newIv `seq` (out, newIv)) + where + (nbBlocks',r) = len `quotRem` 16 + nbBlocks = if r == 0 then nbBlocks' else nbBlocks' + 1 + outputLength = nbBlocks * 16 + -} + +{- TODO: when genCTR has same AESIV requirements for IV, add the following rules: + - RULES "snd . genCounter" forall ctx iv len . snd (genCounter ctx iv len) = genCTR ctx iv len + -} + +-- | encrypt using Counter mode (CTR) +-- +-- in CTR mode encryption and decryption is the same operation. +{-# NOINLINE encryptCTR #-} +encryptCTR :: ByteArray ba + => AES -- ^ AES Context + -> IV AES -- ^ initial vector of AES block size (usually representing a 128 bit integer) + -> ba -- ^ plaintext input + -> ba -- ^ ciphertext output +encryptCTR ctx iv input + | len <= 0 = empty + | byteArrayLength iv /= 16 = error $ "AES error: IV length must be block size (16). Its length is: " ++ (show $ byteArrayLength iv) + | otherwise = byteArrayAllocAndFreeze len doEncrypt + where doEncrypt o = withKeyAndIV ctx iv $ \k v -> withByteArray input $ \i -> + c_aes_encrypt_ctr (castPtr o) k v i (fromIntegral len) + len = byteArrayLength input + +-- | encrypt using Galois counter mode (GCM) +-- return the encrypted bytestring and the tag associated +-- +-- note: encrypted data is identical to CTR mode in GCM, however +-- a tag is also computed. +{-# NOINLINE encryptGCM #-} +encryptGCM :: (ByteArrayAccess iv, ByteArrayAccess aad, ByteArray ba) + => AES -- ^ AES Context + -> iv -- ^ IV initial vector of any size + -> aad -- ^ data to authenticate (AAD) + -> ba -- ^ data to encrypt + -> (ba, AuthTag) -- ^ ciphertext and tag +encryptGCM = doGCM gcmAppendEncrypt + +-- | encrypt using OCB v3 +-- return the encrypted bytestring and the tag associated +{-# NOINLINE encryptOCB #-} +encryptOCB :: (ByteArrayAccess iv, ByteArrayAccess aad, ByteArray ba) + => AES -- ^ AES Context + -> iv -- ^ IV initial vector of any size + -> aad -- ^ data to authenticate (AAD) + -> ba -- ^ data to encrypt + -> (ba, AuthTag) -- ^ ciphertext and tag +encryptOCB = doOCB ocbAppendEncrypt + +-- | encrypt using XTS +-- +-- the first key is the normal block encryption key +-- the second key is used for the initial block tweak +{-# NOINLINE encryptXTS #-} +encryptXTS :: ByteArray ba + => (AES,AES) -- ^ AES cipher and tweak context + -> IV AES -- ^ a 128 bits IV, typically a sector or a block offset in XTS + -> Word32 -- ^ number of rounds to skip, also seen a 16 byte offset in the sector or block. + -> ba -- ^ input to encrypt + -> ba -- ^ output encrypted +encryptXTS = doXTS c_aes_encrypt_xts + +-- | decrypt using Electronic Code Book (ECB) +{-# NOINLINE decryptECB #-} +decryptECB :: ByteArray ba => AES -> ba -> ba +decryptECB = doECB c_aes_decrypt_ecb + +-- | decrypt using Cipher block chaining (CBC) +{-# NOINLINE decryptCBC #-} +decryptCBC :: ByteArray ba => AES -> IV AES -> ba -> ba +decryptCBC = doCBC c_aes_decrypt_cbc + +-- | decrypt using Counter mode (CTR). +-- +-- in CTR mode encryption and decryption is the same operation. +decryptCTR :: ByteArray ba + => AES -- ^ AES Context + -> IV AES -- ^ initial vector, usually representing a 128 bit integer + -> ba -- ^ ciphertext input + -> ba -- ^ plaintext output +decryptCTR = encryptCTR + +-- | decrypt using XTS +{-# NOINLINE decryptXTS #-} +decryptXTS :: ByteArray ba + => (AES,AES) -- ^ AES cipher and tweak context + -> IV AES -- ^ a 128 bits IV, typically a sector or a block offset in XTS + -> Word32 -- ^ number of rounds to skip, also seen a 16 byte offset in the sector or block. + -> ba -- ^ input to decrypt + -> ba -- ^ output decrypted +decryptXTS = doXTS c_aes_decrypt_xts + +-- | decrypt using Galois Counter Mode (GCM) +{-# NOINLINE decryptGCM #-} +decryptGCM :: (ByteArrayAccess aad, ByteArrayAccess iv, ByteArray ba) + => AES -- ^ Key + -> iv -- ^ IV initial vector of any size + -> aad -- ^ data to authenticate (AAD) + -> ba -- ^ data to decrypt + -> (ba, AuthTag) -- ^ plaintext and tag +decryptGCM = doGCM gcmAppendDecrypt + +-- | decrypt using Offset Codebook Mode (OCB) +{-# NOINLINE decryptOCB #-} +decryptOCB :: (ByteArrayAccess aad, ByteArrayAccess iv, ByteArray ba) + => AES -- ^ Key + -> iv -- ^ IV initial vector of any size + -> aad -- ^ data to authenticate (AAD) + -> ba -- ^ data to decrypt + -> (ba, AuthTag) -- ^ plaintext and tag +decryptOCB = doOCB ocbAppendDecrypt + +{-# INLINE doECB #-} +doECB :: ByteArray ba + => (Ptr b -> Ptr AES -> CString -> CUInt -> IO ()) + -> AES -> ba -> ba +doECB f ctx input + | r /= 0 = error $ "Encryption error: input length must be a multiple of block size (16). Its length is: " ++ (show len) + | otherwise = byteArrayAllocAndFreeze len $ \o -> + keyToPtr ctx $ \k -> + withByteArray input $ \i -> + f (castPtr o) k i (fromIntegral nbBlocks) + where (nbBlocks, r) = len `quotRem` 16 + len = byteArrayLength input + +{-# INLINE doCBC #-} +doCBC :: ByteArray ba + => (Ptr b -> Ptr AES -> Ptr Word8 -> CString -> CUInt -> IO ()) + -> AES -> IV AES -> ba -> ba +doCBC f ctx (IV iv) input + | len == 0 = empty + | r /= 0 = error $ "Encryption error: input length must be a multiple of block size (16). Its length is: " ++ (show len) + | otherwise = byteArrayAllocAndFreeze len $ \o -> + withKeyAndIV ctx iv $ \k v -> + withByteArray input $ \i -> + f (castPtr o) k v i (fromIntegral nbBlocks) + where (nbBlocks, r) = len `quotRem` 16 + len = byteArrayLength input + +{-# INLINE doXTS #-} +doXTS :: ByteArray ba + => (Ptr b -> Ptr AES -> Ptr AES -> Ptr Word8 -> CUInt -> CString -> CUInt -> IO ()) + -> (AES, AES) + -> IV AES + -> Word32 + -> ba + -> ba +doXTS f (key1,key2) iv spoint input + | len == 0 = empty + | r /= 0 = error $ "Encryption error: input length must be a multiple of block size (16) for now. Its length is: " ++ (show len) + | otherwise = byteArrayAllocAndFreeze len $ \o -> withKey2AndIV key1 key2 iv $ \k1 k2 v -> withByteArray input $ \i -> + f (castPtr o) k1 k2 v (fromIntegral spoint) i (fromIntegral nbBlocks) + where (nbBlocks, r) = len `quotRem` 16 + len = byteArrayLength input + +------------------------------------------------------------------------ +-- GCM +------------------------------------------------------------------------ + +{-# INLINE doGCM #-} +doGCM :: (ByteArrayAccess iv, ByteArrayAccess aad, ByteArray ba) + => (AES -> AESGCM -> ba -> (ba, AESGCM)) + -> AES + -> iv + -> aad + -> ba + -> (ba, AuthTag) +doGCM f ctx iv aad input = (output, tag) + where tag = gcmFinish ctx after 16 + (output, after) = f ctx afterAAD input + afterAAD = gcmAppendAAD ini aad + ini = gcmInit ctx iv + +-- | initialize a gcm context +{-# NOINLINE gcmInit #-} +gcmInit :: ByteArrayAccess iv => AES -> iv -> AESGCM +gcmInit ctx iv = unsafePerformIO $ do + sm <- createSecureMem sizeGCM $ \gcmStPtr -> + withKeyAndIV ctx iv $ \k v -> + c_aes_gcm_init (castPtr gcmStPtr) k v (fromIntegral $ byteArrayLength iv) + return $ AESGCM sm + +-- | append data which is going to just be authentified to the GCM context. +-- +-- need to happen after initialization and before appending encryption/decryption data. +{-# NOINLINE gcmAppendAAD #-} +gcmAppendAAD :: ByteArrayAccess aad => AESGCM -> aad -> AESGCM +gcmAppendAAD gcmSt input = unsafePerformIO doAppend + where doAppend = + withNewGCMSt gcmSt $ \gcmStPtr -> + withByteArray input $ \i -> + c_aes_gcm_aad gcmStPtr i (fromIntegral $ byteArrayLength input) + +-- | append data to encrypt and append to the GCM context +-- +-- bytestring need to be multiple of AES block size, unless it's the last call to this function. +-- need to happen after AAD appending, or after initialization if no AAD data. +{-# NOINLINE gcmAppendEncrypt #-} +gcmAppendEncrypt :: ByteArray ba => AES -> AESGCM -> ba -> (ba, AESGCM) +gcmAppendEncrypt ctx gcm input = unsafePerformIO $ withGCMKeyAndCopySt ctx gcm doEnc + where len = byteArrayLength input + doEnc gcmStPtr aesPtr = + byteArrayAlloc len $ \o -> + withByteArray input $ \i -> + c_aes_gcm_encrypt (castPtr o) gcmStPtr aesPtr i (fromIntegral len) + +-- | append data to decrypt and append to the GCM context +-- +-- bytestring need to be multiple of AES block size, unless it's the last call to this function. +-- need to happen after AAD appending, or after initialization if no AAD data. +{-# NOINLINE gcmAppendDecrypt #-} +gcmAppendDecrypt :: ByteArray ba => AES -> AESGCM -> ba -> (ba, AESGCM) +gcmAppendDecrypt ctx gcm input = unsafePerformIO $ withGCMKeyAndCopySt ctx gcm doDec + where len = byteArrayLength input + doDec gcmStPtr aesPtr = + byteArrayAlloc len $ \o -> + withByteArray input $ \i -> + c_aes_gcm_decrypt (castPtr o) gcmStPtr aesPtr i (fromIntegral len) + +-- | Generate the Tag from GCM context +{-# NOINLINE gcmFinish #-} +gcmFinish :: AES -> AESGCM -> Int -> AuthTag +gcmFinish ctx gcm taglen = AuthTag $ B.take taglen computeTag + where computeTag = unsafeCreate 16 $ \t -> + withGCMKeyAndCopySt ctx gcm (c_aes_gcm_finish (castPtr t)) >> return () + +------------------------------------------------------------------------ +-- OCB v3 +------------------------------------------------------------------------ + +{-# INLINE doOCB #-} +doOCB :: (ByteArrayAccess iv, ByteArrayAccess aad, ByteArray ba) + => (AES -> AESOCB -> ba -> (ba, AESOCB)) + -> AES + -> iv + -> aad + -> ba + -> (ba, AuthTag) +doOCB f ctx iv aad input = (output, tag) + where tag = ocbFinish ctx after 16 + (output, after) = f ctx afterAAD input + afterAAD = ocbAppendAAD ctx ini aad + ini = ocbInit ctx iv + +-- | initialize an ocb context +{-# NOINLINE ocbInit #-} +ocbInit :: ByteArrayAccess iv => AES -> iv -> AESOCB +ocbInit ctx iv = unsafePerformIO $ do + sm <- createSecureMem sizeOCB $ \ocbStPtr -> + withKeyAndIV ctx iv $ \k v -> + c_aes_ocb_init (castPtr ocbStPtr) k v (fromIntegral $ byteArrayLength iv) + return $ AESOCB sm + +-- | append data which is going to just be authentified to the OCB context. +-- +-- need to happen after initialization and before appending encryption/decryption data. +{-# NOINLINE ocbAppendAAD #-} +ocbAppendAAD :: ByteArrayAccess aad => AES -> AESOCB -> aad -> AESOCB +ocbAppendAAD ctx ocb input = unsafePerformIO (snd `fmap` withOCBKeyAndCopySt ctx ocb doAppend) + where doAppend ocbStPtr aesPtr = + withByteArray input $ \i -> + c_aes_ocb_aad ocbStPtr aesPtr i (fromIntegral $ byteArrayLength input) + +-- | append data to encrypt and append to the OCB context +-- +-- bytestring need to be multiple of AES block size, unless it's the last call to this function. +-- need to happen after AAD appending, or after initialization if no AAD data. +{-# NOINLINE ocbAppendEncrypt #-} +ocbAppendEncrypt :: ByteArray ba => AES -> AESOCB -> ba -> (ba, AESOCB) +ocbAppendEncrypt ctx ocb input = unsafePerformIO $ withOCBKeyAndCopySt ctx ocb doEnc + where len = byteArrayLength input + doEnc ocbStPtr aesPtr = + byteArrayAlloc len $ \o -> + withByteArray input $ \i -> + c_aes_ocb_encrypt (castPtr o) ocbStPtr aesPtr i (fromIntegral len) + +-- | append data to decrypt and append to the OCB context +-- +-- bytestring need to be multiple of AES block size, unless it's the last call to this function. +-- need to happen after AAD appending, or after initialization if no AAD data. +{-# NOINLINE ocbAppendDecrypt #-} +ocbAppendDecrypt :: ByteArray ba => AES -> AESOCB -> ba -> (ba, AESOCB) +ocbAppendDecrypt ctx ocb input = unsafePerformIO $ withOCBKeyAndCopySt ctx ocb doDec + where len = byteArrayLength input + doDec ocbStPtr aesPtr = + byteArrayAlloc len $ \o -> + withByteArray input $ \i -> + c_aes_ocb_decrypt (castPtr o) ocbStPtr aesPtr i (fromIntegral len) + +-- | Generate the Tag from OCB context +{-# NOINLINE ocbFinish #-} +ocbFinish :: AES -> AESOCB -> Int -> AuthTag +ocbFinish ctx ocb taglen = AuthTag $ B.take taglen computeTag + where computeTag = unsafeCreate 16 $ \t -> + withOCBKeyAndCopySt ctx ocb (c_aes_ocb_finish (castPtr t)) >> return () + diff --git a/Crypto/Cipher/Types.hs b/Crypto/Cipher/Types.hs index 7005ac9..5e73395 100644 --- a/Crypto/Cipher/Types.hs +++ b/Crypto/Cipher/Types.hs @@ -13,6 +13,7 @@ module Crypto.Cipher.Types -- * Cipher classes Cipher(..) , BlockCipher(..) + , BlockCipher128(..) , ecbEncryptLegacy , ecbDecryptLegacy , StreamCipher(..) diff --git a/Crypto/Cipher/Types/Block.hs b/Crypto/Cipher/Types/Block.hs index 4812dbf..8870249 100644 --- a/Crypto/Cipher/Types/Block.hs +++ b/Crypto/Cipher/Types/Block.hs @@ -14,6 +14,7 @@ module Crypto.Cipher.Types.Block ( -- * BlockCipher BlockCipher(..) + , BlockCipher128(..) , ecbEncryptLegacy , ecbDecryptLegacy -- * initialization vector (IV) diff --git a/cryptonite.cabal b/cryptonite.cabal index 78a0080..c130f46 100644 --- a/cryptonite.cabal +++ b/cryptonite.cabal @@ -89,6 +89,7 @@ Library Crypto.Random.Entropy.Unsafe Crypto.Internal.ByteArray Other-modules: Crypto.Cipher.AES.Internal + Crypto.Cipher.AES.Primitive Crypto.Cipher.Blowfish.Box Crypto.Cipher.Blowfish.Primitive Crypto.Cipher.Camellia.Primitive