    {-# LANGUAGE CPP, MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}

    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Crypto.Nettle.Ciphers
    -- Copyright   :  (c) 2013 Stefan Bühler
    -- License     :  MIT-style (see the file COPYING)
    --
    -- Maintainer  :  stbuehler@web.de
    -- Stability   :  experimental
    -- Portability :  portable
    --
    -- This module exports ciphers supported by nettle:
    --   <http://www.lysator.liu.se/~nisse/nettle/>
    --
    -----------------------------------------------------------------------------

    module Crypto.Nettle.Ciphers (
    	-- * Block ciphers
    	-- | Only block ciphers with a 128-bit 'blockSize' (16 bytes) support the XTS cipher mode.
    	--
    	--  For 'aeadInit' only 'AEAD_GCM' and 'AEAD_CCM' (with 'ccmInitTLS') is supported, and only if the the 'blockSize' is 16 bytes.
    	--  In all other cases 'aeadInit' just returns 'Nothing'.

    	-- ** AES
    	  AES
    	, AES128
    	, AES192
    	, AES256
    	-- ** ARCTWO
    	, ARCTWO
    	, arctwoInitEKB
    	, arctwoInitGutmann
    	-- ** BLOWFISH
    	, BLOWFISH
    	-- ** Camellia
    	, Camellia
    	, Camellia128
    	, Camellia192
    	, Camellia256
    	-- ** CAST-128
    	, CAST128
    	-- ** DES
    	, DES
    	-- ** DES3 (EDE)
    	, DES_EDE3
    	-- ** TWOFISH
    	, TWOFISH
    	-- ** SERPENT
    	, SERPENT
    	-- * Stream ciphers
    	-- ** Nonce ciphers
    	, StreamNonceCipher(..)
    	, streamSetNonceWord64
    	-- ** ARCFOUR
    	, ARCFOUR
    	-- ** Salsa20
    	, 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

    -- internal functions are not camelCase on purpose
    {-# 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 \
    	}

    {-|
    'AES' is the generic cipher context for the AES cipher, supporting key sizes
    of 128, 196 and 256 bits (16, 24 and 32 bytes). The 'blockSize' is always 128 bits (16 bytes).

    'aeadInit' only supports the 'AEAD_GCM' mode for now.
    -}
    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)

    {-|
    'AES128' provides the same interface as 'AES', but is restricted to 128-bit keys.
    -}
    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)

    {-|
    'AES192' provides the same interface as 'AES', but is restricted to 192-bit keys.
    -}
    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)

    {-|
    'AES256' provides the same interface as 'AES', but is restricted to 256-bit keys.
    -}
    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)

    {-|
    'ARCTWO' (also known as the trade marked name RC2) is a block cipher specified in RFC 2268.

    The default 'cipherInit' uses @ekb = bit-length of the key@; 'arctwoInitEKB' allows to specify ekb manually.
    'arctwoInitGutmann' uses @ekb = 1024@ (the maximum).

    'ARCTWO' uses keysizes from 1 to 128 bytes, and uses a 'blockSize' of 64 bits (8 bytes).
    -}
    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)
    {-|
    Initialize cipher with an explicit @ekb@ value (valid values from 1 to 1024, 0 meaning the same as 1024).
    -}
    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
    {-|
    Initialize cipher with @ekb = 1024@.
    -}
    arctwoInitGutmann :: Key ARCTWO -> ARCTWO
    arctwoInitGutmann = nettle_cipherInit' c_arctwo_set_key_gutmann

    {-|
    'BLOWFISH' is a block cipher designed by Bruce Schneier.
    It uses a 'blockSize' of 64 bits (8 bytes), and a variable key size from 64 to 448 bits (8 to 56 bytes).
    -}
    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)

    {-|
    Camellia is a block cipher developed by Mitsubishi and Nippon Telegraph and Telephone Corporation,
    described in RFC3713, and recommended by some Japanese and European authorities as an alternative to AES.
    The algorithm is patented (details see <http://www.lysator.liu.se/~nisse/nettle/nettle.html>).

    Camellia uses a the same 'blockSize' and key sizes as 'AES'.

    'aeadInit' only supports the 'AEAD_GCM' mode for now.
    -}
    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)

    {-|
    'Camellia128' provides the same interface as 'Camellia', but is restricted to 128-bit keys.
    -}
    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)

    {-|
    'Camellia192' provides the same interface as 'Camellia', but is restricted to 192-bit keys.
    -}
    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)

    {-|
    'Camellia256' provides the same interface as 'Camellia', but is restricted to 256-bit keys.
    -}
    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)

    {-|
    'CAST128' is a block cipher specified in RFC 2144. It uses a 64 bit (8 bytes) 'blockSize',
    and a variable key size of 40 up to 128 bits (5 to 16 bytes).
    -}
    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)

    {-|
    'DES' is the old Data Encryption Standard, specified by NIST.
    It uses a 'blockSize' of 64 bits (8 bytes), and a key size of 56 bits.

    The key is given as 8 bytes, as one bit per byte is used as a parity bit.
    The parity bit is ignored by this implementation.
    -}
    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)

    {-|
    'DES_EDE3' uses 3 'DES' keys @k1 || k2 || k3@.
    Encryption first encrypts with k1, then decrypts with k2, then encrypts with k3.

    The 'blockSize' is the same as for 'DES': 64 bits (8 bytes),
    and the keys are simply concatenated, forming a 24 byte key string (with 168 bits actually getting used).
    -}
    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)

    {-|
    'SERPENT' is one of the AES finalists, designed by Ross Anderson, Eli Biham and Lars Knudsen.

    The 'blockSize' is 128 bits (16 bytes), and the valid key sizes are from 128 bits to 256 bits (16 to 32 bytes),
    although smaller bits are just padded with zeroes.

    'aeadInit' only supports the 'AEAD_GCM' mode for now.
    -}
    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)

    {-|
    'TWOFISH' is another AES finalist, designed by Bruce Schneier and others.

    'TWOFISH' uses a the same 'blockSize' and key sizes as 'AES'.

    'aeadInit' only supports the 'AEAD_GCM' mode for now.
    -}
    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)

    {-|
    'ARCFOUR' is a stream cipher, also known under the trade marked name RC4.

    Valid key sizes are from 1 to 256 bytes.
    -}
    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)

    {-|
    'StreamNonceCipher' are special stream ciphers that can encrypt many messages with the same key;
    setting a nonce restarts the cipher.

    A good value for the nonce is a message/packet counter. Usually a nonce should not be reused with the same key.
    -}
    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

    {-|
    Sets a 'Word64' as 8-byte nonce (bigendian encoded)
    -}
    streamSetNonceWord64 :: StreamNonceCipher cipher => cipher -> Word64 -> Maybe cipher
    streamSetNonceWord64 c nonce = streamSetNonce c $ word64BE nonce

    -- set nonce to 0 on init
    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

    -- check nonce length
    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"

    {-|
    'SALSA20' is a fairly recent stream cipher designed by D. J. Bernstein.

    Valid key sizes are 128 and 256 bits (16 and 32 bytes).

    Salsa20 uses a blocksize of 64 bytes internally; if crpyted input isn't aligned to 64 bytes it will
    pad it with 0 and store the encrypted padding to xor with future input data.

    Each message also requires a 8-byte ('Word64') nonce (which is initialized to 0; you can use a message sequence number).
    Don't reuse a nonce with the same key.

    Setting a nonce also resets the remaining padding data.
    -}
    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)

    {-|
    'ESTREAM_SALSA20' is the same as 'SALSA20', but uses only 12 instead of 20 rounds in mixing.
    -}
    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)