diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
 # haskell-nettle
 
-This is the source repository for the "nettle" cabal package, which is a safe binding to the [nettle](http://www.lysator.liu.se/~nisse/nettle/nettle.html) library (tested with 2.7.1, might work with 2.5, does NOT WORK with 3.0).
+This is the source repository for the "nettle" cabal package, which is a safe binding to the [nettle](http://www.lysator.liu.se/~nisse/nettle/nettle.html) library (tested with 3.1.1, might work with 3.0, does NOT WORK with 2.x).
 
-The binding supports all hash functions, cipher functions, cipher modes and keyed hash functions included in nettle (additionally the AEAD-CCM cipher mode is implemented in pure haskell).
+The binding supports all hash functions, cipher functions, cipher modes and keyed hash functions (apart from Poly1305-AES) included in nettle (additionally the AEAD-CCM cipher mode is implemented in pure haskell).
 
 Not included are the PBKDF2 key derivation functions, the public-key algorithms (RSA, DSA, elliptic curves, ECDSA), the pseudo-random generators (lagged Fibonacci and Yarrow), and the base64/base16 encoding/decoding functions.
 
diff --git a/nettle.cabal b/nettle.cabal
--- a/nettle.cabal
+++ b/nettle.cabal
@@ -1,9 +1,9 @@
 Name:                nettle
-Version:             0.1.1
+Version:             0.2.0
 Synopsis:            safe nettle binding
 Description:
   safe binding for the nettle (<http://www.lysator.liu.se/~nisse/nettle/nettle.html>) library.
-  Tested with nettle-2.7.1, might work with 2.5, does NOT WORK with 3.0.
+  Tested with 3.1.1, might work with 3.0, does NOT WORK with 2.x.
 License:             MIT
 License-file:        COPYING
 Copyright:           Stefan Bühler <stbuehler@web.de>
@@ -32,7 +32,8 @@
                    , tagged
                    , securemem >= 0.1.2
                    , crypto-cipher-types >= 0.0.3 && < 0.1
-  Exposed-modules:   Crypto.Nettle.Ciphers
+  Exposed-modules:   Crypto.Nettle.ChaChaPoly1305
+                     Crypto.Nettle.Ciphers
                      Crypto.Nettle.CCM
                      Crypto.Nettle.Hash
                      Crypto.Nettle.KeyedHash
@@ -43,7 +44,7 @@
                      Crypto.Nettle.Hash.ForeignImports
                      Crypto.Nettle.Hash.Types
                      Nettle.Utils
-  ghc-options:       -Wall -optc-O3 -fno-cse
+  ghc-options:       -Wall -optc-O3 -fno-cse -fno-warn-tabs
   include-dirs:      src
   C-sources:         src/nettle-ciphers.c
   if flag(UsePkgConfig)
@@ -64,6 +65,7 @@
                    , crypto-cipher-types
                    , crypto-cipher-tests
                    , nettle
+  ghc-options: -fno-warn-tabs
 
 Test-Suite test-hashes
   type:              exitcode-stdio-1.0
@@ -77,6 +79,7 @@
                    , HUnit
                    , test-framework-hunit
                    , nettle
+  ghc-options: -fno-warn-tabs
 
 Test-Suite test-hmac
   type:              exitcode-stdio-1.0
@@ -90,6 +93,7 @@
                    , HUnit
                    , test-framework-hunit
                    , nettle
+  ghc-options: -fno-warn-tabs
 
 Test-Suite test-umac
   type:              exitcode-stdio-1.0
@@ -103,6 +107,7 @@
                    , HUnit
                    , test-framework-hunit
                    , nettle
+  ghc-options: -fno-warn-tabs
 
 source-repository head
   type:     git
diff --git a/src/Crypto/Nettle/ChaChaPoly1305.hs b/src/Crypto/Nettle/ChaChaPoly1305.hs
new file mode 100644
--- /dev/null
+++ b/src/Crypto/Nettle/ChaChaPoly1305.hs
@@ -0,0 +1,88 @@
+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Crypto.Nettle.ChaChaPoly1305
+-- Copyright   :  (c) 2013 Stefan Bühler
+-- License     :  MIT-style (see the file COPYING)
+-- 
+-- Maintainer  :  stbuehler@web.de
+-- Stability   :  experimental
+-- Portability :  portable
+--
+-- This module exports the ChaCha-Poly1305 AEAD cipher supported by nettle:
+--   <http://www.lysator.liu.se/~nisse/nettle/>
+--
+-- Both ChaCha (the underlying cipher) and Poly1305 (the keyed hash) were
+-- designed by D. J. Bernstein.
+--
+-----------------------------------------------------------------------------
+
+module Crypto.Nettle.ChaChaPoly1305 (
+	-- * ChaCha-Poly1305
+	--
+	-- No streaming interface is provided, as this basically violates the
+	-- spirit of the "AEAD-should-be-simple-to-use" concept - you only can
+	-- use the decrypted data after it got successfully verified.
+
+	  chaChaPoly1305Encrypt
+	, chaChaPoly1305Decrypt
+	) where
+
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Internal as B
+import Data.SecureMem
+
+import Crypto.Nettle.Ciphers.ForeignImports
+import Nettle.Utils
+
+{-|
+Encrypt plain text and create a verification tag for the encrypted text and some additional data.
+@key@ and @nonce@ must not be reused together.
+The returned tag is 16 bytes long, but may be shortened for verification (loosing security).
+-}
+chaChaPoly1305Encrypt
+	:: B.ByteString                 -- ^ @key@ (must be 32 bytes)
+	-> B.ByteString                 -- ^ @nonce@ (must be 12 bytes)
+	-> B.ByteString                 -- ^ @aad@ additional data to be verified
+	-> B.ByteString                 -- ^ @plain@ data to encrypt
+	-> (B.ByteString, B.ByteString) -- ^ returns (@cipher@, @tag@) ciphertext and verification tag
+chaChaPoly1305Encrypt key nonce aad plain = unsafeDupablePerformIO $ do
+	ctx <- allocateSecureMem c_chacha_poly1305_ctx_size
+	tag <- B.create 16 (\_ -> return ())
+	cipher <- B.create (B.length plain) (\_ -> return ())
+	withByteStringPtr plain $ \psize pptr ->
+		withByteStringPtr aad $ \aadsize aadptr ->
+		withByteStringPtr cipher $ \_ cipherptr ->
+		withByteStringPtr tag $ \_ tagptr ->
+		withSecureMemPtr ctx $ \ctxptr ->
+		withSecureMemPtrSz (toSecureMem key) $ \ksize kptr -> if ksize /= 32 then error "Invalid key length" else
+		withSecureMemPtrSz (toSecureMem nonce) $ \nsize nptr -> if nsize /= 12 then error "Invalid nonce length" else do
+		c_chacha_poly1305_set_key ctxptr kptr
+		c_chacha_poly1305_set_nonce ctxptr nptr
+		c_chacha_poly1305_update ctxptr aadsize aadptr
+		c_chacha_poly1305_encrypt ctxptr psize cipherptr pptr
+		c_chacha_poly1305_digest ctxptr 16 tagptr
+	return (cipher, tag)
+
+{-|
+Decrypt cipher text and verify a (possible shortened) tag for the encrypted text and some additional data.
+@key@ and @nonce@ must not be reused together.
+-}
+chaChaPoly1305Decrypt :: B.ByteString -> B.ByteString -> B.ByteString -> B.ByteString -> B.ByteString -> Maybe B.ByteString
+chaChaPoly1305Decrypt key nonce aad cipher verifytag = unsafeDupablePerformIO $ do
+	ctx <- allocateSecureMem c_chacha_poly1305_ctx_size
+	tag <- B.create 16 (\_ -> return ())
+	plain <- B.create (B.length cipher) (\_ -> return ())
+	withByteStringPtr cipher $ \psize pptr ->
+		withByteStringPtr aad $ \aadsize aadptr ->
+		withByteStringPtr plain $ \_ plainptr ->
+		withByteStringPtr tag $ \_ tagptr ->
+		withSecureMemPtr ctx $ \ctxptr ->
+		withSecureMemPtrSz (toSecureMem key) $ \ksize kptr -> if ksize /= 32 then error "Invalid key length" else
+		withSecureMemPtrSz (toSecureMem nonce) $ \nsize nptr -> if nsize /= 12 then error "Invalid nonce length" else do
+		c_chacha_poly1305_set_key ctxptr kptr
+		c_chacha_poly1305_set_nonce ctxptr nptr
+		c_chacha_poly1305_update ctxptr aadsize aadptr
+		c_chacha_poly1305_decrypt ctxptr psize plainptr pptr
+		c_chacha_poly1305_digest ctxptr 16 tagptr
+	if B.take (B.length verifytag) tag == verifytag then return $ Just plain else return Nothing
diff --git a/src/Crypto/Nettle/Ciphers.hs b/src/Crypto/Nettle/Ciphers.hs
--- a/src/Crypto/Nettle/Ciphers.hs
+++ b/src/Crypto/Nettle/Ciphers.hs
@@ -54,6 +54,8 @@
 	, streamSetNonceWord64
 	-- ** ARCFOUR
 	, ARCFOUR
+	-- ** ChaCha
+	, CHACHA
 	-- ** Salsa20
 	, SALSA20
 	, ESTREAM_SALSA20
@@ -125,7 +127,6 @@
 	; 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).
@@ -142,35 +143,32 @@
 	nc_Ctx           = AES
 instance NettleBlockCipher AES where
 	nbc_blockSize          = Tagged 16
-	nbc_encrypt_ctx_offset = Tagged c_hs_aes_ctx_encrypt
-	nbc_decrypt_ctx_offset = Tagged c_hs_aes_ctx_decrypt
-	nbc_ecb_encrypt        = Tagged c_aes_encrypt
-	nbc_ecb_decrypt        = Tagged c_aes_decrypt
-	nbc_fun_encrypt        = Tagged p_aes_encrypt
-	nbc_fun_decrypt        = Tagged p_aes_decrypt
+	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 c_hs_aes_init
+	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_aes_ctx_size
+	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_aes_ctx_encrypt
-	nbc_decrypt_ctx_offset = Tagged c_hs_aes_ctx_decrypt
-	nbc_ecb_encrypt        = Tagged c_aes_encrypt
-	nbc_ecb_decrypt        = Tagged c_aes_decrypt
-	nbc_fun_encrypt        = Tagged p_aes_encrypt
-	nbc_fun_decrypt        = Tagged p_aes_decrypt
+	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)
 
@@ -180,20 +178,20 @@
 -}
 newtype AES192 = AES192 SecureMem
 instance NettleCipher AES192 where
-	nc_cipherInit    = Tagged c_hs_aes_init
+	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_aes_ctx_size
+	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_aes_ctx_encrypt
-	nbc_decrypt_ctx_offset = Tagged c_hs_aes_ctx_decrypt
-	nbc_ecb_encrypt        = Tagged c_aes_encrypt
-	nbc_ecb_decrypt        = Tagged c_aes_decrypt
-	nbc_fun_encrypt        = Tagged p_aes_encrypt
-	nbc_fun_decrypt        = Tagged p_aes_decrypt
+	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)
 
@@ -203,20 +201,20 @@
 -}
 newtype AES256 = AES256 SecureMem
 instance NettleCipher AES256 where
-	nc_cipherInit    = Tagged c_hs_aes_init
+	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_aes_ctx_size
+	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_aes_ctx_encrypt
-	nbc_decrypt_ctx_offset = Tagged c_hs_aes_ctx_decrypt
-	nbc_ecb_encrypt        = Tagged c_aes_encrypt
-	nbc_ecb_decrypt        = Tagged c_aes_decrypt
-	nbc_fun_encrypt        = Tagged p_aes_encrypt
-	nbc_fun_decrypt        = Tagged p_aes_decrypt
+	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)
 
@@ -297,12 +295,10 @@
 	nc_Ctx             = Camellia
 instance NettleBlockCipher Camellia where
 	nbc_blockSize          = Tagged 16
-	nbc_encrypt_ctx_offset = Tagged c_hs_camellia_ctx_encrypt
-	nbc_decrypt_ctx_offset = Tagged c_hs_camellia_ctx_decrypt
-	nbc_ecb_encrypt        = Tagged c_camellia_crypt
-	nbc_ecb_decrypt        = Tagged c_camellia_crypt
-	nbc_fun_encrypt        = Tagged p_camellia_crypt
-	nbc_fun_decrypt        = Tagged p_camellia_crypt
+	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)
 
@@ -311,20 +307,20 @@
 -}
 newtype Camellia128 = Camellia128 SecureMem
 instance NettleCipher Camellia128 where
-	nc_cipherInit    = Tagged c_hs_camellia_init
+	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_camellia_ctx_size
+	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_camellia_ctx_encrypt
-	nbc_decrypt_ctx_offset = Tagged c_hs_camellia_ctx_decrypt
-	nbc_ecb_encrypt        = Tagged c_camellia_crypt
-	nbc_ecb_decrypt        = Tagged c_camellia_crypt
-	nbc_fun_encrypt        = Tagged p_camellia_crypt
-	nbc_fun_decrypt        = Tagged p_camellia_crypt
+	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)
 
@@ -333,20 +329,20 @@
 -}
 newtype Camellia192 = Camellia192 SecureMem
 instance NettleCipher Camellia192 where
-	nc_cipherInit    = Tagged c_hs_camellia_init
+	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_camellia_ctx_size
+	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_camellia_ctx_encrypt
-	nbc_decrypt_ctx_offset = Tagged c_hs_camellia_ctx_decrypt
-	nbc_ecb_encrypt        = Tagged c_camellia_crypt
-	nbc_ecb_decrypt        = Tagged c_camellia_crypt
-	nbc_fun_encrypt        = Tagged p_camellia_crypt
-	nbc_fun_decrypt        = Tagged p_camellia_crypt
+	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)
 
@@ -355,20 +351,20 @@
 -}
 newtype Camellia256 = Camellia256 SecureMem
 instance NettleCipher Camellia256 where
-	nc_cipherInit    = Tagged c_hs_camellia_init
+	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_camellia_ctx_size
+	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_camellia_ctx_encrypt
-	nbc_decrypt_ctx_offset = Tagged c_hs_camellia_ctx_decrypt
-	nbc_ecb_encrypt        = Tagged c_camellia_crypt
-	nbc_ecb_decrypt        = Tagged c_camellia_crypt
-	nbc_fun_encrypt        = Tagged p_camellia_crypt
-	nbc_fun_decrypt        = Tagged p_camellia_crypt
+	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)
 
@@ -378,7 +374,7 @@
 -}
 newtype CAST128 = CAST128 SecureMem
 instance NettleCipher CAST128 where
-	nc_cipherInit    = Tagged c_cast128_set_key
+	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
@@ -529,15 +525,58 @@
 streamSetNonceWord64 c nonce = streamSetNonce c $ word64BE nonce
 
 -- set nonce to 0 on init
+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
+
+-- check nonce length
+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"
+
+{-|
+'CHACHA' is a variant of the 'SALSA20' stream cipher, both designed by D. J. Bernstein.
+
+Key size is 256 bits (32 bytes).
+
+'CHACHA' works similar to 'SALSA20'; it could theoretically also support 128-bit keys, but there is no need for it as they share the same performance.
+
+ChaCha 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 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)
+
+-- 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_iv ctxptr nonceptr
+		c_salsa20_set_nonce ctxptr nonceptr
 
 -- check nonce length
-wrap_salsa20_set_iv :: Ptr Word8 -> Word -> Ptr Word8 -> IO ()
-wrap_salsa20_set_iv ctxptr ivlen ivptr = if ivlen == 8 then c_salsa20_set_iv ctxptr ivptr else fail "Invalid 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.
@@ -566,7 +605,7 @@
 	nbsc_incompleteState (SALSA20 (_, inc)) = inc
 	nbsc_streamCombine = Tagged c_salsa20_crypt
 	nbsc_nonceSize     = Tagged $ KeySizeFixed 8
-	nbsc_setNonce      = Tagged $ Just wrap_salsa20_set_iv
+	nbsc_setNonce      = Tagged $ Just wrap_salsa20_set_nonce
 INSTANCE_BLOCKEDSTREAMNONCECIPHER(SALSA20)
 
 
@@ -587,5 +626,5 @@
 	nbsc_incompleteState (ESTREAM_SALSA20 (_, inc)) = inc
 	nbsc_streamCombine = Tagged c_salsa20r12_crypt
 	nbsc_nonceSize     = Tagged $ KeySizeFixed 8
-	nbsc_setNonce      = Tagged $ Just wrap_salsa20_set_iv
+	nbsc_setNonce      = Tagged $ Just wrap_salsa20_set_nonce
 INSTANCE_BLOCKEDSTREAMNONCECIPHER(ESTREAM_SALSA20)
diff --git a/src/Crypto/Nettle/Ciphers/ForeignImports.hsc b/src/Crypto/Nettle/Ciphers/ForeignImports.hsc
--- a/src/Crypto/Nettle/Ciphers/ForeignImports.hsc
+++ b/src/Crypto/Nettle/Ciphers/ForeignImports.hsc
@@ -23,14 +23,39 @@
 	, c_gcm_digest
 
 	, c_hs_aes_ctx_size
-	, c_hs_aes_ctx_encrypt
-	, c_hs_aes_ctx_decrypt
 	, c_hs_aes_init
-	, c_aes_encrypt
-	, p_aes_encrypt
-	, c_aes_decrypt
-	, p_aes_decrypt
+	, c_hs_aes_encrypt
+	, p_hs_aes_encrypt
+	, c_hs_aes_decrypt
+	, p_hs_aes_decrypt
 
+	, c_hs_aes128_ctx_size
+	, c_hs_aes128_ctx_encrypt
+	, c_hs_aes128_ctx_decrypt
+	, c_hs_aes128_init
+	, c_aes128_encrypt
+	, p_aes128_encrypt
+	, c_aes128_decrypt
+	, p_aes128_decrypt
+
+	, c_hs_aes192_ctx_size
+	, c_hs_aes192_ctx_encrypt
+	, c_hs_aes192_ctx_decrypt
+	, c_hs_aes192_init
+	, c_aes192_encrypt
+	, p_aes192_encrypt
+	, c_aes192_decrypt
+	, p_aes192_decrypt
+
+	, c_hs_aes256_ctx_size
+	, c_hs_aes256_ctx_encrypt
+	, c_hs_aes256_ctx_decrypt
+	, c_hs_aes256_init
+	, c_aes256_encrypt
+	, p_aes256_encrypt
+	, c_aes256_decrypt
+	, p_aes256_decrypt
+
 	, c_arctwo_ctx_size
 	, c_arctwo_set_key
 	, c_arctwo_set_key_ekb
@@ -48,14 +73,35 @@
 	, p_blowfish_decrypt
 
 	, c_hs_camellia_ctx_size
-	, c_hs_camellia_ctx_encrypt
-	, c_hs_camellia_ctx_decrypt
 	, c_hs_camellia_init
-	, c_camellia_crypt
-	, p_camellia_crypt
+	, c_hs_camellia_encrypt
+	, p_hs_camellia_encrypt
+	, c_hs_camellia_decrypt
+	, p_hs_camellia_decrypt
 
+	, c_hs_camellia128_ctx_size
+	, c_hs_camellia128_ctx_encrypt
+	, c_hs_camellia128_ctx_decrypt
+	, c_hs_camellia128_init
+	, c_camellia128_crypt
+	, p_camellia128_crypt
+
+	, c_hs_camellia192_ctx_size
+	, c_hs_camellia192_ctx_encrypt
+	, c_hs_camellia192_ctx_decrypt
+	, c_hs_camellia192_init
+	, c_camellia192_crypt
+	, p_camellia192_crypt
+
+	, c_hs_camellia256_ctx_size
+	, c_hs_camellia256_ctx_encrypt
+	, c_hs_camellia256_ctx_decrypt
+	, c_hs_camellia256_init
+	, c_camellia256_crypt
+	, p_camellia256_crypt
+
 	, c_cast128_ctx_size
-	, c_cast128_set_key
+	, c_cast5_set_key
 	, c_cast128_encrypt
 	, p_cast128_encrypt
 	, c_cast128_decrypt
@@ -93,11 +139,24 @@
 	, c_arcfour_set_key
 	, c_arcfour_crypt
 
+	, c_chacha_ctx_size
+	, c_chacha_set_key
+	, c_chacha_set_nonce
+	, c_chacha_crypt
+
 	, c_salsa20_ctx_size
 	, c_salsa20_set_key
-	, c_salsa20_set_iv
+	, c_salsa20_set_nonce
 	, c_salsa20_crypt
 	, c_salsa20r12_crypt
+
+	, c_chacha_poly1305_ctx_size
+	, c_chacha_poly1305_set_key
+	, c_chacha_poly1305_set_nonce
+	, c_chacha_poly1305_update
+	, c_chacha_poly1305_encrypt
+	, c_chacha_poly1305_decrypt
+	, c_chacha_poly1305_digest
 	) where
 
 import Nettle.Utils
@@ -145,22 +204,68 @@
 
 c_hs_aes_ctx_size :: Int
 c_hs_aes_ctx_size = #{size struct hs_aes_ctx}
-c_hs_aes_ctx_encrypt :: Ptr Word8 -> Ptr Word8
-c_hs_aes_ctx_encrypt = #ptr struct hs_aes_ctx, encrypt
-c_hs_aes_ctx_decrypt :: Ptr Word8 -> Ptr Word8
-c_hs_aes_ctx_decrypt = #ptr struct hs_aes_ctx, decrypt
 foreign import ccall unsafe "hs_nettle_aes_init"
 	c_hs_aes_init :: Ptr Word8 -> Word -> Ptr Word8 -> IO ()
-foreign import ccall unsafe "nettle_aes_encrypt"
-	c_aes_encrypt :: NettleCryptFunc
-foreign import ccall unsafe "&nettle_aes_encrypt"
-	p_aes_encrypt :: FunPtr NettleCryptFunc
-foreign import ccall unsafe "nettle_aes_decrypt"
-	c_aes_decrypt :: NettleCryptFunc
-foreign import ccall unsafe "&nettle_aes_decrypt"
-	p_aes_decrypt :: FunPtr NettleCryptFunc
+foreign import ccall unsafe "hs_nettle_aes_encrypt"
+	c_hs_aes_encrypt :: NettleCryptFunc
+foreign import ccall unsafe "&hs_nettle_aes_encrypt"
+	p_hs_aes_encrypt :: FunPtr NettleCryptFunc
+foreign import ccall unsafe "hs_nettle_aes_decrypt"
+	c_hs_aes_decrypt :: NettleCryptFunc
+foreign import ccall unsafe "&hs_nettle_aes_decrypt"
+	p_hs_aes_decrypt :: FunPtr NettleCryptFunc
 
+c_hs_aes128_ctx_size :: Int
+c_hs_aes128_ctx_size = #{size struct hs_aes128_ctx}
+c_hs_aes128_ctx_encrypt :: Ptr Word8 -> Ptr Word8
+c_hs_aes128_ctx_encrypt = #ptr struct hs_aes128_ctx, encrypt
+c_hs_aes128_ctx_decrypt :: Ptr Word8 -> Ptr Word8
+c_hs_aes128_ctx_decrypt = #ptr struct hs_aes128_ctx, decrypt
+foreign import ccall unsafe "hs_nettle_aes128_init"
+	c_hs_aes128_init :: Ptr Word8 -> Ptr Word8 -> IO ()
+foreign import ccall unsafe "nettle_aes128_encrypt"
+	c_aes128_encrypt :: NettleCryptFunc
+foreign import ccall unsafe "&nettle_aes128_encrypt"
+	p_aes128_encrypt :: FunPtr NettleCryptFunc
+foreign import ccall unsafe "nettle_aes128_decrypt"
+	c_aes128_decrypt :: NettleCryptFunc
+foreign import ccall unsafe "&nettle_aes128_decrypt"
+	p_aes128_decrypt :: FunPtr NettleCryptFunc
 
+c_hs_aes192_ctx_size :: Int
+c_hs_aes192_ctx_size = #{size struct hs_aes192_ctx}
+c_hs_aes192_ctx_encrypt :: Ptr Word8 -> Ptr Word8
+c_hs_aes192_ctx_encrypt = #ptr struct hs_aes192_ctx, encrypt
+c_hs_aes192_ctx_decrypt :: Ptr Word8 -> Ptr Word8
+c_hs_aes192_ctx_decrypt = #ptr struct hs_aes192_ctx, decrypt
+foreign import ccall unsafe "hs_nettle_aes192_init"
+	c_hs_aes192_init :: Ptr Word8 -> Ptr Word8 -> IO ()
+foreign import ccall unsafe "nettle_aes192_encrypt"
+	c_aes192_encrypt :: NettleCryptFunc
+foreign import ccall unsafe "&nettle_aes192_encrypt"
+	p_aes192_encrypt :: FunPtr NettleCryptFunc
+foreign import ccall unsafe "nettle_aes192_decrypt"
+	c_aes192_decrypt :: NettleCryptFunc
+foreign import ccall unsafe "&nettle_aes192_decrypt"
+	p_aes192_decrypt :: FunPtr NettleCryptFunc
+
+c_hs_aes256_ctx_size :: Int
+c_hs_aes256_ctx_size = #{size struct hs_aes256_ctx}
+c_hs_aes256_ctx_encrypt :: Ptr Word8 -> Ptr Word8
+c_hs_aes256_ctx_encrypt = #ptr struct hs_aes256_ctx, encrypt
+c_hs_aes256_ctx_decrypt :: Ptr Word8 -> Ptr Word8
+c_hs_aes256_ctx_decrypt = #ptr struct hs_aes256_ctx, decrypt
+foreign import ccall unsafe "hs_nettle_aes256_init"
+	c_hs_aes256_init :: Ptr Word8 -> Ptr Word8 -> IO ()
+foreign import ccall unsafe "nettle_aes256_encrypt"
+	c_aes256_encrypt :: NettleCryptFunc
+foreign import ccall unsafe "&nettle_aes256_encrypt"
+	p_aes256_encrypt :: FunPtr NettleCryptFunc
+foreign import ccall unsafe "nettle_aes256_decrypt"
+	c_aes256_decrypt :: NettleCryptFunc
+foreign import ccall unsafe "&nettle_aes256_decrypt"
+	p_aes256_decrypt :: FunPtr NettleCryptFunc
+
 c_arctwo_ctx_size :: Int
 c_arctwo_ctx_size = #{size struct arctwo_ctx}
 foreign import ccall unsafe "nettle_arctwo_set_key"
@@ -193,21 +298,62 @@
 
 c_hs_camellia_ctx_size :: Int
 c_hs_camellia_ctx_size = #{size struct hs_camellia_ctx}
-c_hs_camellia_ctx_encrypt :: Ptr Word8 -> Ptr Word8
-c_hs_camellia_ctx_encrypt = #ptr struct hs_camellia_ctx, encrypt
-c_hs_camellia_ctx_decrypt :: Ptr Word8 -> Ptr Word8
-c_hs_camellia_ctx_decrypt = #ptr struct hs_camellia_ctx, decrypt
 foreign import ccall unsafe "hs_nettle_camellia_init"
 	c_hs_camellia_init :: Ptr Word8 -> Word -> Ptr Word8 -> IO ()
-foreign import ccall unsafe "nettle_camellia_crypt"
-	c_camellia_crypt :: NettleCryptFunc
-foreign import ccall unsafe "&nettle_camellia_crypt"
-	p_camellia_crypt :: FunPtr NettleCryptFunc
+foreign import ccall unsafe "hs_nettle_camellia_encrypt"
+	c_hs_camellia_encrypt :: NettleCryptFunc
+foreign import ccall unsafe "&hs_nettle_camellia_encrypt"
+	p_hs_camellia_encrypt :: FunPtr NettleCryptFunc
+foreign import ccall unsafe "hs_nettle_camellia_decrypt"
+	c_hs_camellia_decrypt :: NettleCryptFunc
+foreign import ccall unsafe "&hs_nettle_camellia_decrypt"
+	p_hs_camellia_decrypt :: FunPtr NettleCryptFunc
 
+c_hs_camellia128_ctx_size :: Int
+c_hs_camellia128_ctx_size = #{size struct hs_camellia128_ctx}
+c_hs_camellia128_ctx_encrypt :: Ptr Word8 -> Ptr Word8
+c_hs_camellia128_ctx_encrypt = #ptr struct hs_camellia128_ctx, encrypt
+c_hs_camellia128_ctx_decrypt :: Ptr Word8 -> Ptr Word8
+c_hs_camellia128_ctx_decrypt = #ptr struct hs_camellia128_ctx, decrypt
+foreign import ccall unsafe "hs_nettle_camellia128_init"
+	c_hs_camellia128_init :: Ptr Word8 -> Ptr Word8 -> IO ()
+foreign import ccall unsafe "nettle_camellia128_crypt"
+	c_camellia128_crypt :: NettleCryptFunc
+foreign import ccall unsafe "&nettle_camellia128_crypt"
+	p_camellia128_crypt :: FunPtr NettleCryptFunc
+
+c_hs_camellia192_ctx_size :: Int
+c_hs_camellia192_ctx_size = #{size struct hs_camellia192_ctx}
+c_hs_camellia192_ctx_encrypt :: Ptr Word8 -> Ptr Word8
+c_hs_camellia192_ctx_encrypt = #ptr struct hs_camellia192_ctx, encrypt
+c_hs_camellia192_ctx_decrypt :: Ptr Word8 -> Ptr Word8
+c_hs_camellia192_ctx_decrypt = #ptr struct hs_camellia192_ctx, decrypt
+foreign import ccall unsafe "hs_nettle_camellia192_init"
+	c_hs_camellia192_init :: Ptr Word8 -> Ptr Word8 -> IO ()
+-- 192 and 256 bit variants use same crypt function
+foreign import ccall unsafe "nettle_camellia256_crypt"
+	c_camellia192_crypt :: NettleCryptFunc
+foreign import ccall unsafe "&nettle_camellia256_crypt"
+	p_camellia192_crypt :: FunPtr NettleCryptFunc
+
+c_hs_camellia256_ctx_size :: Int
+c_hs_camellia256_ctx_size = #{size struct hs_camellia256_ctx}
+c_hs_camellia256_ctx_encrypt :: Ptr Word8 -> Ptr Word8
+c_hs_camellia256_ctx_encrypt = #ptr struct hs_camellia256_ctx, encrypt
+c_hs_camellia256_ctx_decrypt :: Ptr Word8 -> Ptr Word8
+c_hs_camellia256_ctx_decrypt = #ptr struct hs_camellia256_ctx, decrypt
+foreign import ccall unsafe "hs_nettle_camellia256_init"
+	c_hs_camellia256_init :: Ptr Word8 -> Ptr Word8 -> IO ()
+foreign import ccall unsafe "nettle_camellia256_crypt"
+	c_camellia256_crypt :: NettleCryptFunc
+foreign import ccall unsafe "&nettle_camellia256_crypt"
+	p_camellia256_crypt :: FunPtr NettleCryptFunc
+
 c_cast128_ctx_size :: Int
 c_cast128_ctx_size = #{size struct cast128_ctx}
-foreign import ccall unsafe "nettle_cast128_set_key"
-	c_cast128_set_key :: Ptr Word8 -> Word -> Ptr Word8 -> IO ()
+-- cast128_set_key uses a 128-bit fixed size key, cast-5 supports the variable length
+foreign import ccall unsafe "nettle_cast5_set_key"
+	c_cast5_set_key :: Ptr Word8 -> Word -> Ptr Word8 -> IO ()
 foreign import ccall unsafe "nettle_cast128_encrypt"
 	c_cast128_encrypt :: NettleCryptFunc
 foreign import ccall unsafe "&nettle_cast128_encrypt"
@@ -278,17 +424,37 @@
 foreign import ccall unsafe "nettle_arcfour_crypt"
 	c_arcfour_crypt :: NettleCryptFunc
 
+c_chacha_ctx_size :: Int
+c_chacha_ctx_size = #{size struct chacha_ctx}
+foreign import ccall unsafe "nettle_chacha_set_key"
+	c_chacha_set_key :: Ptr Word8 -> Ptr Word8 -> IO ()
+foreign import ccall unsafe "nettle_chacha_set_nonce"
+	c_chacha_set_nonce :: Ptr Word8 -> Ptr Word8 -> IO ()
+foreign import ccall unsafe "nettle_chacha_crypt"
+	c_chacha_crypt :: NettleCryptFunc
+
 c_salsa20_ctx_size :: Int
 c_salsa20_ctx_size = #{size struct salsa20_ctx}
 foreign import ccall unsafe "nettle_salsa20_set_key"
 	c_salsa20_set_key :: Ptr Word8 -> Word -> Ptr Word8 -> IO ()
-foreign import ccall unsafe "nettle_salsa20_set_iv"
-	c_salsa20_set_iv :: Ptr Word8 -> Ptr Word8 -> IO ()
+foreign import ccall unsafe "nettle_salsa20_set_nonce"
+	c_salsa20_set_nonce :: Ptr Word8 -> Ptr Word8 -> IO ()
 foreign import ccall unsafe "nettle_salsa20_crypt"
 	c_salsa20_crypt :: NettleCryptFunc
 foreign import ccall unsafe "nettle_salsa20r12_crypt"
 	c_salsa20r12_crypt :: NettleCryptFunc
 
-
-
-
+c_chacha_poly1305_ctx_size :: Int
+c_chacha_poly1305_ctx_size = #{size struct chacha_poly1305_ctx}
+foreign import ccall unsafe "nettle_chacha_poly1305_set_key"
+	c_chacha_poly1305_set_key :: Ptr Word8 -> Ptr Word8 -> IO ()
+foreign import ccall unsafe "nettle_chacha_poly1305_set_nonce"
+	c_chacha_poly1305_set_nonce :: Ptr Word8 -> Ptr Word8 -> IO ()
+foreign import ccall unsafe "nettle_chacha_poly1305_update"
+	c_chacha_poly1305_update :: Ptr Word8 -> Word -> Ptr Word8 -> IO ()
+foreign import ccall unsafe "nettle_chacha_poly1305_encrypt"
+	c_chacha_poly1305_encrypt :: NettleCryptFunc
+foreign import ccall unsafe "nettle_chacha_poly1305_decrypt"
+	c_chacha_poly1305_decrypt :: NettleCryptFunc
+foreign import ccall unsafe "nettle_chacha_poly1305_digest"
+	c_chacha_poly1305_digest :: Ptr Word8 -> Word -> Ptr Word8 -> IO ()
diff --git a/src/Tests/Ciphers.hs b/src/Tests/Ciphers.hs
--- a/src/Tests/Ciphers.hs
+++ b/src/Tests/Ciphers.hs
@@ -148,6 +148,7 @@
 	, testBlockCipher defaultKATs (undefined :: TWOFISH)
 	, testBlockCipher defaultKATs (undefined :: SERPENT)
 	, testStreamCipher defaultStreamKATs (undefined :: ARCFOUR)
+	, testStreamCipher defaultStreamKATs (undefined :: CHACHA)
 	, testStreamCipher defaultStreamKATs (undefined :: SALSA20)
 	, testStreamCipher defaultStreamKATs (undefined :: ESTREAM_SALSA20)
 
@@ -170,10 +171,13 @@
 	, genBlockTest (undefined :: TWOFISH)
 	, genBlockTest (undefined :: SERPENT)
 	, genStreamTest (undefined :: ARCFOUR)
+	, genStreamTest (undefined :: CHACHA)
 	, genStreamTest (undefined :: SALSA20)
 	, genStreamTest (undefined :: ESTREAM_SALSA20)
+	, genStreamNonceTest (undefined :: CHACHA)
 	, genStreamNonceTest (undefined :: SALSA20)
 	, genStreamNonceTest (undefined :: ESTREAM_SALSA20)
+	, genStreamNonceWord64Test (undefined :: CHACHA)
 	, genStreamNonceWord64Test (undefined :: SALSA20)
 	, genStreamNonceWord64Test (undefined :: ESTREAM_SALSA20)
 	]
diff --git a/src/nettle-ciphers.c b/src/nettle-ciphers.c
--- a/src/nettle-ciphers.c
+++ b/src/nettle-ciphers.c
@@ -38,16 +38,132 @@
 	}
 }
 
+void hs_nettle_aes128_init(struct hs_aes128_ctx *ctx, const char *key) {
+	aes128_set_encrypt_key(&ctx->encrypt, key);
+	aes128_invert_key(&ctx->decrypt, &ctx->encrypt);
+}
+
+void hs_nettle_aes192_init(struct hs_aes192_ctx *ctx, const char *key) {
+	aes192_set_encrypt_key(&ctx->encrypt, key);
+	aes192_invert_key(&ctx->decrypt, &ctx->encrypt);
+}
+
+void hs_nettle_aes256_init(struct hs_aes256_ctx *ctx, const char *key) {
+	aes256_set_encrypt_key(&ctx->encrypt, key);
+	aes256_invert_key(&ctx->decrypt, &ctx->encrypt);
+}
+
 void hs_nettle_aes_init(struct hs_aes_ctx *ctx, unsigned int key_size, const char *key) {
 	assert(16 == key_size || 24 == key_size || 32 == key_size);
 
-	aes_set_encrypt_key(&ctx->encrypt, key_size, key);
-	aes_invert_key(&ctx->decrypt, &ctx->encrypt);
+	switch (key_size) {
+	case 16:
+		ctx->selector = AES128;
+		aes128_set_encrypt_key(&ctx->encrypt.inner128, key);
+		aes128_invert_key(&ctx->decrypt.inner128, &ctx->encrypt.inner128);
+		break;
+	case 24:
+		ctx->selector = AES192;
+		aes192_set_encrypt_key(&ctx->encrypt.inner192, key);
+		aes192_invert_key(&ctx->decrypt.inner192, &ctx->encrypt.inner192);
+		break;
+	case 32:
+		ctx->selector = AES256;
+		aes256_set_encrypt_key(&ctx->encrypt.inner256, key);
+		aes256_invert_key(&ctx->decrypt.inner256, &ctx->encrypt.inner256);
+		break;
+	}
 }
 
+void hs_nettle_aes_encrypt(const struct hs_aes_ctx *ctx, size_t length, uint8_t *dst, const uint8_t *src) {
+	switch (ctx->selector) {
+	case AES128:
+		aes128_encrypt(&ctx->encrypt.inner128, length, dst, src);
+		break;
+	case AES192:
+		aes192_encrypt(&ctx->encrypt.inner192, length, dst, src);
+		break;
+	case AES256:
+		aes256_encrypt(&ctx->encrypt.inner256, length, dst, src);
+		break;
+	}
+}
+
+void hs_nettle_aes_decrypt(const struct hs_aes_ctx *ctx, size_t length, uint8_t *dst, const uint8_t *src) {
+	switch (ctx->selector) {
+	case AES128:
+		aes128_decrypt(&ctx->decrypt.inner128, length, dst, src);
+		break;
+	case AES192:
+		aes192_decrypt(&ctx->decrypt.inner192, length, dst, src);
+		break;
+	case AES256:
+		aes256_decrypt(&ctx->decrypt.inner256, length, dst, src);
+		break;
+	}
+}
+
+void hs_nettle_camellia128_init(struct hs_camellia128_ctx *ctx, const char *key) {
+	camellia128_set_encrypt_key(&ctx->encrypt, key);
+	camellia128_invert_key(&ctx->decrypt, &ctx->encrypt);
+}
+
+void hs_nettle_camellia192_init(struct hs_camellia192_ctx *ctx, const char *key) {
+	camellia192_set_encrypt_key(&ctx->encrypt, key);
+	camellia192_invert_key(&ctx->decrypt, &ctx->encrypt);
+}
+
+void hs_nettle_camellia256_init(struct hs_camellia256_ctx *ctx, const char *key) {
+	camellia256_set_encrypt_key(&ctx->encrypt, key);
+	camellia256_invert_key(&ctx->decrypt, &ctx->encrypt);
+}
+
 void hs_nettle_camellia_init(struct hs_camellia_ctx *ctx, unsigned int key_size, const char *key) {
 	assert(16 == key_size || 24 == key_size || 32 == key_size);
 
-	camellia_set_encrypt_key(&ctx->encrypt, key_size, key);
-	camellia_invert_key(&ctx->decrypt, &ctx->encrypt);
+	switch (key_size) {
+	case 16:
+		ctx->selector = CAMELLIA128;
+		camellia128_set_encrypt_key(&ctx->encrypt.inner128, key);
+		camellia128_invert_key(&ctx->decrypt.inner128, &ctx->encrypt.inner128);
+		break;
+	case 24:
+		ctx->selector = CAMELLIA192;
+		camellia192_set_encrypt_key(&ctx->encrypt.inner192, key);
+		camellia192_invert_key(&ctx->decrypt.inner192, &ctx->encrypt.inner192);
+		break;
+	case 32:
+		ctx->selector = CAMELLIA256;
+		camellia256_set_encrypt_key(&ctx->encrypt.inner256, key);
+		camellia256_invert_key(&ctx->decrypt.inner256, &ctx->encrypt.inner256);
+		break;
+	}
+}
+
+void hs_nettle_camellia_encrypt(const struct hs_camellia_ctx *ctx, size_t length, uint8_t *dst, const uint8_t *src) {
+	switch (ctx->selector) {
+	case CAMELLIA128:
+		camellia128_crypt(&ctx->encrypt.inner128, length, dst, src);
+		break;
+	case CAMELLIA192:
+		camellia192_crypt(&ctx->encrypt.inner192, length, dst, src);
+		break;
+	case CAMELLIA256:
+		camellia256_crypt(&ctx->encrypt.inner256, length, dst, src);
+		break;
+	}
+}
+
+void hs_nettle_camellia_decrypt(const struct hs_camellia_ctx *ctx, size_t length, uint8_t *dst, const uint8_t *src) {
+	switch (ctx->selector) {
+	case CAMELLIA128:
+		camellia128_crypt(&ctx->decrypt.inner128, length, dst, src);
+		break;
+	case CAMELLIA192:
+		camellia192_crypt(&ctx->decrypt.inner192, length, dst, src);
+		break;
+	case CAMELLIA256:
+		camellia256_crypt(&ctx->decrypt.inner256, length, dst, src);
+		break;
+	}
 }
diff --git a/src/nettle-ciphers.h b/src/nettle-ciphers.h
--- a/src/nettle-ciphers.h
+++ b/src/nettle-ciphers.h
@@ -2,6 +2,12 @@
 #ifndef _HS_NETTLE_CIPHERS_H
 #define _HS_NETTLE_CIPHERS_H _HS_NETTLE_CIPHERS_H
 
+#include <nettle/version.h>
+
+#if (NETTLE_VERSION_MAJOR != 3)
+#error unsupported nettle version
+#endif
+
 #include <sys/types.h>
 #include <nettle/cbc.h>
 #include <nettle/gcm.h>
@@ -19,8 +25,16 @@
 
 /* stream ciphers */
 #include <nettle/arcfour.h>
+#include <nettle/chacha.h>
 #include <nettle/salsa20.h>
 
+/* AEAD ciphers */
+#include <nettle/chacha-poly1305.h>
+
+#if (CHACHA_POLY1305_NONCE_SIZE != CHACHA_NONCE96_SIZE)
+#error unsupported nettle version, require 96-bit nonce chacha-poly1305 variant
+#endif
+
 void hs_nettle_cfb_encrypt(void *ctx, nettle_crypt_func *f,
 	unsigned block_size, uint8_t *iv,
 	unsigned length, uint8_t *dst,
@@ -33,15 +47,60 @@
 	const uint8_t *src);
 
 
+struct hs_aes128_ctx {
+	struct aes128_ctx encrypt, decrypt;
+};
+void hs_nettle_aes128_init(struct hs_aes128_ctx *ctx, const char *key);
+
+struct hs_aes192_ctx {
+	struct aes192_ctx encrypt, decrypt;
+};
+void hs_nettle_aes192_init(struct hs_aes192_ctx *ctx, const char *key);
+
+struct hs_aes256_ctx {
+	struct aes256_ctx encrypt, decrypt;
+};
+void hs_nettle_aes256_init(struct hs_aes256_ctx *ctx, const char *key);
+
+union hs_aes_ctx_inner {
+	struct aes128_ctx inner128;
+	struct aes192_ctx inner192;
+	struct aes256_ctx inner256;
+};
 struct hs_aes_ctx {
-	struct aes_ctx encrypt, decrypt;
+	enum { AES128, AES192, AES256 } selector;
+	union hs_aes_ctx_inner encrypt, decrypt;
 };
 void hs_nettle_aes_init(struct hs_aes_ctx *ctx, unsigned int key_size, const char *key);
+void hs_nettle_aes_encrypt(const struct hs_aes_ctx *ctx, size_t length, uint8_t *dst, const uint8_t *src);
+void hs_nettle_aes_decrypt(const struct hs_aes_ctx *ctx, size_t length, uint8_t *dst, const uint8_t *src);
 
+struct hs_camellia128_ctx {
+	struct camellia128_ctx encrypt, decrypt;
+};
+void hs_nettle_camellia128_init(struct hs_camellia128_ctx *ctx, const char *key);
 
+struct hs_camellia192_ctx {
+	struct camellia192_ctx encrypt, decrypt;
+};
+void hs_nettle_camellia192_init(struct hs_camellia192_ctx *ctx, const char *key);
+
+struct hs_camellia256_ctx {
+	struct camellia256_ctx encrypt, decrypt;
+};
+void hs_nettle_camellia256_init(struct hs_camellia256_ctx *ctx, const char *key);
+
+union hs_camellia_ctx_inner {
+	struct camellia128_ctx inner128;
+	struct camellia192_ctx inner192;
+	struct camellia256_ctx inner256;
+};
 struct hs_camellia_ctx {
-	struct camellia_ctx encrypt, decrypt;
+	enum { CAMELLIA128, CAMELLIA192, CAMELLIA256 } selector;
+	union hs_camellia_ctx_inner encrypt, decrypt;
 };
 void hs_nettle_camellia_init(struct hs_camellia_ctx *ctx, unsigned int key_size, const char *key);
+void hs_nettle_camellia_encrypt(const struct hs_camellia_ctx *ctx, size_t length, uint8_t *dst, const uint8_t *src);
+void hs_nettle_camellia_decrypt(const struct hs_camellia_ctx *ctx, size_t length, uint8_t *dst, const uint8_t *src);
 
 #endif
diff --git a/src/nettle-hash.h b/src/nettle-hash.h
--- a/src/nettle-hash.h
+++ b/src/nettle-hash.h
@@ -2,6 +2,12 @@
 #ifndef _HS_NETTLE_HASH_H
 #define _HS_NETTLE_HASH_H _HS_NETTLE_HASH_H
 
+#include <nettle/version.h>
+
+#if (NETTLE_VERSION_MAJOR != 3)
+#error unsupported nettle version
+#endif
+
 #include <sys/types.h>
 #include <nettle/cbc.h>
 #include <nettle/gcm.h>
