{-# LANGUAGE CPP, MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
module Crypto.Nettle.Ciphers (
	
	
	
	
	
	
	  AES
	, AES128
	, AES192
	, AES256
	
	, ARCTWO
	, arctwoInitEKB
	, arctwoInitGutmann
	
	, BLOWFISH
	
	, Camellia
	, Camellia128
	, Camellia192
	, Camellia256
	
	, CAST128
	
	, DES
	
	, DES_EDE3
	
	, TWOFISH
	
	, SERPENT
	
	
	, StreamNonceCipher(..)
	, streamSetNonceWord64
	
	, ARCFOUR
	
	, CHACHA
	
	, SALSA20
	, ESTREAM_SALSA20
	) where
import Crypto.Cipher.Types
import Crypto.Nettle.CCM
import Data.SecureMem
import qualified Data.ByteString as B
import Data.Word (Word64)
import Data.Bits
import Data.Tagged
import Crypto.Nettle.Ciphers.Internal
import Crypto.Nettle.Ciphers.ForeignImports
import Nettle.Utils
{-# ANN module "HLint: ignore Use camelCase" #-}
#define INSTANCE_CIPHER(Typ) \
instance Cipher Typ where \
	{ cipherInit = nettle_cipherInit \
	; cipherName = witness nc_cipherName \
	; cipherKeySize = witness nc_cipherKeySize \
	}
#define INSTANCE_BLOCKCIPHER(Typ) \
INSTANCE_CIPHER(Typ); \
instance BlockCipher Typ where \
	{ blockSize = witness nbc_blockSize \
	; ecbEncrypt = nettle_ecbEncrypt \
	; ecbDecrypt = nettle_ecbDecrypt \
	; cbcEncrypt = nettle_cbcEncrypt \
	; cbcDecrypt = nettle_cbcDecrypt \
	; cfbEncrypt = nettle_cfbEncrypt \
	; cfbDecrypt = nettle_cfbDecrypt \
	; ctrCombine = nettle_ctrCombine \
	; aeadInit AEAD_GCM = nettle_gcm_aeadInit \
	; aeadInit AEAD_CCM = ccmInitTLS \
	; aeadInit _        = \_ _ -> Nothing \
	} ; \
instance AEADModeImpl Typ NettleGCM where \
	{ aeadStateAppendHeader = nettle_gcm_aeadStateAppendHeader \
	; aeadStateEncrypt      = nettle_gcm_aeadStateEncrypt \
	; aeadStateDecrypt      = nettle_gcm_aeadStateDecrypt \
	; aeadStateFinalize     = nettle_gcm_aeadStateFinalize \
	}
#define INSTANCE_STREAMCIPHER(Typ) \
INSTANCE_CIPHER(Typ); \
instance StreamCipher Typ where \
	{ streamCombine = nettle_streamCombine \
	}
#define INSTANCE_STREAMNONCECIPHER(Typ) \
INSTANCE_STREAMCIPHER(Typ); \
instance StreamNonceCipher Typ where \
	{ streamSetNonce = nettle_streamSetNonce \
	; streamNonceSize = witness nsc_nonceSize \
	}
#define INSTANCE_BLOCKEDSTREAMCIPHER(Typ) \
INSTANCE_CIPHER(Typ); \
instance StreamCipher Typ where \
	{ streamCombine = nettle_blockedStreamCombine \
	}
#define INSTANCE_BLOCKEDSTREAMNONCECIPHER(Typ) \
INSTANCE_BLOCKEDSTREAMCIPHER(Typ); \
instance StreamNonceCipher Typ where \
	{ streamSetNonce = nettle_blockedStreamSetNonce \
	; streamNonceSize = witness nbsc_nonceSize \
	}
newtype AES = AES SecureMem
instance NettleCipher AES where
	nc_cipherInit    = Tagged c_hs_aes_init
	nc_cipherName    = Tagged "AES"
	nc_cipherKeySize = Tagged $ KeySizeEnum [16,24,32]
	nc_ctx_size      = Tagged c_hs_aes_ctx_size
	nc_ctx   (AES c) = c
	nc_Ctx           = AES
instance NettleBlockCipher AES where
	nbc_blockSize          = Tagged 16
	nbc_ecb_encrypt        = Tagged c_hs_aes_encrypt
	nbc_ecb_decrypt        = Tagged c_hs_aes_decrypt
	nbc_fun_encrypt        = Tagged p_hs_aes_encrypt
	nbc_fun_decrypt        = Tagged p_hs_aes_decrypt
INSTANCE_BLOCKCIPHER(AES)
newtype AES128 = AES128 SecureMem
instance NettleCipher AES128 where
	nc_cipherInit    = Tagged (\ctx _ key -> c_hs_aes128_init ctx key)
	nc_cipherName    = Tagged "AES-128"
	nc_cipherKeySize = Tagged $ KeySizeFixed 16
	nc_ctx_size      = Tagged c_hs_aes128_ctx_size
	nc_ctx (AES128 c) = c
	nc_Ctx            = AES128
instance NettleBlockCipher AES128 where
	nbc_blockSize          = Tagged 16
	nbc_encrypt_ctx_offset = Tagged c_hs_aes128_ctx_encrypt
	nbc_decrypt_ctx_offset = Tagged c_hs_aes128_ctx_decrypt
	nbc_ecb_encrypt        = Tagged c_aes128_encrypt
	nbc_ecb_decrypt        = Tagged c_aes128_decrypt
	nbc_fun_encrypt        = Tagged p_aes128_encrypt
	nbc_fun_decrypt        = Tagged p_aes128_decrypt
INSTANCE_BLOCKCIPHER(AES128)
newtype AES192 = AES192 SecureMem
instance NettleCipher AES192 where
	nc_cipherInit    = Tagged (\ctx _ key -> c_hs_aes192_init ctx key)
	nc_cipherName    = Tagged "AES-192"
	nc_cipherKeySize = Tagged $ KeySizeFixed 24
	nc_ctx_size      = Tagged c_hs_aes192_ctx_size
	nc_ctx  (AES192 c) = c
	nc_Ctx             = AES192
instance NettleBlockCipher AES192 where
	nbc_blockSize          = Tagged 16
	nbc_encrypt_ctx_offset = Tagged c_hs_aes192_ctx_encrypt
	nbc_decrypt_ctx_offset = Tagged c_hs_aes192_ctx_decrypt
	nbc_ecb_encrypt        = Tagged c_aes192_encrypt
	nbc_ecb_decrypt        = Tagged c_aes192_decrypt
	nbc_fun_encrypt        = Tagged p_aes192_encrypt
	nbc_fun_decrypt        = Tagged p_aes192_decrypt
INSTANCE_BLOCKCIPHER(AES192)
newtype AES256 = AES256 SecureMem
instance NettleCipher AES256 where
	nc_cipherInit    = Tagged (\ctx _ key -> c_hs_aes256_init ctx key)
	nc_cipherName    = Tagged "AES-256"
	nc_cipherKeySize = Tagged $ KeySizeFixed 32
	nc_ctx_size      = Tagged c_hs_aes256_ctx_size
	nc_ctx  (AES256 c) = c
	nc_Ctx             = AES256
instance NettleBlockCipher AES256 where
	nbc_blockSize          = Tagged 16
	nbc_encrypt_ctx_offset = Tagged c_hs_aes256_ctx_encrypt
	nbc_decrypt_ctx_offset = Tagged c_hs_aes256_ctx_decrypt
	nbc_ecb_encrypt        = Tagged c_aes256_encrypt
	nbc_ecb_decrypt        = Tagged c_aes256_decrypt
	nbc_fun_encrypt        = Tagged p_aes256_encrypt
	nbc_fun_decrypt        = Tagged p_aes256_decrypt
INSTANCE_BLOCKCIPHER(AES256)
newtype ARCTWO = ARCTWO SecureMem
instance NettleCipher ARCTWO where
	nc_cipherInit    = Tagged c_arctwo_set_key
	nc_cipherName    = Tagged "ARCTWO"
	nc_cipherKeySize = Tagged $ KeySizeRange 1 128
	nc_ctx_size      = Tagged c_arctwo_ctx_size
	nc_ctx  (ARCTWO c) = c
	nc_Ctx             = ARCTWO
instance NettleBlockCipher ARCTWO where
	nbc_blockSize          = Tagged 8
	nbc_ecb_encrypt        = Tagged c_arctwo_encrypt
	nbc_ecb_decrypt        = Tagged c_arctwo_decrypt
	nbc_fun_encrypt        = Tagged p_arctwo_encrypt
	nbc_fun_decrypt        = Tagged p_arctwo_decrypt
INSTANCE_BLOCKCIPHER(ARCTWO)
arctwoInitEKB :: Key ARCTWO -> Word -> ARCTWO
arctwoInitEKB k ekb = nettle_cipherInit' initfun k where
	initfun ctxptr ksize ptr = c_arctwo_set_key_ekb ctxptr ksize ptr ekb
arctwoInitGutmann :: Key ARCTWO -> ARCTWO
arctwoInitGutmann = nettle_cipherInit' c_arctwo_set_key_gutmann
newtype BLOWFISH = BLOWFISH SecureMem
instance NettleCipher BLOWFISH where
	nc_cipherInit    = Tagged c_blowfish_set_key
	nc_cipherName    = Tagged "BLOWFISH"
	nc_cipherKeySize = Tagged $ KeySizeRange 1 128
	nc_ctx_size      = Tagged c_blowfish_ctx_size
	nc_ctx  (BLOWFISH c) = c
	nc_Ctx             = BLOWFISH
instance NettleBlockCipher BLOWFISH where
	nbc_blockSize          = Tagged 8
	nbc_ecb_encrypt        = Tagged c_blowfish_encrypt
	nbc_ecb_decrypt        = Tagged c_blowfish_decrypt
	nbc_fun_encrypt        = Tagged p_blowfish_encrypt
	nbc_fun_decrypt        = Tagged p_blowfish_decrypt
INSTANCE_BLOCKCIPHER(BLOWFISH)
newtype Camellia = Camellia SecureMem
instance NettleCipher Camellia where
	nc_cipherInit    = Tagged c_hs_camellia_init
	nc_cipherName    = Tagged "Camellia"
	nc_cipherKeySize = Tagged $ KeySizeEnum [16,24,32]
	nc_ctx_size      = Tagged c_hs_camellia_ctx_size
	nc_ctx     (Camellia c) = c
	nc_Ctx             = Camellia
instance NettleBlockCipher Camellia where
	nbc_blockSize          = Tagged 16
	nbc_ecb_encrypt        = Tagged c_hs_camellia_encrypt
	nbc_ecb_decrypt        = Tagged c_hs_camellia_decrypt
	nbc_fun_encrypt        = Tagged p_hs_camellia_encrypt
	nbc_fun_decrypt        = Tagged p_hs_camellia_decrypt
INSTANCE_BLOCKCIPHER(Camellia)
newtype Camellia128 = Camellia128 SecureMem
instance NettleCipher Camellia128 where
	nc_cipherInit    = Tagged (\ctx _ key -> c_hs_camellia128_init ctx key)
	nc_cipherName    = Tagged "Camellia-128"
	nc_cipherKeySize = Tagged $ KeySizeFixed 16
	nc_ctx_size      = Tagged c_hs_camellia128_ctx_size
	nc_ctx  (Camellia128 c) = c
	nc_Ctx             = Camellia128
instance NettleBlockCipher Camellia128 where
	nbc_blockSize          = Tagged 16
	nbc_encrypt_ctx_offset = Tagged c_hs_camellia128_ctx_encrypt
	nbc_decrypt_ctx_offset = Tagged c_hs_camellia128_ctx_decrypt
	nbc_ecb_encrypt        = Tagged c_camellia128_crypt
	nbc_ecb_decrypt        = Tagged c_camellia128_crypt
	nbc_fun_encrypt        = Tagged p_camellia128_crypt
	nbc_fun_decrypt        = Tagged p_camellia128_crypt
INSTANCE_BLOCKCIPHER(Camellia128)
newtype Camellia192 = Camellia192 SecureMem
instance NettleCipher Camellia192 where
	nc_cipherInit    = Tagged (\ctx _ key -> c_hs_camellia192_init ctx key)
	nc_cipherName    = Tagged "Camellia-192"
	nc_cipherKeySize = Tagged $ KeySizeFixed 24
	nc_ctx_size      = Tagged c_hs_camellia192_ctx_size
	nc_ctx  (Camellia192 c) = c
	nc_Ctx             = Camellia192
instance NettleBlockCipher Camellia192 where
	nbc_blockSize          = Tagged 16
	nbc_encrypt_ctx_offset = Tagged c_hs_camellia192_ctx_encrypt
	nbc_decrypt_ctx_offset = Tagged c_hs_camellia192_ctx_decrypt
	nbc_ecb_encrypt        = Tagged c_camellia192_crypt
	nbc_ecb_decrypt        = Tagged c_camellia192_crypt
	nbc_fun_encrypt        = Tagged p_camellia192_crypt
	nbc_fun_decrypt        = Tagged p_camellia192_crypt
INSTANCE_BLOCKCIPHER(Camellia192)
newtype Camellia256 = Camellia256 SecureMem
instance NettleCipher Camellia256 where
	nc_cipherInit    = Tagged (\ctx _ key -> c_hs_camellia256_init ctx key)
	nc_cipherName    = Tagged "Camellia-256"
	nc_cipherKeySize = Tagged $ KeySizeFixed 32
	nc_ctx_size      = Tagged c_hs_camellia256_ctx_size
	nc_ctx  (Camellia256 c) = c
	nc_Ctx             = Camellia256
instance NettleBlockCipher Camellia256 where
	nbc_blockSize          = Tagged 16
	nbc_encrypt_ctx_offset = Tagged c_hs_camellia256_ctx_encrypt
	nbc_decrypt_ctx_offset = Tagged c_hs_camellia256_ctx_decrypt
	nbc_ecb_encrypt        = Tagged c_camellia256_crypt
	nbc_ecb_decrypt        = Tagged c_camellia256_crypt
	nbc_fun_encrypt        = Tagged p_camellia256_crypt
	nbc_fun_decrypt        = Tagged p_camellia256_crypt
INSTANCE_BLOCKCIPHER(Camellia256)
newtype CAST128 = CAST128 SecureMem
instance NettleCipher CAST128 where
	nc_cipherInit    = Tagged c_cast5_set_key
	nc_cipherName    = Tagged "CAST-128"
	nc_cipherKeySize = Tagged $ KeySizeRange 5 16
	nc_ctx_size      = Tagged c_cast128_ctx_size
	nc_ctx  (CAST128 c) = c
	nc_Ctx             = CAST128
instance NettleBlockCipher CAST128 where
	nbc_blockSize          = Tagged 8
	nbc_ecb_encrypt        = Tagged c_cast128_encrypt
	nbc_ecb_decrypt        = Tagged c_cast128_decrypt
	nbc_fun_encrypt        = Tagged p_cast128_encrypt
	nbc_fun_decrypt        = Tagged p_cast128_decrypt
INSTANCE_BLOCKCIPHER(CAST128)
newtype DES = DES SecureMem
instance NettleCipher DES where
	nc_cipherInit    = Tagged $ \ctxptr _ -> c_des_set_key ctxptr
	nc_cipherName    = Tagged "DES"
	nc_cipherKeySize = Tagged $ KeySizeFixed 8
	nc_ctx_size      = Tagged c_des_ctx_size
	nc_ctx  (DES c) = c
	nc_Ctx             = DES
instance NettleBlockCipher DES where
	nbc_blockSize          = Tagged 8
	nbc_ecb_encrypt        = Tagged c_des_encrypt
	nbc_ecb_decrypt        = Tagged c_des_decrypt
	nbc_fun_encrypt        = Tagged p_des_encrypt
	nbc_fun_decrypt        = Tagged p_des_decrypt
INSTANCE_BLOCKCIPHER(DES)
newtype DES_EDE3 = DES_EDE3 SecureMem
instance NettleCipher DES_EDE3 where
	nc_cipherInit    = Tagged $ \ctxptr _ -> c_des3_set_key ctxptr
	nc_cipherName    = Tagged "DES-EDE3"
	nc_cipherKeySize = Tagged $ KeySizeFixed 24
	nc_ctx_size      = Tagged c_des3_ctx_size
	nc_ctx  (DES_EDE3 c) = c
	nc_Ctx             = DES_EDE3
instance NettleBlockCipher DES_EDE3 where
	nbc_blockSize          = Tagged 8
	nbc_ecb_encrypt        = Tagged c_des3_encrypt
	nbc_ecb_decrypt        = Tagged c_des3_decrypt
	nbc_fun_encrypt        = Tagged p_des3_encrypt
	nbc_fun_decrypt        = Tagged p_des3_decrypt
INSTANCE_BLOCKCIPHER(DES_EDE3)
newtype SERPENT = SERPENT SecureMem
instance NettleCipher SERPENT where
	nc_cipherInit    = Tagged c_serpent_set_key
	nc_cipherName    = Tagged "SERPENT"
	nc_cipherKeySize = Tagged $ KeySizeRange 16 32
	nc_ctx_size      = Tagged c_serpent_ctx_size
	nc_ctx  (SERPENT c) = c
	nc_Ctx             = SERPENT
instance NettleBlockCipher SERPENT where
	nbc_blockSize          = Tagged 16
	nbc_ecb_encrypt        = Tagged c_serpent_encrypt
	nbc_ecb_decrypt        = Tagged c_serpent_decrypt
	nbc_fun_encrypt        = Tagged p_serpent_encrypt
	nbc_fun_decrypt        = Tagged p_serpent_decrypt
INSTANCE_BLOCKCIPHER(SERPENT)
newtype TWOFISH = TWOFISH SecureMem
instance NettleCipher TWOFISH where
	nc_cipherInit    = Tagged c_twofish_set_key
	nc_cipherName    = Tagged "TWOFISH"
	nc_cipherKeySize = Tagged $ KeySizeEnum [16,24,32]
	nc_ctx_size      = Tagged c_twofish_ctx_size
	nc_ctx  (TWOFISH c) = c
	nc_Ctx             = TWOFISH
instance NettleBlockCipher TWOFISH where
	nbc_blockSize          = Tagged 16
	nbc_ecb_encrypt        = Tagged c_twofish_encrypt
	nbc_ecb_decrypt        = Tagged c_twofish_decrypt
	nbc_fun_encrypt        = Tagged p_twofish_encrypt
	nbc_fun_decrypt        = Tagged p_twofish_decrypt
INSTANCE_BLOCKCIPHER(TWOFISH)
newtype ARCFOUR = ARCFOUR SecureMem
instance NettleCipher ARCFOUR where
	nc_cipherInit    = Tagged c_arcfour_set_key
	nc_cipherName    = Tagged "ARCFOUR"
	nc_cipherKeySize = Tagged $ KeySizeEnum [16,24,32]
	nc_ctx_size      = Tagged c_arcfour_ctx_size
	nc_ctx  (ARCFOUR c) = c
	nc_Ctx             = ARCFOUR
instance NettleStreamCipher ARCFOUR where
	nsc_streamCombine = Tagged c_arcfour_crypt
INSTANCE_STREAMCIPHER(ARCFOUR)
class StreamCipher cipher => StreamNonceCipher cipher where
	streamNonceSize :: cipher -> KeySizeSpecifier
	streamSetNonce  :: cipher -> B.ByteString -> Maybe cipher
word64BE :: Word64 -> B.ByteString
word64BE value = B.pack $ _work (8::Int) [] value where
	_work 0 r _ = r
	_work n r v = let d = v `shiftR` 8; m = fromIntegral v :: Word8 in _work (n-1) (m:r) d
streamSetNonceWord64 :: StreamNonceCipher cipher => cipher -> Word64 -> Maybe cipher
streamSetNonceWord64 c nonce = streamSetNonce c $ word64BE nonce
wrap_chacha_set_key :: Ptr Word8 -> Word -> Ptr Word8 -> IO ()
wrap_chacha_set_key ctxptr _ keyptr = do
	c_chacha_set_key ctxptr keyptr
	withByteStringPtr (B.replicate 8 0) $ \_ nonceptr ->
		c_chacha_set_nonce ctxptr nonceptr
wrap_chacha_set_nonce :: Ptr Word8 -> Word -> Ptr Word8 -> IO ()
wrap_chacha_set_nonce ctxptr ivlen ivptr = if ivlen == 8 then c_chacha_set_nonce ctxptr ivptr else fail "Invalid nonce length"
newtype CHACHA = CHACHA (SecureMem, B.ByteString)
instance NettleCipher CHACHA where
	nc_cipherInit    = Tagged wrap_chacha_set_key
	nc_cipherName    = Tagged "ChaCha"
	nc_cipherKeySize = Tagged $ KeySizeFixed 32
	nc_ctx_size      = Tagged c_chacha_ctx_size
	nc_ctx (CHACHA (c, _)) = c
	nc_Ctx c           = CHACHA (c, B.empty)
instance NettleBlockedStreamCipher CHACHA where
	nbsc_blockSize     = Tagged 64
	nbsc_IncompleteState (CHACHA (c, _)) inc = CHACHA (c, inc)
	nbsc_incompleteState (CHACHA (_, inc)) = inc
	nbsc_streamCombine = Tagged c_chacha_crypt
	nbsc_nonceSize     = Tagged $ KeySizeFixed 8
	nbsc_setNonce      = Tagged $ Just wrap_chacha_set_nonce
INSTANCE_BLOCKEDSTREAMNONCECIPHER(CHACHA)
wrap_salsa20_set_key :: Ptr Word8 -> Word -> Ptr Word8 -> IO ()
wrap_salsa20_set_key ctxptr keylen keyptr = do
	c_salsa20_set_key ctxptr keylen keyptr
	withByteStringPtr (B.replicate 8 0) $ \_ nonceptr ->
		c_salsa20_set_nonce ctxptr nonceptr
wrap_salsa20_set_nonce :: Ptr Word8 -> Word -> Ptr Word8 -> IO ()
wrap_salsa20_set_nonce ctxptr ivlen ivptr = if ivlen == 8 then c_salsa20_set_nonce ctxptr ivptr else fail "Invalid nonce length"
newtype SALSA20 = SALSA20 (SecureMem, B.ByteString)
instance NettleCipher SALSA20 where
	nc_cipherInit    = Tagged wrap_salsa20_set_key
	nc_cipherName    = Tagged "Salsa20"
	nc_cipherKeySize = Tagged $ KeySizeEnum [16,32]
	nc_ctx_size      = Tagged c_salsa20_ctx_size
	nc_ctx (SALSA20 (c, _)) = c
	nc_Ctx c           = SALSA20 (c, B.empty)
instance NettleBlockedStreamCipher SALSA20 where
	nbsc_blockSize     = Tagged 64
	nbsc_IncompleteState (SALSA20 (c, _)) inc = SALSA20 (c, inc)
	nbsc_incompleteState (SALSA20 (_, inc)) = inc
	nbsc_streamCombine = Tagged c_salsa20_crypt
	nbsc_nonceSize     = Tagged $ KeySizeFixed 8
	nbsc_setNonce      = Tagged $ Just wrap_salsa20_set_nonce
INSTANCE_BLOCKEDSTREAMNONCECIPHER(SALSA20)
newtype ESTREAM_SALSA20 = ESTREAM_SALSA20 (SecureMem, B.ByteString)
instance NettleCipher ESTREAM_SALSA20 where
	nc_cipherInit    = Tagged wrap_salsa20_set_key
	nc_cipherName    = Tagged "eSTREAM-Salsa20"
	nc_cipherKeySize = Tagged $ KeySizeEnum [16,32]
	nc_ctx_size      = Tagged c_salsa20_ctx_size
	nc_ctx (ESTREAM_SALSA20 (c, _)) = c
	nc_Ctx c           = ESTREAM_SALSA20 (c, B.empty)
instance NettleBlockedStreamCipher ESTREAM_SALSA20 where
	nbsc_blockSize     = Tagged 64
	nbsc_IncompleteState (ESTREAM_SALSA20 (c, _)) inc = ESTREAM_SALSA20 (c, inc)
	nbsc_incompleteState (ESTREAM_SALSA20 (_, inc)) = inc
	nbsc_streamCombine = Tagged c_salsa20r12_crypt
	nbsc_nonceSize     = Tagged $ KeySizeFixed 8
	nbsc_setNonce      = Tagged $ Just wrap_salsa20_set_nonce
INSTANCE_BLOCKEDSTREAMNONCECIPHER(ESTREAM_SALSA20)