{-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE RecordWildCards #-} module Crypto.Cipher.AES128 ( -- * Key types with crypto-api instances AESKey128, AESKey192, AESKey256 , BlockCipher(..), buildKeyIO, zeroIV -- * GCM Operations , makeGCMCtx, aesKeyToGCM, GCMCtx, AuthTag(..), AES_GCM , Crypto.Cipher.AES128.encryptGCM , Crypto.Cipher.AES128.decryptGCM ) where import Crypto.Cipher.AES128.Internal as I import Crypto.Classes import Data.Function (on) import Control.Monad (when) import Data.Serialize import Data.Tagged import Data.Word (Word8) import Foreign.Ptr import Foreign.ForeignPtr import Foreign.Marshal.Alloc as F import System.IO.Unsafe import Data.ByteString (ByteString) import qualified Data.ByteString as B import qualified Data.ByteString.Internal as B import qualified Data.ByteString.Unsafe as B instance Serialize AESKey128 where put k = do let RKey128 l h = (rawKey128 k) putWord64be h putWord64be l get = do b <- getByteString 16 case buildKey b of Nothing -> fail "Invalid key on 'get'" Just k -> return k instance Serialize AESKey192 where put k = do let RKey192 a b c = (rawKey192 k) putWord64be c putWord64be b putWord64be a get = do b <- getByteString 24 case buildKey b of Nothing -> fail "Invalid key on 'get'" Just k -> return k instance Serialize AESKey256 where put k = do let RKey256 a b c d = (rawKey256 k) putWord64be d putWord64be c putWord64be b putWord64be a get = do b <- getByteString 32 case buildKey b of Nothing -> fail "Invalid key on 'get'" Just k -> return k instance BlockCipher AESKey128 where blockSize = Tagged 128 keyLength = Tagged 128 buildKey bs | B.length bs >= 16 = unsafePerformIO $ B.unsafeUseAsCString bs (\p -> generateKey128 (castPtr p)) | otherwise = Nothing encryptBlock k b = unsafePerformIO $ do B.unsafeUseAsCStringLen b $ \(inP,len) -> do B.create (B.length b) $ \outP -> do encryptECB k (castPtr outP) (castPtr inP) (len`div`blkSize) decryptBlock k b = unsafePerformIO $ do B.unsafeUseAsCStringLen b $ \(inP,len) -> do B.create (B.length b) $ \outP -> do decryptECB k (castPtr outP) (castPtr inP) (len`div`blkSize) ecb = encryptBlock unEcb = decryptBlock ctr k (IV bs) pt = unsafePerformIO $ do B.unsafeUseAsCStringLen pt $ \(inP, len) -> do B.unsafeUseAsCStringLen bs $ \(ivP, ivLen) -> do when (ivLen /= (blockSizeBytes .::. k)) (error "Cipher-AES128: IV wrong length! They type system would have/should have caught this if you didn't use the IV constructor...") newIVFP <- B.mallocByteString ivLen ct <- B.create len $ \outP -> withForeignPtr newIVFP $ \newIVP -> do encryptCTR k (castPtr ivP) (castPtr newIVP) (castPtr outP) (castPtr inP) len let newIV = B.fromForeignPtr newIVFP 0 ivLen return (ct,IV newIV) {-# INLINE ctr #-} unCtr = ctr blkSize :: Int blkSize = 16 instance BlockCipher AESKey192 where blockSize = Tagged 128 keyLength = Tagged 192 buildKey bs | B.length bs >= 16 = unsafePerformIO $ B.unsafeUseAsCString bs (\p -> generateKey192 (castPtr p)) | otherwise = Nothing encryptBlock k b = unsafePerformIO $ do B.unsafeUseAsCStringLen b $ \(inP,len) -> do B.create (B.length b) $ \outP -> do encryptECB k (castPtr outP) (castPtr inP) (len`div`blkSize) decryptBlock k b = unsafePerformIO $ do B.unsafeUseAsCStringLen b $ \(inP,len) -> do B.create (B.length b) $ \outP -> do decryptECB k (castPtr outP) (castPtr inP) (len`div`blkSize) ecb = encryptBlock unEcb = decryptBlock ctr k (IV bs) pt = unsafePerformIO $ do B.unsafeUseAsCStringLen pt $ \(inP, len) -> do B.unsafeUseAsCStringLen bs $ \(ivP, ivLen) -> do when (ivLen /= (blockSizeBytes .::. k)) (error "Cipher-AES128: IV wrong length! They type system would have/should have caught this if you didn't use the IV constructor...") newIVFP <- B.mallocByteString ivLen ct <- B.create len $ \outP -> withForeignPtr newIVFP $ \newIVP -> do encryptCTR k (castPtr ivP) (castPtr newIVP) (castPtr outP) (castPtr inP) len let newIV = B.fromForeignPtr newIVFP 0 ivLen return (ct,IV newIV) {-# INLINE ctr #-} unCtr = ctr instance BlockCipher AESKey256 where blockSize = Tagged 128 keyLength = Tagged 256 buildKey bs | B.length bs >= 16 = unsafePerformIO $ B.unsafeUseAsCString bs (\p -> generateKey256 (castPtr p)) | otherwise = Nothing encryptBlock k b = unsafePerformIO $ do B.unsafeUseAsCStringLen b $ \(inP,len) -> do B.create (B.length b) $ \outP -> do encryptECB k (castPtr outP) (castPtr inP) (len`div`blkSize) decryptBlock k b = unsafePerformIO $ do B.unsafeUseAsCStringLen b $ \(inP,len) -> do B.create (B.length b) $ \outP -> do decryptECB k (castPtr outP) (castPtr inP) (len`div`blkSize) ecb = encryptBlock unEcb = decryptBlock ctr k (IV bs) pt = unsafePerformIO $ do B.unsafeUseAsCStringLen pt $ \(inP, len) -> do B.unsafeUseAsCStringLen bs $ \(ivP, ivLen) -> do when (ivLen /= (blockSizeBytes .::. k)) (error "Cipher-AES128: IV wrong length! They type system would have/should have caught this if you didn't use the IV constructor...") newIVFP <- B.mallocByteString ivLen ct <- B.create len $ \outP -> withForeignPtr newIVFP $ \newIVP -> do encryptCTR k (castPtr ivP) (castPtr newIVP) (castPtr outP) (castPtr inP) len let newIV = B.fromForeignPtr newIVFP 0 ivLen return (ct,IV newIV) {-# INLINE ctr #-} unCtr = ctr -- GCM Routines maxTagLen :: Int maxTagLen = 16 data AuthTag = AuthTag { unAuthTag :: ByteString } -- | A tuple of key and precomputed data for use by GCM data GCMCtx k = GCMCtx { gcmkey :: k , gcmpc :: GCMpc } instance Eq AuthTag where (==) = constTimeEq `on` unAuthTag -- A super-class indicating which keys can be used with GCMCtx. class (BlockCipher k, GetExpanded k) => AES_GCM k where instance AES_GCM AESKey128 instance AES_GCM AESKey192 instance AES_GCM AESKey256 -- | Given key material produce a context useful for GCM operations makeGCMCtx :: AES_GCM k => ByteString -> Maybe (GCMCtx k) makeGCMCtx = fmap aesKeyToGCM . buildKey -- | Given an AESKey produce a GCM Context. aesKeyToGCM :: AES_GCM k => k -> GCMCtx k aesKeyToGCM k = GCMCtx k (I.precomputeGCMdata k) -- |Encrypts multiple-of-block-sized input, returning a bytestring and tag. encryptGCM :: AES_GCM k => GCMCtx k -> ByteString -- ^ IV -> ByteString -- ^ Plaintext -> ByteString -- ^ AAD -> (ByteString, AuthTag) encryptGCM key iv pt aad = unsafePerformIO $ do B.unsafeUseAsCString pt $ \ptPtr -> do B.unsafeUseAsCString iv $ \ivPtr -> do B.unsafeUseAsCString aad $ \aadPtr -> do ctPtr <- F.mallocBytes (B.length pt) tagPtr <- F.mallocBytes maxTagLen encryptGCMPtr key (castPtr ivPtr) (B.length iv) (castPtr ptPtr) (B.length pt) (castPtr aadPtr) (B.length aad) (castPtr ctPtr) (castPtr tagPtr) ctBS <- B.unsafePackMallocCStringLen (castPtr ctPtr, B.length pt) tagBS <- B.unsafePackMallocCStringLen (castPtr tagPtr, maxTagLen) return (ctBS, AuthTag tagBS) -- Encrypts multiple-of-block-sized input, filling a pointer with the -- result of [ctr, ct, tag]. encryptGCMPtr :: AES_GCM k => GCMCtx k -> Ptr Word8 -- ^ IV -> Int -- ^ IV Length -> Ptr Word8 -- ^ Plaintext buffer -> Int -- ^ Plaintext length -> Ptr Word8 -- ^ AAD buffer -> Int -- ^ AAD Length -> Ptr Word8 -- ^ ciphertext buffer (at least encBytes large) -> Ptr Word8 -- ^ Tag buffer (always allocated to max length) -> IO () encryptGCMPtr (GCMCtx {..}) ivPtr ivLen ptPtr ptLen aadPtr aadLen ctPtr tagPtr = do I.encryptGCM gcmkey gcmpc (castPtr ivPtr) (fromIntegral ivLen) (castPtr aadPtr) (fromIntegral aadLen) (castPtr ptPtr) (fromIntegral ptLen) (castPtr ctPtr) (castPtr tagPtr) -- | Decrypts multiple-of-block-sized input, returing a bytestring of the -- [ctr, ct, tag]. decryptGCM :: AES_GCM k => GCMCtx k -> ByteString -- ^ IV -> ByteString -- ^ Ciphertext -> ByteString -- ^ AAD -> (ByteString, AuthTag) -- ^ Plaintext and incremented context (or an error) decryptGCM gcmdata iv ct aad = unsafePerformIO $ do let ivLen = B.length iv tagLen = maxTagLen ctLen = B.length ct B.unsafeUseAsCString iv $ \ivPtr -> do B.unsafeUseAsCString ct $ \ctPtr -> do B.unsafeUseAsCString aad $ \aadPtr -> do tagPtr <- F.mallocBytes tagLen ptPtr <- F.mallocBytes ctLen decryptGCM_ptr gcmdata (castPtr ivPtr) ivLen (castPtr ctPtr) ctLen (castPtr aadPtr) (B.length aad) (castPtr ptPtr) (castPtr tagPtr) tagBS <- B.unsafePackMallocCStringLen (castPtr tagPtr,tagLen) ptBS <- B.unsafePackMallocCStringLen (castPtr ptPtr, ctLen) return (ptBS, AuthTag tagBS) decryptGCM_ptr :: AES_GCM k => GCMCtx k -> Ptr Word8 -> Int -- IV -> Ptr Word8 -> Int -- CT -> Ptr Word8 -> Int -- AAD -> Ptr Word8 -- Plaintext -> Ptr Word8 -- Tag -> IO () decryptGCM_ptr (GCMCtx {..}) ivPtr ivLen ctPtr ctLen aadPtr aadLen ptPtr tagPtr = I.decryptGCM gcmkey gcmpc ivPtr (fromIntegral ivLen) aadPtr (fromIntegral aadLen) ctPtr (fromIntegral ctLen) ptPtr tagPtr