packages feed

crypton-2.0.0: Crypto/Cipher/Camellia/Primitive.hs

{-# LANGUAGE ForeignFunctionInterface #-}

-- |
-- Module      : Crypto.Cipher.Camellia.Primitive
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : Good
--
-- Camellia with a 128-bit key, over the C in @cbits/crypton_camellia.c@.
--
-- This only cover Camellia 128 bits for now. The API will change once
-- 192 and 256 mode are implemented too.
module Crypto.Cipher.Camellia.Primitive (
    Camellia,
    initCamellia,
    encrypt,
    decrypt,
) where

import Crypto.Error
import Crypto.Internal.ByteArray (ByteArray, ByteArrayAccess, Bytes)
import qualified Crypto.Internal.ByteArray as B
import Crypto.Internal.Compat (unsafeDoIO)
import Data.Word
import Foreign.Ptr (Ptr)

-- | The subkeys of RFC 3713 section 2.2: kw, k and ke, as 26 64-bit words.
newtype Camellia = Camellia Bytes
    deriving (Eq)

scheduleSize :: Int
scheduleSize = 26 * 8

blockBytes :: Int
blockBytes = 16

-- | Initialize a 128-bit key.
initCamellia :: ByteArrayAccess key => key -> CryptoFailable Camellia
initCamellia key
    | B.length key /= 16 = CryptoFailed CryptoError_KeySizeInvalid
    | otherwise =
        CryptoPassed $
            Camellia $
                B.allocAndFreeze scheduleSize $ \ks ->
                    B.withByteArray key $ \k -> c_camellia_init ks k

-- | Encrypt the given input, which has to be a whole number of blocks.
encrypt :: ByteArray ba => Camellia -> ba -> ba
encrypt = run c_camellia_encrypt

-- | Decrypt the given input, which has to be a whole number of blocks.
decrypt :: ByteArray ba => Camellia -> ba -> ba
decrypt = run c_camellia_decrypt

run
    :: ByteArray ba
    => (Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ())
    -> Camellia
    -> ba
    -> ba
run f (Camellia sched) input
    | len `mod` blockBytes /= 0 =
        error $
            "Crypto.Cipher.Camellia: input length must be a multiple of block size (16). Its length is: "
                ++ show len
    | otherwise = unsafeDoIO $
        B.alloc len $ \out ->
            B.withByteArray sched $ \ks ->
                B.withByteArray input $ \inp ->
                    f out ks inp (fromIntegral (len `div` blockBytes))
  where
    len = B.length input

foreign import ccall unsafe "crypton_camellia.h crypton_camellia_init"
    c_camellia_init :: Ptr Word8 -> Ptr Word8 -> IO ()

foreign import ccall unsafe "crypton_camellia.h crypton_camellia_encrypt"
    c_camellia_encrypt :: Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ()

foreign import ccall unsafe "crypton_camellia.h crypton_camellia_decrypt"
    c_camellia_decrypt :: Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ()