{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE RecordWildCards #-}
module Crypto.Cipher.AES128
(
AESKey128, AESKey192, AESKey256
, BlockCipher(..), buildKeyIO, zeroIV
, 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
maxTagLen :: Int
maxTagLen = 16
data AuthTag = AuthTag { unAuthTag :: ByteString }
data GCMCtx k = GCMCtx { gcmkey :: k
, gcmpc :: GCMpc
}
instance Eq AuthTag where
(==) = constTimeEq `on` unAuthTag
class (BlockCipher k, GetExpanded k) => AES_GCM k where
instance AES_GCM AESKey128
instance AES_GCM AESKey192
instance AES_GCM AESKey256
makeGCMCtx :: AES_GCM k => ByteString -> Maybe (GCMCtx k)
makeGCMCtx = fmap aesKeyToGCM . buildKey
aesKeyToGCM :: AES_GCM k => k -> GCMCtx k
aesKeyToGCM k = GCMCtx k (I.precomputeGCMdata k)
encryptGCM :: AES_GCM k
=> GCMCtx k
-> ByteString
-> ByteString
-> ByteString
-> (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)
encryptGCMPtr :: AES_GCM k
=> GCMCtx k
-> Ptr Word8
-> Int
-> Ptr Word8
-> Int
-> Ptr Word8
-> Int
-> Ptr Word8
-> Ptr Word8
-> 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)
decryptGCM :: AES_GCM k
=> GCMCtx k
-> ByteString
-> ByteString
-> ByteString
-> (ByteString, AuthTag)
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
-> Ptr Word8 -> Int
-> Ptr Word8 -> Int
-> Ptr Word8
-> Ptr Word8
-> 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