packages feed

cryptocipher-0.3.1: Crypto/Cipher/AES/X86NI.hs

{-# LANGUAGE ForeignFunctionInterface #-}
module Crypto.Cipher.AES.X86NI
    ( encrypt
    , decrypt
    , encryptCBC
    , decryptCBC
    , initKey128
    ) where

import Foreign.Storable
import Foreign.Marshal.Alloc (alloca, allocaBytes)
import Foreign.Ptr
import Foreign.C.String
import Foreign.C.Types
import Data.ByteString (ByteString)
import Data.ByteString.Unsafe (unsafeUseAsCString)
import Data.ByteString.Internal (create, memcpy)
import Data.Bits (shiftR)
import qualified Data.ByteString as B

import System.IO.Unsafe

type IV = ByteString
newtype Key = Key ByteString

instance Storable Key where
    sizeOf _         = 16 * 11 * 2
    alignment _      = 16
    poke ptr (Key b) = unsafeUseAsCString b (\cs -> memcpy (castPtr ptr) (castPtr cs) (16 * 11 * 2))
    peek ptr         = Key `fmap` create (16*11*2) (\bptr -> memcpy bptr (castPtr ptr) (16 * 11 * 2))

foreign import ccall safe "aes.h aes_generate_key128"
        c_aes_generate_key128 :: Ptr Key -> CString -> IO ()

foreign import ccall safe "aes.h aes_encrypt"
        c_aes_encrypt :: CString -> Ptr Key -> CString -> CUInt -> IO ()

foreign import ccall safe "aes.h aes_decrypt"
        c_aes_decrypt :: CString -> Ptr Key -> CString -> CUInt -> IO ()

foreign import ccall safe "aes.h aes_encrypt_cbc"
        c_aes_encrypt_cbc :: CString -> Ptr Key -> CString -> CString -> CUInt -> IO ()

foreign import ccall safe "aes.h aes_decrypt_cbc"
        c_aes_decrypt_cbc :: CString -> Ptr Key -> CString -> CString -> CUInt -> IO ()

withKey :: Key -> (Ptr Key -> IO a) -> IO a
withKey k f = alloca (\ikey -> poke ikey k >> f ikey)

{-# NOINLINE initKey128 #-}
initKey128 :: ByteString -> Key
initKey128 b = unsafePerformIO $ unsafeUseAsCString b (\ikey ->
        alloca (\key -> c_aes_generate_key128 key ikey >> peek key))

{-# NOINLINE encrypt #-}
encrypt :: Key -> ByteString -> ByteString
encrypt key input = unsafePerformIO $ allocateAndMapBlocks input $ \blocks o i ->
    withKey key $ \k -> c_aes_encrypt o k i blocks

{-# NOINLINE decrypt #-}
decrypt :: Key -> ByteString -> ByteString
decrypt key input = unsafePerformIO $ allocateAndMapBlocks input $ \blocks o i ->
    withKey key $ \k -> c_aes_decrypt o k i blocks

{-# NOINLINE encryptCBC #-}
encryptCBC :: Key -> IV -> ByteString -> ByteString
encryptCBC key iv input = unsafePerformIO $ allocateAndMapBlocks input $ \blocks o i ->
    withKey key $ \k -> unsafeUseAsCString iv $ \ivptr -> c_aes_encrypt_cbc o k ivptr i blocks

{-# NOINLINE decryptCBC #-}
decryptCBC :: Key -> IV -> ByteString -> ByteString
decryptCBC key iv input = unsafePerformIO $ allocateAndMapBlocks input $ \blocks o i ->
    withKey key $ \k -> unsafeUseAsCString iv $ \ivptr -> c_aes_decrypt_cbc o k ivptr i blocks

allocateAndMapBlocks :: ByteString -> (CUInt -> Ptr CChar -> CString -> IO ()) -> IO ByteString
allocateAndMapBlocks input f = allocaBytes len (\output -> do unsafeUseAsCString input (f nbBlocks output)
                                                              B.packCStringLen (output, len))
    where len = B.length input
          nbBlocks = fromIntegral (len `shiftR` 4)