{-# 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 (Camellia -> Camellia -> Bool
(Camellia -> Camellia -> Bool)
-> (Camellia -> Camellia -> Bool) -> Eq Camellia
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Camellia -> Camellia -> Bool
== :: Camellia -> Camellia -> Bool
$c/= :: Camellia -> Camellia -> Bool
/= :: Camellia -> Camellia -> Bool
Eq)

scheduleSize :: Int
scheduleSize :: Int
scheduleSize = Int
26 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
8

blockBytes :: Int
blockBytes :: Int
blockBytes = Int
16

-- | Initialize a 128-bit key.
initCamellia :: ByteArrayAccess key => key -> CryptoFailable Camellia
initCamellia :: forall key. ByteArrayAccess key => key -> CryptoFailable Camellia
initCamellia key
key
    | key -> Int
forall ba. ByteArrayAccess ba => ba -> Int
B.length key
key Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
16 = CryptoError -> CryptoFailable Camellia
forall a. CryptoError -> CryptoFailable a
CryptoFailed CryptoError
CryptoError_KeySizeInvalid
    | Bool
otherwise =
        Camellia -> CryptoFailable Camellia
forall a. a -> CryptoFailable a
CryptoPassed (Camellia -> CryptoFailable Camellia)
-> Camellia -> CryptoFailable Camellia
forall a b. (a -> b) -> a -> b
$
            Bytes -> Camellia
Camellia (Bytes -> Camellia) -> Bytes -> Camellia
forall a b. (a -> b) -> a -> b
$
                Int -> (Ptr Word8 -> IO ()) -> Bytes
forall a p. ByteArray a => Int -> (Ptr p -> IO ()) -> a
B.allocAndFreeze Int
scheduleSize ((Ptr Word8 -> IO ()) -> Bytes) -> (Ptr Word8 -> IO ()) -> Bytes
forall a b. (a -> b) -> a -> b
$ \Ptr Word8
ks ->
                    key -> (Ptr Word8 -> IO ()) -> IO ()
forall ba p a. ByteArrayAccess ba => ba -> (Ptr p -> IO a) -> IO a
forall p a. key -> (Ptr p -> IO a) -> IO a
B.withByteArray key
key ((Ptr Word8 -> IO ()) -> IO ()) -> (Ptr Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Ptr Word8
k -> Ptr Word8 -> Ptr Word8 -> IO ()
c_camellia_init Ptr Word8
ks Ptr Word8
k

-- | Encrypt the given input, which has to be a whole number of blocks.
encrypt :: ByteArray ba => Camellia -> ba -> ba
encrypt :: forall ba. ByteArray ba => Camellia -> ba -> ba
encrypt = (Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ())
-> Camellia -> ba -> ba
forall ba.
ByteArray ba =>
(Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ())
-> Camellia -> ba -> ba
run Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ()
c_camellia_encrypt

-- | Decrypt the given input, which has to be a whole number of blocks.
decrypt :: ByteArray ba => Camellia -> ba -> ba
decrypt :: forall ba. ByteArray ba => Camellia -> ba -> ba
decrypt = (Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ())
-> Camellia -> ba -> ba
forall ba.
ByteArray ba =>
(Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ())
-> Camellia -> ba -> ba
run Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ()
c_camellia_decrypt

run
    :: ByteArray ba
    => (Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ())
    -> Camellia
    -> ba
    -> ba
run :: forall ba.
ByteArray ba =>
(Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ())
-> Camellia -> ba -> ba
run Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ()
f (Camellia Bytes
sched) ba
input
    | Int
len Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
blockBytes Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
0 =
        [Char] -> ba
forall a. HasCallStack => [Char] -> a
error ([Char] -> ba) -> [Char] -> ba
forall a b. (a -> b) -> a -> b
$
            [Char]
"Crypto.Cipher.Camellia: input length must be a multiple of block size (16). Its length is: "
                [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Int -> [Char]
forall a. Show a => a -> [Char]
show Int
len
    | Bool
otherwise = IO ba -> ba
forall a. IO a -> a
unsafeDoIO (IO ba -> ba) -> IO ba -> ba
forall a b. (a -> b) -> a -> b
$
        Int -> (Ptr Word8 -> IO ()) -> IO ba
forall ba p. ByteArray ba => Int -> (Ptr p -> IO ()) -> IO ba
B.alloc Int
len ((Ptr Word8 -> IO ()) -> IO ba) -> (Ptr Word8 -> IO ()) -> IO ba
forall a b. (a -> b) -> a -> b
$ \Ptr Word8
out ->
            Bytes -> (Ptr Word8 -> IO ()) -> IO ()
forall ba p a. ByteArrayAccess ba => ba -> (Ptr p -> IO a) -> IO a
forall p a. Bytes -> (Ptr p -> IO a) -> IO a
B.withByteArray Bytes
sched ((Ptr Word8 -> IO ()) -> IO ()) -> (Ptr Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Ptr Word8
ks ->
                ba -> (Ptr Word8 -> IO ()) -> IO ()
forall ba p a. ByteArrayAccess ba => ba -> (Ptr p -> IO a) -> IO a
forall p a. ba -> (Ptr p -> IO a) -> IO a
B.withByteArray ba
input ((Ptr Word8 -> IO ()) -> IO ()) -> (Ptr Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Ptr Word8
inp ->
                    Ptr Word8 -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ()
f Ptr Word8
out Ptr Word8
ks Ptr Word8
inp (Int -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int
len Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
blockBytes))
  where
    len :: Int
len = ba -> Int
forall ba. ByteArrayAccess ba => ba -> Int
B.length ba
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 ()