             [buddy.core.nonce :as nonce]
             [buddy.core.codecs :as codecs])
   (:import org.bouncycastle.crypto.engines.TwofishEngine
+           org.bouncycastle.crypto.engines.SerpentEngine
            org.bouncycastle.crypto.engines.BlowfishEngine
-           org.bouncycastle.crypto.engines.AESEngine
+           org.bouncycastle.crypto.engines.AESFastEngine
            org.bouncycastle.crypto.engines.ChaChaEngine
            org.bouncycastle.crypto.modes.CBCBlockCipher
            org.bouncycastle.crypto.modes.SICBlockCipher

(defn- algorithm-supported?
  [^Keyword type ^Keyword cipher]
  (condp = type
    :block (contains? *supported-block-ciphers* cipher)
    :stream (contains? *supported-stream-ciphers* cipher)))

(defn- mode-supported?
  [^Keyword mode]
  (contains? *supported-modes* mode))

(defn get-block-size
  "Given a block cipher, return the block size
  in bytes."
  [^BlockCipher engine]
  (get-block-size* engine))

(defn get-output-size
  "Given a aead cipher, return the buffer size required for
  a `process-bytes!` plus a `calculate-authtag!` with an input of
  `len` bytes."
  [^AEADBlockCipher engine ^long len]
  (get-output-size* engine len))

(defn block-cipher
  "Block cipher engine constructor."
  [^Keyword alg ^Keyword mode]
  {:pre [(algorithm-supported? :block alg)
         (mode-supported? mode)]}
  (let [modefactory (get *supported-modes* mode)
        enginefactory (get *supported-block-ciphers* alg)]
    (modefactory (enginefactory))))

(defn stream-cipher
  "Stream cipher engine constructor."
  [^Keyword alg]
  {:pre [(algorithm-supported? :stream alg)]}
  (let [enginefactory (get *supported-stream-ciphers* alg)]
    (enginefactory)))

(defn initialize!
  "Initialize the cipher engine."
  [engine {:keys [iv key op] :as params}]
  (initialize engine params))

(defn process-bytes!
  "Encrypt or decrypt a block of bytes using the specified engine.


(extend-type BlockCipher
  IBlockCipher
  (-block-size [engine]
    (.getBlockSize engine))

  ICipher
  (-init [engine params]
    (let [iv (:iv params)
          key (:key params)
          params' (if (nil? iv)
