select CRYPTO_HASH
        select CRYPTO_POLY1305
 
+config CRYPTO_ADIANTUM
+       tristate "Adiantum support"
+       select CRYPTO_CHACHA20
+       select CRYPTO_POLY1305
+       select CRYPTO_NHPOLY1305
+       help
+         Adiantum is a tweakable, length-preserving encryption mode
+         designed for fast and secure disk encryption, especially on
+         CPUs without dedicated crypto instructions.  It encrypts
+         each sector using the XChaCha12 stream cipher, two passes of
+         an ε-almost-∆-universal hash function, and an invocation of
+         the AES-256 block cipher on a single 16-byte block.  On CPUs
+         without AES instructions, Adiantum is much faster than
+         AES-XTS.
+
+         Adiantum's security is provably reducible to that of its
+         underlying stream and block ciphers, subject to a security
+         bound.  Unlike XTS, Adiantum is a true wide-block encryption
+         mode, so it actually provides an even stronger notion of
+         security than XTS, subject to the security bound.
+
+         If unsure, say N.
+
 comment "Hash modes"
 
 config CRYPTO_CMAC
 
 obj-$(CONFIG_CRYPTO_XTS) += xts.o
 obj-$(CONFIG_CRYPTO_CTR) += ctr.o
 obj-$(CONFIG_CRYPTO_KEYWRAP) += keywrap.o
+obj-$(CONFIG_CRYPTO_ADIANTUM) += adiantum.o
 obj-$(CONFIG_CRYPTO_NHPOLY1305) += nhpoly1305.o
 obj-$(CONFIG_CRYPTO_GCM) += gcm.o
 obj-$(CONFIG_CRYPTO_CCM) += ccm.o
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Adiantum length-preserving encryption mode
+ *
+ * Copyright 2018 Google LLC
+ */
+
+/*
+ * Adiantum is a tweakable, length-preserving encryption mode designed for fast
+ * and secure disk encryption, especially on CPUs without dedicated crypto
+ * instructions.  Adiantum encrypts each sector using the XChaCha12 stream
+ * cipher, two passes of an ε-almost-∆-universal (εA∆U) hash function based on
+ * NH and Poly1305, and an invocation of the AES-256 block cipher on a single
+ * 16-byte block.  See the paper for details:
+ *
+ *     Adiantum: length-preserving encryption for entry-level processors
+ *      (https://eprint.iacr.org/2018/720.pdf)
+ *
+ * For flexibility, this implementation also allows other ciphers:
+ *
+ *     - Stream cipher: XChaCha12 or XChaCha20
+ *     - Block cipher: any with a 128-bit block size and 256-bit key
+ *
+ * This implementation doesn't currently allow other εA∆U hash functions, i.e.
+ * HPolyC is not supported.  This is because Adiantum is ~20% faster than HPolyC
+ * but still provably as secure, and also the εA∆U hash function of HBSH is
+ * formally defined to take two inputs (tweak, message) which makes it difficult
+ * to wrap with the crypto_shash API.  Rather, some details need to be handled
+ * here.  Nevertheless, if needed in the future, support for other εA∆U hash
+ * functions could be added here.
+ */
+
+#include <crypto/b128ops.h>
+#include <crypto/chacha.h>
+#include <crypto/internal/hash.h>
+#include <crypto/internal/skcipher.h>
+#include <crypto/nhpoly1305.h>
+#include <crypto/scatterwalk.h>
+#include <linux/module.h>
+
+#include "internal.h"
+
+/*
+ * Size of right-hand block of input data, in bytes; also the size of the block
+ * cipher's block size and the hash function's output.
+ */
+#define BLOCKCIPHER_BLOCK_SIZE         16
+
+/* Size of the block cipher key (K_E) in bytes */
+#define BLOCKCIPHER_KEY_SIZE           32
+
+/* Size of the hash key (K_H) in bytes */
+#define HASH_KEY_SIZE          (POLY1305_BLOCK_SIZE + NHPOLY1305_KEY_SIZE)
+
+/*
+ * The specification allows variable-length tweaks, but Linux's crypto API
+ * currently only allows algorithms to support a single length.  The "natural"
+ * tweak length for Adiantum is 16, since that fits into one Poly1305 block for
+ * the best performance.  But longer tweaks are useful for fscrypt, to avoid
+ * needing to derive per-file keys.  So instead we use two blocks, or 32 bytes.
+ */
+#define TWEAK_SIZE             32
+
+struct adiantum_instance_ctx {
+       struct crypto_skcipher_spawn streamcipher_spawn;
+       struct crypto_spawn blockcipher_spawn;
+       struct crypto_shash_spawn hash_spawn;
+};
+
+struct adiantum_tfm_ctx {
+       struct crypto_skcipher *streamcipher;
+       struct crypto_cipher *blockcipher;
+       struct crypto_shash *hash;
+       struct poly1305_key header_hash_key;
+};
+
+struct adiantum_request_ctx {
+
+       /*
+        * Buffer for right-hand block of data, i.e.
+        *
+        *    P_L => P_M => C_M => C_R when encrypting, or
+        *    C_R => C_M => P_M => P_L when decrypting.
+        *
+        * Also used to build the IV for the stream cipher.
+        */
+       union {
+               u8 bytes[XCHACHA_IV_SIZE];
+               __le32 words[XCHACHA_IV_SIZE / sizeof(__le32)];
+               le128 bignum;   /* interpret as element of Z/(2^{128}Z) */
+       } rbuf;
+
+       bool enc; /* true if encrypting, false if decrypting */
+
+       /*
+        * The result of the Poly1305 εA∆U hash function applied to
+        * (message length, tweak).
+        */
+       le128 header_hash;
+
+       /* Sub-requests, must be last */
+       union {
+               struct shash_desc hash_desc;
+               struct skcipher_request streamcipher_req;
+       } u;
+};
+
+/*
+ * Given the XChaCha stream key K_S, derive the block cipher key K_E and the
+ * hash key K_H as follows:
+ *
+ *     K_E || K_H || ... = XChaCha(key=K_S, nonce=1||0^191)
+ *
+ * Note that this denotes using bits from the XChaCha keystream, which here we
+ * get indirectly by encrypting a buffer containing all 0's.
+ */
+static int adiantum_setkey(struct crypto_skcipher *tfm, const u8 *key,
+                          unsigned int keylen)
+{
+       struct adiantum_tfm_ctx *tctx = crypto_skcipher_ctx(tfm);
+       struct {
+               u8 iv[XCHACHA_IV_SIZE];
+               u8 derived_keys[BLOCKCIPHER_KEY_SIZE + HASH_KEY_SIZE];
+               struct scatterlist sg;
+               struct crypto_wait wait;
+               struct skcipher_request req; /* must be last */
+       } *data;
+       u8 *keyp;
+       int err;
+
+       /* Set the stream cipher key (K_S) */
+       crypto_skcipher_clear_flags(tctx->streamcipher, CRYPTO_TFM_REQ_MASK);
+       crypto_skcipher_set_flags(tctx->streamcipher,
+                                 crypto_skcipher_get_flags(tfm) &
+                                 CRYPTO_TFM_REQ_MASK);
+       err = crypto_skcipher_setkey(tctx->streamcipher, key, keylen);
+       crypto_skcipher_set_flags(tfm,
+                               crypto_skcipher_get_flags(tctx->streamcipher) &
+                               CRYPTO_TFM_RES_MASK);
+       if (err)
+               return err;
+
+       /* Derive the subkeys */
+       data = kzalloc(sizeof(*data) +
+                      crypto_skcipher_reqsize(tctx->streamcipher), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+       data->iv[0] = 1;
+       sg_init_one(&data->sg, data->derived_keys, sizeof(data->derived_keys));
+       crypto_init_wait(&data->wait);
+       skcipher_request_set_tfm(&data->req, tctx->streamcipher);
+       skcipher_request_set_callback(&data->req, CRYPTO_TFM_REQ_MAY_SLEEP |
+                                                 CRYPTO_TFM_REQ_MAY_BACKLOG,
+                                     crypto_req_done, &data->wait);
+       skcipher_request_set_crypt(&data->req, &data->sg, &data->sg,
+                                  sizeof(data->derived_keys), data->iv);
+       err = crypto_wait_req(crypto_skcipher_encrypt(&data->req), &data->wait);
+       if (err)
+               goto out;
+       keyp = data->derived_keys;
+
+       /* Set the block cipher key (K_E) */
+       crypto_cipher_clear_flags(tctx->blockcipher, CRYPTO_TFM_REQ_MASK);
+       crypto_cipher_set_flags(tctx->blockcipher,
+                               crypto_skcipher_get_flags(tfm) &
+                               CRYPTO_TFM_REQ_MASK);
+       err = crypto_cipher_setkey(tctx->blockcipher, keyp,
+                                  BLOCKCIPHER_KEY_SIZE);
+       crypto_skcipher_set_flags(tfm,
+                                 crypto_cipher_get_flags(tctx->blockcipher) &
+                                 CRYPTO_TFM_RES_MASK);
+       if (err)
+               goto out;
+       keyp += BLOCKCIPHER_KEY_SIZE;
+
+       /* Set the hash key (K_H) */
+       poly1305_core_setkey(&tctx->header_hash_key, keyp);
+       keyp += POLY1305_BLOCK_SIZE;
+
+       crypto_shash_clear_flags(tctx->hash, CRYPTO_TFM_REQ_MASK);
+       crypto_shash_set_flags(tctx->hash, crypto_skcipher_get_flags(tfm) &
+                                          CRYPTO_TFM_REQ_MASK);
+       err = crypto_shash_setkey(tctx->hash, keyp, NHPOLY1305_KEY_SIZE);
+       crypto_skcipher_set_flags(tfm, crypto_shash_get_flags(tctx->hash) &
+                                      CRYPTO_TFM_RES_MASK);
+       keyp += NHPOLY1305_KEY_SIZE;
+       WARN_ON(keyp != &data->derived_keys[ARRAY_SIZE(data->derived_keys)]);
+out:
+       kzfree(data);
+       return err;
+}
+
+/* Addition in Z/(2^{128}Z) */
+static inline void le128_add(le128 *r, const le128 *v1, const le128 *v2)
+{
+       u64 x = le64_to_cpu(v1->b);
+       u64 y = le64_to_cpu(v2->b);
+
+       r->b = cpu_to_le64(x + y);
+       r->a = cpu_to_le64(le64_to_cpu(v1->a) + le64_to_cpu(v2->a) +
+                          (x + y < x));
+}
+
+/* Subtraction in Z/(2^{128}Z) */
+static inline void le128_sub(le128 *r, const le128 *v1, const le128 *v2)
+{
+       u64 x = le64_to_cpu(v1->b);
+       u64 y = le64_to_cpu(v2->b);
+
+       r->b = cpu_to_le64(x - y);
+       r->a = cpu_to_le64(le64_to_cpu(v1->a) - le64_to_cpu(v2->a) -
+                          (x - y > x));
+}
+
+/*
+ * Apply the Poly1305 εA∆U hash function to (message length, tweak) and save the
+ * result to rctx->header_hash.
+ *
+ * This value is reused in both the first and second hash steps.  Specifically,
+ * it's added to the result of an independently keyed εA∆U hash function (for
+ * equal length inputs only) taken over the message.  This gives the overall
+ * Adiantum hash of the (tweak, message) pair.
+ */
+static void adiantum_hash_header(struct skcipher_request *req)
+{
+       struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+       const struct adiantum_tfm_ctx *tctx = crypto_skcipher_ctx(tfm);
+       struct adiantum_request_ctx *rctx = skcipher_request_ctx(req);
+       const unsigned int bulk_len = req->cryptlen - BLOCKCIPHER_BLOCK_SIZE;
+       struct {
+               __le64 message_bits;
+               __le64 padding;
+       } header = {
+               .message_bits = cpu_to_le64((u64)bulk_len * 8)
+       };
+       struct poly1305_state state;
+
+       poly1305_core_init(&state);
+
+       BUILD_BUG_ON(sizeof(header) % POLY1305_BLOCK_SIZE != 0);
+       poly1305_core_blocks(&state, &tctx->header_hash_key,
+                            &header, sizeof(header) / POLY1305_BLOCK_SIZE);
+
+       BUILD_BUG_ON(TWEAK_SIZE % POLY1305_BLOCK_SIZE != 0);
+       poly1305_core_blocks(&state, &tctx->header_hash_key, req->iv,
+                            TWEAK_SIZE / POLY1305_BLOCK_SIZE);
+
+       poly1305_core_emit(&state, &rctx->header_hash);
+}
+
+/* Hash the left-hand block (the "bulk") of the message using NHPoly1305 */
+static int adiantum_hash_message(struct skcipher_request *req,
+                                struct scatterlist *sgl, le128 *digest)
+{
+       struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+       const struct adiantum_tfm_ctx *tctx = crypto_skcipher_ctx(tfm);
+       struct adiantum_request_ctx *rctx = skcipher_request_ctx(req);
+       const unsigned int bulk_len = req->cryptlen - BLOCKCIPHER_BLOCK_SIZE;
+       struct shash_desc *hash_desc = &rctx->u.hash_desc;
+       struct sg_mapping_iter miter;
+       unsigned int i, n;
+       int err;
+
+       hash_desc->tfm = tctx->hash;
+       hash_desc->flags = 0;
+
+       err = crypto_shash_init(hash_desc);
+       if (err)
+               return err;
+
+       sg_miter_start(&miter, sgl, sg_nents(sgl),
+                      SG_MITER_FROM_SG | SG_MITER_ATOMIC);
+       for (i = 0; i < bulk_len; i += n) {
+               sg_miter_next(&miter);
+               n = min_t(unsigned int, miter.length, bulk_len - i);
+               err = crypto_shash_update(hash_desc, miter.addr, n);
+               if (err)
+                       break;
+       }
+       sg_miter_stop(&miter);
+       if (err)
+               return err;
+
+       return crypto_shash_final(hash_desc, (u8 *)digest);
+}
+
+/* Continue Adiantum encryption/decryption after the stream cipher step */
+static int adiantum_finish(struct skcipher_request *req)
+{
+       struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+       const struct adiantum_tfm_ctx *tctx = crypto_skcipher_ctx(tfm);
+       struct adiantum_request_ctx *rctx = skcipher_request_ctx(req);
+       const unsigned int bulk_len = req->cryptlen - BLOCKCIPHER_BLOCK_SIZE;
+       le128 digest;
+       int err;
+
+       /* If decrypting, decrypt C_M with the block cipher to get P_M */
+       if (!rctx->enc)
+               crypto_cipher_decrypt_one(tctx->blockcipher, rctx->rbuf.bytes,
+                                         rctx->rbuf.bytes);
+
+       /*
+        * Second hash step
+        *      enc: C_R = C_M - H_{K_H}(T, C_L)
+        *      dec: P_R = P_M - H_{K_H}(T, P_L)
+        */
+       err = adiantum_hash_message(req, req->dst, &digest);
+       if (err)
+               return err;
+       le128_add(&digest, &digest, &rctx->header_hash);
+       le128_sub(&rctx->rbuf.bignum, &rctx->rbuf.bignum, &digest);
+       scatterwalk_map_and_copy(&rctx->rbuf.bignum, req->dst,
+                                bulk_len, BLOCKCIPHER_BLOCK_SIZE, 1);
+       return 0;
+}
+
+static void adiantum_streamcipher_done(struct crypto_async_request *areq,
+                                      int err)
+{
+       struct skcipher_request *req = areq->data;
+
+       if (!err)
+               err = adiantum_finish(req);
+
+       skcipher_request_complete(req, err);
+}
+
+static int adiantum_crypt(struct skcipher_request *req, bool enc)
+{
+       struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
+       const struct adiantum_tfm_ctx *tctx = crypto_skcipher_ctx(tfm);
+       struct adiantum_request_ctx *rctx = skcipher_request_ctx(req);
+       const unsigned int bulk_len = req->cryptlen - BLOCKCIPHER_BLOCK_SIZE;
+       unsigned int stream_len;
+       le128 digest;
+       int err;
+
+       if (req->cryptlen < BLOCKCIPHER_BLOCK_SIZE)
+               return -EINVAL;
+
+       rctx->enc = enc;
+
+       /*
+        * First hash step
+        *      enc: P_M = P_R + H_{K_H}(T, P_L)
+        *      dec: C_M = C_R + H_{K_H}(T, C_L)
+        */
+       adiantum_hash_header(req);
+       err = adiantum_hash_message(req, req->src, &digest);
+       if (err)
+               return err;
+       le128_add(&digest, &digest, &rctx->header_hash);
+       scatterwalk_map_and_copy(&rctx->rbuf.bignum, req->src,
+                                bulk_len, BLOCKCIPHER_BLOCK_SIZE, 0);
+       le128_add(&rctx->rbuf.bignum, &rctx->rbuf.bignum, &digest);
+
+       /* If encrypting, encrypt P_M with the block cipher to get C_M */
+       if (enc)
+               crypto_cipher_encrypt_one(tctx->blockcipher, rctx->rbuf.bytes,
+                                         rctx->rbuf.bytes);
+
+       /* Initialize the rest of the XChaCha IV (first part is C_M) */
+       BUILD_BUG_ON(BLOCKCIPHER_BLOCK_SIZE != 16);
+       BUILD_BUG_ON(XCHACHA_IV_SIZE != 32);    /* nonce || stream position */
+       rctx->rbuf.words[4] = cpu_to_le32(1);
+       rctx->rbuf.words[5] = 0;
+       rctx->rbuf.words[6] = 0;
+       rctx->rbuf.words[7] = 0;
+
+       /*
+        * XChaCha needs to be done on all the data except the last 16 bytes;
+        * for disk encryption that usually means 4080 or 496 bytes.  But ChaCha
+        * implementations tend to be most efficient when passed a whole number
+        * of 64-byte ChaCha blocks, or sometimes even a multiple of 256 bytes.
+        * And here it doesn't matter whether the last 16 bytes are written to,
+        * as the second hash step will overwrite them.  Thus, round the XChaCha
+        * length up to the next 64-byte boundary if possible.
+        */
+       stream_len = bulk_len;
+       if (round_up(stream_len, CHACHA_BLOCK_SIZE) <= req->cryptlen)
+               stream_len = round_up(stream_len, CHACHA_BLOCK_SIZE);
+
+       skcipher_request_set_tfm(&rctx->u.streamcipher_req, tctx->streamcipher);
+       skcipher_request_set_crypt(&rctx->u.streamcipher_req, req->src,
+                                  req->dst, stream_len, &rctx->rbuf);
+       skcipher_request_set_callback(&rctx->u.streamcipher_req,
+                                     req->base.flags,
+                                     adiantum_streamcipher_done, req);
+       return crypto_skcipher_encrypt(&rctx->u.streamcipher_req) ?:
+               adiantum_finish(req);
+}
+
+static int adiantum_encrypt(struct skcipher_request *req)
+{
+       return adiantum_crypt(req, true);
+}
+
+static int adiantum_decrypt(struct skcipher_request *req)
+{
+       return adiantum_crypt(req, false);
+}
+
+static int adiantum_init_tfm(struct crypto_skcipher *tfm)
+{
+       struct skcipher_instance *inst = skcipher_alg_instance(tfm);
+       struct adiantum_instance_ctx *ictx = skcipher_instance_ctx(inst);
+       struct adiantum_tfm_ctx *tctx = crypto_skcipher_ctx(tfm);
+       struct crypto_skcipher *streamcipher;
+       struct crypto_cipher *blockcipher;
+       struct crypto_shash *hash;
+       unsigned int subreq_size;
+       int err;
+
+       streamcipher = crypto_spawn_skcipher(&ictx->streamcipher_spawn);
+       if (IS_ERR(streamcipher))
+               return PTR_ERR(streamcipher);
+
+       blockcipher = crypto_spawn_cipher(&ictx->blockcipher_spawn);
+       if (IS_ERR(blockcipher)) {
+               err = PTR_ERR(blockcipher);
+               goto err_free_streamcipher;
+       }
+
+       hash = crypto_spawn_shash(&ictx->hash_spawn);
+       if (IS_ERR(hash)) {
+               err = PTR_ERR(hash);
+               goto err_free_blockcipher;
+       }
+
+       tctx->streamcipher = streamcipher;
+       tctx->blockcipher = blockcipher;
+       tctx->hash = hash;
+
+       BUILD_BUG_ON(offsetofend(struct adiantum_request_ctx, u) !=
+                    sizeof(struct adiantum_request_ctx));
+       subreq_size = max(FIELD_SIZEOF(struct adiantum_request_ctx,
+                                      u.hash_desc) +
+                         crypto_shash_descsize(hash),
+                         FIELD_SIZEOF(struct adiantum_request_ctx,
+                                      u.streamcipher_req) +
+                         crypto_skcipher_reqsize(streamcipher));
+
+       crypto_skcipher_set_reqsize(tfm,
+                                   offsetof(struct adiantum_request_ctx, u) +
+                                   subreq_size);
+       return 0;
+
+err_free_blockcipher:
+       crypto_free_cipher(blockcipher);
+err_free_streamcipher:
+       crypto_free_skcipher(streamcipher);
+       return err;
+}
+
+static void adiantum_exit_tfm(struct crypto_skcipher *tfm)
+{
+       struct adiantum_tfm_ctx *tctx = crypto_skcipher_ctx(tfm);
+
+       crypto_free_skcipher(tctx->streamcipher);
+       crypto_free_cipher(tctx->blockcipher);
+       crypto_free_shash(tctx->hash);
+}
+
+static void adiantum_free_instance(struct skcipher_instance *inst)
+{
+       struct adiantum_instance_ctx *ictx = skcipher_instance_ctx(inst);
+
+       crypto_drop_skcipher(&ictx->streamcipher_spawn);
+       crypto_drop_spawn(&ictx->blockcipher_spawn);
+       crypto_drop_shash(&ictx->hash_spawn);
+       kfree(inst);
+}
+
+/*
+ * Check for a supported set of inner algorithms.
+ * See the comment at the beginning of this file.
+ */
+static bool adiantum_supported_algorithms(struct skcipher_alg *streamcipher_alg,
+                                         struct crypto_alg *blockcipher_alg,
+                                         struct shash_alg *hash_alg)
+{
+       if (strcmp(streamcipher_alg->base.cra_name, "xchacha12") != 0 &&
+           strcmp(streamcipher_alg->base.cra_name, "xchacha20") != 0)
+               return false;
+
+       if (blockcipher_alg->cra_cipher.cia_min_keysize > BLOCKCIPHER_KEY_SIZE ||
+           blockcipher_alg->cra_cipher.cia_max_keysize < BLOCKCIPHER_KEY_SIZE)
+               return false;
+       if (blockcipher_alg->cra_blocksize != BLOCKCIPHER_BLOCK_SIZE)
+               return false;
+
+       if (strcmp(hash_alg->base.cra_name, "nhpoly1305") != 0)
+               return false;
+
+       return true;
+}
+
+static int adiantum_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+       struct crypto_attr_type *algt;
+       const char *streamcipher_name;
+       const char *blockcipher_name;
+       const char *nhpoly1305_name;
+       struct skcipher_instance *inst;
+       struct adiantum_instance_ctx *ictx;
+       struct skcipher_alg *streamcipher_alg;
+       struct crypto_alg *blockcipher_alg;
+       struct crypto_alg *_hash_alg;
+       struct shash_alg *hash_alg;
+       int err;
+
+       algt = crypto_get_attr_type(tb);
+       if (IS_ERR(algt))
+               return PTR_ERR(algt);
+
+       if ((algt->type ^ CRYPTO_ALG_TYPE_SKCIPHER) & algt->mask)
+               return -EINVAL;
+
+       streamcipher_name = crypto_attr_alg_name(tb[1]);
+       if (IS_ERR(streamcipher_name))
+               return PTR_ERR(streamcipher_name);
+
+       blockcipher_name = crypto_attr_alg_name(tb[2]);
+       if (IS_ERR(blockcipher_name))
+               return PTR_ERR(blockcipher_name);
+
+       nhpoly1305_name = crypto_attr_alg_name(tb[3]);
+       if (nhpoly1305_name == ERR_PTR(-ENOENT))
+               nhpoly1305_name = "nhpoly1305";
+       if (IS_ERR(nhpoly1305_name))
+               return PTR_ERR(nhpoly1305_name);
+
+       inst = kzalloc(sizeof(*inst) + sizeof(*ictx), GFP_KERNEL);
+       if (!inst)
+               return -ENOMEM;
+       ictx = skcipher_instance_ctx(inst);
+
+       /* Stream cipher, e.g. "xchacha12" */
+       err = crypto_grab_skcipher(&ictx->streamcipher_spawn, streamcipher_name,
+                                  0, crypto_requires_sync(algt->type,
+                                                          algt->mask));
+       if (err)
+               goto out_free_inst;
+       streamcipher_alg = crypto_spawn_skcipher_alg(&ictx->streamcipher_spawn);
+
+       /* Block cipher, e.g. "aes" */
+       err = crypto_grab_spawn(&ictx->blockcipher_spawn, blockcipher_name,
+                               CRYPTO_ALG_TYPE_CIPHER, CRYPTO_ALG_TYPE_MASK);
+       if (err)
+               goto out_drop_streamcipher;
+       blockcipher_alg = ictx->blockcipher_spawn.alg;
+
+       /* NHPoly1305 εA∆U hash function */
+       _hash_alg = crypto_alg_mod_lookup(nhpoly1305_name,
+                                         CRYPTO_ALG_TYPE_SHASH,
+                                         CRYPTO_ALG_TYPE_MASK);
+       if (IS_ERR(_hash_alg)) {
+               err = PTR_ERR(_hash_alg);
+               goto out_drop_blockcipher;
+       }
+       hash_alg = __crypto_shash_alg(_hash_alg);
+       err = crypto_init_shash_spawn(&ictx->hash_spawn, hash_alg,
+                                     skcipher_crypto_instance(inst));
+       if (err) {
+               crypto_mod_put(_hash_alg);
+               goto out_drop_blockcipher;
+       }
+
+       /* Check the set of algorithms */
+       if (!adiantum_supported_algorithms(streamcipher_alg, blockcipher_alg,
+                                          hash_alg)) {
+               pr_warn("Unsupported Adiantum instantiation: (%s,%s,%s)\n",
+                       streamcipher_alg->base.cra_name,
+                       blockcipher_alg->cra_name, hash_alg->base.cra_name);
+               err = -EINVAL;
+               goto out_drop_hash;
+       }
+
+       /* Instance fields */
+
+       err = -ENAMETOOLONG;
+       if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,
+                    "adiantum(%s,%s)", streamcipher_alg->base.cra_name,
+                    blockcipher_alg->cra_name) >= CRYPTO_MAX_ALG_NAME)
+               goto out_drop_hash;
+       if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
+                    "adiantum(%s,%s,%s)",
+                    streamcipher_alg->base.cra_driver_name,
+                    blockcipher_alg->cra_driver_name,
+                    hash_alg->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
+               goto out_drop_hash;
+
+       inst->alg.base.cra_blocksize = BLOCKCIPHER_BLOCK_SIZE;
+       inst->alg.base.cra_ctxsize = sizeof(struct adiantum_tfm_ctx);
+       inst->alg.base.cra_alignmask = streamcipher_alg->base.cra_alignmask |
+                                      hash_alg->base.cra_alignmask;
+       /*
+        * The block cipher is only invoked once per message, so for long
+        * messages (e.g. sectors for disk encryption) its performance doesn't
+        * matter as much as that of the stream cipher and hash function.  Thus,
+        * weigh the block cipher's ->cra_priority less.
+        */
+       inst->alg.base.cra_priority = (4 * streamcipher_alg->base.cra_priority +
+                                      2 * hash_alg->base.cra_priority +
+                                      blockcipher_alg->cra_priority) / 7;
+
+       inst->alg.setkey = adiantum_setkey;
+       inst->alg.encrypt = adiantum_encrypt;
+       inst->alg.decrypt = adiantum_decrypt;
+       inst->alg.init = adiantum_init_tfm;
+       inst->alg.exit = adiantum_exit_tfm;
+       inst->alg.min_keysize = crypto_skcipher_alg_min_keysize(streamcipher_alg);
+       inst->alg.max_keysize = crypto_skcipher_alg_max_keysize(streamcipher_alg);
+       inst->alg.ivsize = TWEAK_SIZE;
+
+       inst->free = adiantum_free_instance;
+
+       err = skcipher_register_instance(tmpl, inst);
+       if (err)
+               goto out_drop_hash;
+
+       return 0;
+
+out_drop_hash:
+       crypto_drop_shash(&ictx->hash_spawn);
+out_drop_blockcipher:
+       crypto_drop_spawn(&ictx->blockcipher_spawn);
+out_drop_streamcipher:
+       crypto_drop_skcipher(&ictx->streamcipher_spawn);
+out_free_inst:
+       kfree(inst);
+       return err;
+}
+
+/* adiantum(streamcipher_name, blockcipher_name [, nhpoly1305_name]) */
+static struct crypto_template adiantum_tmpl = {
+       .name = "adiantum",
+       .create = adiantum_create,
+       .module = THIS_MODULE,
+};
+
+static int __init adiantum_module_init(void)
+{
+       return crypto_register_template(&adiantum_tmpl);
+}
+
+static void __exit adiantum_module_exit(void)
+{
+       crypto_unregister_template(&adiantum_tmpl);
+}
+
+module_init(adiantum_module_init);
+module_exit(adiantum_module_exit);
+
+MODULE_DESCRIPTION("Adiantum length-preserving encryption mode");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>");
+MODULE_ALIAS_CRYPTO("adiantum");
 
                test_cipher_speed("ctr(sm4)", DECRYPT, sec, NULL, 0,
                                speed_template_16);
                break;
+
+       case 219:
+               test_cipher_speed("adiantum(xchacha12,aes)", ENCRYPT, sec, NULL,
+                                 0, speed_template_32);
+               test_cipher_speed("adiantum(xchacha12,aes)", DECRYPT, sec, NULL,
+                                 0, speed_template_32);
+               test_cipher_speed("adiantum(xchacha20,aes)", ENCRYPT, sec, NULL,
+                                 0, speed_template_32);
+               test_cipher_speed("adiantum(xchacha20,aes)", DECRYPT, sec, NULL,
+                                 0, speed_template_32);
+               break;
+
        case 300:
                if (alg) {
                        test_hash_speed(alg, sec, generic_hash_speed_template);
 
 /* Please keep this list sorted by algorithm name. */
 static const struct alg_test_desc alg_test_descs[] = {
        {
+               .alg = "adiantum(xchacha12,aes)",
+               .test = alg_test_skcipher,
+               .suite = {
+                       .cipher = __VECS(adiantum_xchacha12_aes_tv_template)
+               },
+       }, {
+               .alg = "adiantum(xchacha20,aes)",
+               .test = alg_test_skcipher,
+               .suite = {
+                       .cipher = __VECS(adiantum_xchacha20_aes_tv_template)
+               },
+       }, {
                .alg = "aegis128",
                .test = alg_test_aead,
                .suite = {
 
        },
 };
 
+/* Adiantum test vectors from https://github.com/google/adiantum */
+static const struct cipher_testvec adiantum_xchacha12_aes_tv_template[] = {
+       {
+               .key    = "\x9e\xeb\xb2\x49\x3c\x1c\xf5\xf4"
+                         "\x6a\x99\xc2\xc4\xdf\xb1\xf4\xdd"
+                         "\x75\x20\x57\xea\x2c\x4f\xcd\xb2"
+                         "\xa5\x3d\x7b\x49\x1e\xab\xfd\x0f",
+               .klen   = 32,
+               .iv     = "\xdf\x63\xd4\xab\xd2\x49\xf3\xd8"
+                         "\x33\x81\x37\x60\x7d\xfa\x73\x08"
+                         "\xd8\x49\x6d\x80\xe8\x2f\x62\x54"
+                         "\xeb\x0e\xa9\x39\x5b\x45\x7f\x8a",
+               .ptext  = "\x67\xc9\xf2\x30\x84\x41\x8e\x43"
+                         "\xfb\xf3\xb3\x3e\x79\x36\x7f\xe8",
+               .ctext  = "\x6d\x32\x86\x18\x67\x86\x0f\x3f"
+                         "\x96\x7c\x9d\x28\x0d\x53\xec\x9f",
+               .len    = 16,
+               .also_non_np = 1,
+               .np     = 2,
+               .tap    = { 14, 2 },
+       }, {
+               .key    = "\x36\x2b\x57\x97\xf8\x5d\xcd\x99"
+                         "\x5f\x1a\x5a\x44\x1d\x92\x0f\x27"
+                         "\xcc\x16\xd7\x2b\x85\x63\x99\xd3"
+                         "\xba\x96\xa1\xdb\xd2\x60\x68\xda",
+               .klen   = 32,
+               .iv     = "\xef\x58\x69\xb1\x2c\x5e\x9a\x47"
+                         "\x24\xc1\xb1\x69\xe1\x12\x93\x8f"
+                         "\x43\x3d\x6d\x00\xdb\x5e\xd8\xd9"
+                         "\x12\x9a\xfe\xd9\xff\x2d\xaa\xc4",
+               .ptext  = "\x5e\xa8\x68\x19\x85\x98\x12\x23"
+                         "\x26\x0a\xcc\xdb\x0a\x04\xb9\xdf"
+                         "\x4d\xb3\x48\x7b\xb0\xe3\xc8\x19"
+                         "\x43\x5a\x46\x06\x94\x2d\xf2",
+               .ctext  = "\xc7\xc6\xf1\x73\x8f\xc4\xff\x4a"
+                         "\x39\xbe\x78\xbe\x8d\x28\xc8\x89"
+                         "\x46\x63\xe7\x0c\x7d\x87\xe8\x4e"
+                         "\xc9\x18\x7b\xbe\x18\x60\x50",
+               .len    = 31,
+       }, {
+               .key    = "\xa5\x28\x24\x34\x1a\x3c\xd8\xf7"
+                         "\x05\x91\x8f\xee\x85\x1f\x35\x7f"
+                         "\x80\x3d\xfc\x9b\x94\xf6\xfc\x9e"
+                         "\x19\x09\x00\xa9\x04\x31\x4f\x11",
+               .klen   = 32,
+               .iv     = "\xa1\xba\x49\x95\xff\x34\x6d\xb8"
+                         "\xcd\x87\x5d\x5e\xfd\xea\x85\xdb"
+                         "\x8a\x7b\x5e\xb2\x5d\x57\xdd\x62"
+                         "\xac\xa9\x8c\x41\x42\x94\x75\xb7",
+               .ptext  = "\x69\xb4\xe8\x8c\x37\xe8\x67\x82"
+                         "\xf1\xec\x5d\x04\xe5\x14\x91\x13"
+                         "\xdf\xf2\x87\x1b\x69\x81\x1d\x71"
+                         "\x70\x9e\x9c\x3b\xde\x49\x70\x11"
+                         "\xa0\xa3\xdb\x0d\x54\x4f\x66\x69"
+                         "\xd7\xdb\x80\xa7\x70\x92\x68\xce"
+                         "\x81\x04\x2c\xc6\xab\xae\xe5\x60"
+                         "\x15\xe9\x6f\xef\xaa\x8f\xa7\xa7"
+                         "\x63\x8f\xf2\xf0\x77\xf1\xa8\xea"
+                         "\xe1\xb7\x1f\x9e\xab\x9e\x4b\x3f"
+                         "\x07\x87\x5b\x6f\xcd\xa8\xaf\xb9"
+                         "\xfa\x70\x0b\x52\xb8\xa8\xa7\x9e"
+                         "\x07\x5f\xa6\x0e\xb3\x9b\x79\x13"
+                         "\x79\xc3\x3e\x8d\x1c\x2c\x68\xc8"
+                         "\x51\x1d\x3c\x7b\x7d\x79\x77\x2a"
+                         "\x56\x65\xc5\x54\x23\x28\xb0\x03",
+               .ctext  = "\x9e\x16\xab\xed\x4b\xa7\x42\x5a"
+                         "\xc6\xfb\x4e\x76\xff\xbe\x03\xa0"
+                         "\x0f\xe3\xad\xba\xe4\x98\x2b\x0e"
+                         "\x21\x48\xa0\xb8\x65\x48\x27\x48"
+                         "\x84\x54\x54\xb2\x9a\x94\x7b\xe6"
+                         "\x4b\x29\xe9\xcf\x05\x91\x80\x1a"
+                         "\x3a\xf3\x41\x96\x85\x1d\x9f\x74"
+                         "\x51\x56\x63\xfa\x7c\x28\x85\x49"
+                         "\xf7\x2f\xf9\xf2\x18\x46\xf5\x33"
+                         "\x80\xa3\x3c\xce\xb2\x57\x93\xf5"
+                         "\xae\xbd\xa9\xf5\x7b\x30\xc4\x93"
+                         "\x66\xe0\x30\x77\x16\xe4\xa0\x31"
+                         "\xba\x70\xbc\x68\x13\xf5\xb0\x9a"
+                         "\xc1\xfc\x7e\xfe\x55\x80\x5c\x48"
+                         "\x74\xa6\xaa\xa3\xac\xdc\xc2\xf5"
+                         "\x8d\xde\x34\x86\x78\x60\x75\x8d",
+               .len    = 128,
+               .also_non_np = 1,
+               .np     = 4,
+               .tap    = { 104, 16, 4, 4 },
+       }, {
+               .key    = "\xd3\x81\x72\x18\x23\xff\x6f\x4a"
+                         "\x25\x74\x29\x0d\x51\x8a\x0e\x13"
+                         "\xc1\x53\x5d\x30\x8d\xee\x75\x0d"
+                         "\x14\xd6\x69\xc9\x15\xa9\x0c\x60",
+               .klen   = 32,
+               .iv     = "\x65\x9b\xd4\xa8\x7d\x29\x1d\xf4"
+                         "\xc4\xd6\x9b\x6a\x28\xab\x64\xe2"
+                         "\x62\x81\x97\xc5\x81\xaa\xf9\x44"
+                         "\xc1\x72\x59\x82\xaf\x16\xc8\x2c",
+               .ptext  = "\xc7\x6b\x52\x6a\x10\xf0\xcc\x09"
+                         "\xc1\x12\x1d\x6d\x21\xa6\x78\xf5"
+                         "\x05\xa3\x69\x60\x91\x36\x98\x57"
+                         "\xba\x0c\x14\xcc\xf3\x2d\x73\x03"
+                         "\xc6\xb2\x5f\xc8\x16\x27\x37\x5d"
+                         "\xd0\x0b\x87\xb2\x50\x94\x7b\x58"
+                         "\x04\xf4\xe0\x7f\x6e\x57\x8e\xc9"
+                         "\x41\x84\xc1\xb1\x7e\x4b\x91\x12"
+                         "\x3a\x8b\x5d\x50\x82\x7b\xcb\xd9"
+                         "\x9a\xd9\x4e\x18\x06\x23\x9e\xd4"
+                         "\xa5\x20\x98\xef\xb5\xda\xe5\xc0"
+                         "\x8a\x6a\x83\x77\x15\x84\x1e\xae"
+                         "\x78\x94\x9d\xdf\xb7\xd1\xea\x67"
+                         "\xaa\xb0\x14\x15\xfa\x67\x21\x84"
+                         "\xd3\x41\x2a\xce\xba\x4b\x4a\xe8"
+                         "\x95\x62\xa9\x55\xf0\x80\xad\xbd"
+                         "\xab\xaf\xdd\x4f\xa5\x7c\x13\x36"
+                         "\xed\x5e\x4f\x72\xad\x4b\xf1\xd0"
+                         "\x88\x4e\xec\x2c\x88\x10\x5e\xea"
+                         "\x12\xc0\x16\x01\x29\xa3\xa0\x55"
+                         "\xaa\x68\xf3\xe9\x9d\x3b\x0d\x3b"
+                         "\x6d\xec\xf8\xa0\x2d\xf0\x90\x8d"
+                         "\x1c\xe2\x88\xd4\x24\x71\xf9\xb3"
+                         "\xc1\x9f\xc5\xd6\x76\x70\xc5\x2e"
+                         "\x9c\xac\xdb\x90\xbd\x83\x72\xba"
+                         "\x6e\xb5\xa5\x53\x83\xa9\xa5\xbf"
+                         "\x7d\x06\x0e\x3c\x2a\xd2\x04\xb5"
+                         "\x1e\x19\x38\x09\x16\xd2\x82\x1f"
+                         "\x75\x18\x56\xb8\x96\x0b\xa6\xf9"
+                         "\xcf\x62\xd9\x32\x5d\xa9\xd7\x1d"
+                         "\xec\xe4\xdf\x1b\xbe\xf1\x36\xee"
+                         "\xe3\x7b\xb5\x2f\xee\xf8\x53\x3d"
+                         "\x6a\xb7\x70\xa9\xfc\x9c\x57\x25"
+                         "\xf2\x89\x10\xd3\xb8\xa8\x8c\x30"
+                         "\xae\x23\x4f\x0e\x13\x66\x4f\xe1"
+                         "\xb6\xc0\xe4\xf8\xef\x93\xbd\x6e"
+                         "\x15\x85\x6b\xe3\x60\x81\x1d\x68"
+                         "\xd7\x31\x87\x89\x09\xab\xd5\x96"
+                         "\x1d\xf3\x6d\x67\x80\xca\x07\x31"
+                         "\x5d\xa7\xe4\xfb\x3e\xf2\x9b\x33"
+                         "\x52\x18\xc8\x30\xfe\x2d\xca\x1e"
+                         "\x79\x92\x7a\x60\x5c\xb6\x58\x87"
+                         "\xa4\x36\xa2\x67\x92\x8b\xa4\xb7"
+                         "\xf1\x86\xdf\xdc\xc0\x7e\x8f\x63"
+                         "\xd2\xa2\xdc\x78\xeb\x4f\xd8\x96"
+                         "\x47\xca\xb8\x91\xf9\xf7\x94\x21"
+                         "\x5f\x9a\x9f\x5b\xb8\x40\x41\x4b"
+                         "\x66\x69\x6a\x72\xd0\xcb\x70\xb7"
+                         "\x93\xb5\x37\x96\x05\x37\x4f\xe5"
+                         "\x8c\xa7\x5a\x4e\x8b\xb7\x84\xea"
+                         "\xc7\xfc\x19\x6e\x1f\x5a\xa1\xac"
+                         "\x18\x7d\x52\x3b\xb3\x34\x62\x99"
+                         "\xe4\x9e\x31\x04\x3f\xc0\x8d\x84"
+                         "\x17\x7c\x25\x48\x52\x67\x11\x27"
+                         "\x67\xbb\x5a\x85\xca\x56\xb2\x5c"
+                         "\xe6\xec\xd5\x96\x3d\x15\xfc\xfb"
+                         "\x22\x25\xf4\x13\xe5\x93\x4b\x9a"
+                         "\x77\xf1\x52\x18\xfa\x16\x5e\x49"
+                         "\x03\x45\xa8\x08\xfa\xb3\x41\x92"
+                         "\x79\x50\x33\xca\xd0\xd7\x42\x55"
+                         "\xc3\x9a\x0c\x4e\xd9\xa4\x3c\x86"
+                         "\x80\x9f\x53\xd1\xa4\x2e\xd1\xbc"
+                         "\xf1\x54\x6e\x93\xa4\x65\x99\x8e"
+                         "\xdf\x29\xc0\x64\x63\x07\xbb\xea",
+               .ctext  = "\x15\x97\xd0\x86\x18\x03\x9c\x51"
+                         "\xc5\x11\x36\x62\x13\x92\xe6\x73"
+                         "\x29\x79\xde\xa1\x00\x3e\x08\x64"
+                         "\x17\x1a\xbc\xd5\xfe\x33\x0e\x0c"
+                         "\x7c\x94\xa7\xc6\x3c\xbe\xac\xa2"
+                         "\x89\xe6\xbc\xdf\x0c\x33\x27\x42"
+                         "\x46\x73\x2f\xba\x4e\xa6\x46\x8f"
+                         "\xe4\xee\x39\x63\x42\x65\xa3\x88"
+                         "\x7a\xad\x33\x23\xa9\xa7\x20\x7f"
+                         "\x0b\xe6\x6a\xc3\x60\xda\x9e\xb4"
+                         "\xd6\x07\x8a\x77\x26\xd1\xab\x44"
+                         "\x99\x55\x03\x5e\xed\x8d\x7b\xbd"
+                         "\xc8\x21\xb7\x21\x30\x3f\xc0\xb5"
+                         "\xc8\xec\x6c\x23\xa6\xa3\x6d\xf1"
+                         "\x30\x0a\xd0\xa6\xa9\x28\x69\xae"
+                         "\x2a\xe6\x54\xac\x82\x9d\x6a\x95"
+                         "\x6f\x06\x44\xc5\x5a\x77\x6e\xec"
+                         "\xf8\xf8\x63\xb2\xe6\xaa\xbd\x8e"
+                         "\x0e\x8a\x62\x00\x03\xc8\x84\xdd"
+                         "\x47\x4a\xc3\x55\xba\xb7\xe7\xdf"
+                         "\x08\xbf\x62\xf5\xe8\xbc\xb6\x11"
+                         "\xe4\xcb\xd0\x66\x74\x32\xcf\xd4"
+                         "\xf8\x51\x80\x39\x14\x05\x12\xdb"
+                         "\x87\x93\xe2\x26\x30\x9c\x3a\x21"
+                         "\xe5\xd0\x38\x57\x80\x15\xe4\x08"
+                         "\x58\x05\x49\x7d\xe6\x92\x77\x70"
+                         "\xfb\x1e\x2d\x6a\x84\x00\xc8\x68"
+                         "\xf7\x1a\xdd\xf0\x7b\x38\x1e\xd8"
+                         "\x2c\x78\x78\x61\xcf\xe3\xde\x69"
+                         "\x1f\xd5\x03\xd5\x1a\xb4\xcf\x03"
+                         "\xc8\x7a\x70\x68\x35\xb4\xf6\xbe"
+                         "\x90\x62\xb2\x28\x99\x86\xf5\x44"
+                         "\x99\xeb\x31\xcf\xca\xdf\xd0\x21"
+                         "\xd6\x60\xf7\x0f\x40\xb4\x80\xb7"
+                         "\xab\xe1\x9b\x45\xba\x66\xda\xee"
+                         "\xdd\x04\x12\x40\x98\xe1\x69\xe5"
+                         "\x2b\x9c\x59\x80\xe7\x7b\xcc\x63"
+                         "\xa6\xc0\x3a\xa9\xfe\x8a\xf9\x62"
+                         "\x11\x34\x61\x94\x35\xfe\xf2\x99"
+                         "\xfd\xee\x19\xea\x95\xb6\x12\xbf"
+                         "\x1b\xdf\x02\x1a\xcc\x3e\x7e\x65"
+                         "\x78\x74\x10\x50\x29\x63\x28\xea"
+                         "\x6b\xab\xd4\x06\x4d\x15\x24\x31"
+                         "\xc7\x0a\xc9\x16\xb6\x48\xf0\xbf"
+                         "\x49\xdb\x68\x71\x31\x8f\x87\xe2"
+                         "\x13\x05\x64\xd6\x22\x0c\xf8\x36"
+                         "\x84\x24\x3e\x69\x5e\xb8\x9e\x16"
+                         "\x73\x6c\x83\x1e\xe0\x9f\x9e\xba"
+                         "\xe5\x59\x21\x33\x1b\xa9\x26\xc2"
+                         "\xc7\xd9\x30\x73\xb6\xa6\x73\x82"
+                         "\x19\xfa\x44\x4d\x40\x8b\x69\x04"
+                         "\x94\x74\xea\x6e\xb3\x09\x47\x01"
+                         "\x2a\xb9\x78\x34\x43\x11\xed\xd6"
+                         "\x8c\x95\x65\x1b\x85\x67\xa5\x40"
+                         "\xac\x9c\x05\x4b\x57\x4a\xa9\x96"
+                         "\x0f\xdd\x4f\xa1\xe0\xcf\x6e\xc7"
+                         "\x1b\xed\xa2\xb4\x56\x8c\x09\x6e"
+                         "\xa6\x65\xd7\x55\x81\xb7\xed\x11"
+                         "\x9b\x40\x75\xa8\x6b\x56\xaf\x16"
+                         "\x8b\x3d\xf4\xcb\xfe\xd5\x1d\x3d"
+                         "\x85\xc2\xc0\xde\x43\x39\x4a\x96"
+                         "\xba\x88\x97\xc0\xd6\x00\x0e\x27"
+                         "\x21\xb0\x21\x52\xba\xa7\x37\xaa"
+                         "\xcc\xbf\x95\xa8\xf4\xd0\x91\xf6",
+               .len    = 512,
+               .also_non_np = 1,
+               .np     = 2,
+               .tap    = { 144, 368 },
+       }
+};
+
+/* Adiantum with XChaCha20 instead of XChaCha12 */
+/* Test vectors from https://github.com/google/adiantum */
+static const struct cipher_testvec adiantum_xchacha20_aes_tv_template[] = {
+       {
+               .key    = "\x9e\xeb\xb2\x49\x3c\x1c\xf5\xf4"
+                         "\x6a\x99\xc2\xc4\xdf\xb1\xf4\xdd"
+                         "\x75\x20\x57\xea\x2c\x4f\xcd\xb2"
+                         "\xa5\x3d\x7b\x49\x1e\xab\xfd\x0f",
+               .klen   = 32,
+               .iv     = "\xdf\x63\xd4\xab\xd2\x49\xf3\xd8"
+                         "\x33\x81\x37\x60\x7d\xfa\x73\x08"
+                         "\xd8\x49\x6d\x80\xe8\x2f\x62\x54"
+                         "\xeb\x0e\xa9\x39\x5b\x45\x7f\x8a",
+               .ptext  = "\x67\xc9\xf2\x30\x84\x41\x8e\x43"
+                         "\xfb\xf3\xb3\x3e\x79\x36\x7f\xe8",
+               .ctext  = "\xf6\x78\x97\xd6\xaa\x94\x01\x27"
+                         "\x2e\x4d\x83\xe0\x6e\x64\x9a\xdf",
+               .len    = 16,
+               .also_non_np = 1,
+               .np     = 3,
+               .tap    = { 5, 2, 9 },
+       }, {
+               .key    = "\x36\x2b\x57\x97\xf8\x5d\xcd\x99"
+                         "\x5f\x1a\x5a\x44\x1d\x92\x0f\x27"
+                         "\xcc\x16\xd7\x2b\x85\x63\x99\xd3"
+                         "\xba\x96\xa1\xdb\xd2\x60\x68\xda",
+               .klen   = 32,
+               .iv     = "\xef\x58\x69\xb1\x2c\x5e\x9a\x47"
+                         "\x24\xc1\xb1\x69\xe1\x12\x93\x8f"
+                         "\x43\x3d\x6d\x00\xdb\x5e\xd8\xd9"
+                         "\x12\x9a\xfe\xd9\xff\x2d\xaa\xc4",
+               .ptext  = "\x5e\xa8\x68\x19\x85\x98\x12\x23"
+                         "\x26\x0a\xcc\xdb\x0a\x04\xb9\xdf"
+                         "\x4d\xb3\x48\x7b\xb0\xe3\xc8\x19"
+                         "\x43\x5a\x46\x06\x94\x2d\xf2",
+               .ctext  = "\x4b\xb8\x90\x10\xdf\x7f\x64\x08"
+                         "\x0e\x14\x42\x5f\x00\x74\x09\x36"
+                         "\x57\x72\xb5\xfd\xb5\x5d\xb8\x28"
+                         "\x0c\x04\x91\x14\x91\xe9\x37",
+               .len    = 31,
+               .also_non_np = 1,
+               .np     = 2,
+               .tap    = { 16, 15 },
+       }, {
+               .key    = "\xa5\x28\x24\x34\x1a\x3c\xd8\xf7"
+                         "\x05\x91\x8f\xee\x85\x1f\x35\x7f"
+                         "\x80\x3d\xfc\x9b\x94\xf6\xfc\x9e"
+                         "\x19\x09\x00\xa9\x04\x31\x4f\x11",
+               .klen   = 32,
+               .iv     = "\xa1\xba\x49\x95\xff\x34\x6d\xb8"
+                         "\xcd\x87\x5d\x5e\xfd\xea\x85\xdb"
+                         "\x8a\x7b\x5e\xb2\x5d\x57\xdd\x62"
+                         "\xac\xa9\x8c\x41\x42\x94\x75\xb7",
+               .ptext  = "\x69\xb4\xe8\x8c\x37\xe8\x67\x82"
+                         "\xf1\xec\x5d\x04\xe5\x14\x91\x13"
+                         "\xdf\xf2\x87\x1b\x69\x81\x1d\x71"
+                         "\x70\x9e\x9c\x3b\xde\x49\x70\x11"
+                         "\xa0\xa3\xdb\x0d\x54\x4f\x66\x69"
+                         "\xd7\xdb\x80\xa7\x70\x92\x68\xce"
+                         "\x81\x04\x2c\xc6\xab\xae\xe5\x60"
+                         "\x15\xe9\x6f\xef\xaa\x8f\xa7\xa7"
+                         "\x63\x8f\xf2\xf0\x77\xf1\xa8\xea"
+                         "\xe1\xb7\x1f\x9e\xab\x9e\x4b\x3f"
+                         "\x07\x87\x5b\x6f\xcd\xa8\xaf\xb9"
+                         "\xfa\x70\x0b\x52\xb8\xa8\xa7\x9e"
+                         "\x07\x5f\xa6\x0e\xb3\x9b\x79\x13"
+                         "\x79\xc3\x3e\x8d\x1c\x2c\x68\xc8"
+                         "\x51\x1d\x3c\x7b\x7d\x79\x77\x2a"
+                         "\x56\x65\xc5\x54\x23\x28\xb0\x03",
+               .ctext  = "\xb1\x8b\xa0\x05\x77\xa8\x4d\x59"
+                         "\x1b\x8e\x21\xfc\x3a\x49\xfa\xd4"
+                         "\xeb\x36\xf3\xc4\xdf\xdc\xae\x67"
+                         "\x07\x3f\x70\x0e\xe9\x66\xf5\x0c"
+                         "\x30\x4d\x66\xc9\xa4\x2f\x73\x9c"
+                         "\x13\xc8\x49\x44\xcc\x0a\x90\x9d"
+                         "\x7c\xdd\x19\x3f\xea\x72\x8d\x58"
+                         "\xab\xe7\x09\x2c\xec\xb5\x44\xd2"
+                         "\xca\xa6\x2d\x7a\x5c\x9c\x2b\x15"
+                         "\xec\x2a\xa6\x69\x91\xf9\xf3\x13"
+                         "\xf7\x72\xc1\xc1\x40\xd5\xe1\x94"
+                         "\xf4\x29\xa1\x3e\x25\x02\xa8\x3e"
+                         "\x94\xc1\x91\x14\xa1\x14\xcb\xbe"
+                         "\x67\x4c\xb9\x38\xfe\xa7\xaa\x32"
+                         "\x29\x62\x0d\xb2\xf6\x3c\x58\x57"
+                         "\xc1\xd5\x5a\xbb\xd6\xa6\x2a\xe5",
+               .len    = 128,
+               .also_non_np = 1,
+               .np     = 4,
+               .tap    = { 112, 7, 8, 1 },
+       }, {
+               .key    = "\xd3\x81\x72\x18\x23\xff\x6f\x4a"
+                         "\x25\x74\x29\x0d\x51\x8a\x0e\x13"
+                         "\xc1\x53\x5d\x30\x8d\xee\x75\x0d"
+                         "\x14\xd6\x69\xc9\x15\xa9\x0c\x60",
+               .klen   = 32,
+               .iv     = "\x65\x9b\xd4\xa8\x7d\x29\x1d\xf4"
+                         "\xc4\xd6\x9b\x6a\x28\xab\x64\xe2"
+                         "\x62\x81\x97\xc5\x81\xaa\xf9\x44"
+                         "\xc1\x72\x59\x82\xaf\x16\xc8\x2c",
+               .ptext  = "\xc7\x6b\x52\x6a\x10\xf0\xcc\x09"
+                         "\xc1\x12\x1d\x6d\x21\xa6\x78\xf5"
+                         "\x05\xa3\x69\x60\x91\x36\x98\x57"
+                         "\xba\x0c\x14\xcc\xf3\x2d\x73\x03"
+                         "\xc6\xb2\x5f\xc8\x16\x27\x37\x5d"
+                         "\xd0\x0b\x87\xb2\x50\x94\x7b\x58"
+                         "\x04\xf4\xe0\x7f\x6e\x57\x8e\xc9"
+                         "\x41\x84\xc1\xb1\x7e\x4b\x91\x12"
+                         "\x3a\x8b\x5d\x50\x82\x7b\xcb\xd9"
+                         "\x9a\xd9\x4e\x18\x06\x23\x9e\xd4"
+                         "\xa5\x20\x98\xef\xb5\xda\xe5\xc0"
+                         "\x8a\x6a\x83\x77\x15\x84\x1e\xae"
+                         "\x78\x94\x9d\xdf\xb7\xd1\xea\x67"
+                         "\xaa\xb0\x14\x15\xfa\x67\x21\x84"
+                         "\xd3\x41\x2a\xce\xba\x4b\x4a\xe8"
+                         "\x95\x62\xa9\x55\xf0\x80\xad\xbd"
+                         "\xab\xaf\xdd\x4f\xa5\x7c\x13\x36"
+                         "\xed\x5e\x4f\x72\xad\x4b\xf1\xd0"
+                         "\x88\x4e\xec\x2c\x88\x10\x5e\xea"
+                         "\x12\xc0\x16\x01\x29\xa3\xa0\x55"
+                         "\xaa\x68\xf3\xe9\x9d\x3b\x0d\x3b"
+                         "\x6d\xec\xf8\xa0\x2d\xf0\x90\x8d"
+                         "\x1c\xe2\x88\xd4\x24\x71\xf9\xb3"
+                         "\xc1\x9f\xc5\xd6\x76\x70\xc5\x2e"
+                         "\x9c\xac\xdb\x90\xbd\x83\x72\xba"
+                         "\x6e\xb5\xa5\x53\x83\xa9\xa5\xbf"
+                         "\x7d\x06\x0e\x3c\x2a\xd2\x04\xb5"
+                         "\x1e\x19\x38\x09\x16\xd2\x82\x1f"
+                         "\x75\x18\x56\xb8\x96\x0b\xa6\xf9"
+                         "\xcf\x62\xd9\x32\x5d\xa9\xd7\x1d"
+                         "\xec\xe4\xdf\x1b\xbe\xf1\x36\xee"
+                         "\xe3\x7b\xb5\x2f\xee\xf8\x53\x3d"
+                         "\x6a\xb7\x70\xa9\xfc\x9c\x57\x25"
+                         "\xf2\x89\x10\xd3\xb8\xa8\x8c\x30"
+                         "\xae\x23\x4f\x0e\x13\x66\x4f\xe1"
+                         "\xb6\xc0\xe4\xf8\xef\x93\xbd\x6e"
+                         "\x15\x85\x6b\xe3\x60\x81\x1d\x68"
+                         "\xd7\x31\x87\x89\x09\xab\xd5\x96"
+                         "\x1d\xf3\x6d\x67\x80\xca\x07\x31"
+                         "\x5d\xa7\xe4\xfb\x3e\xf2\x9b\x33"
+                         "\x52\x18\xc8\x30\xfe\x2d\xca\x1e"
+                         "\x79\x92\x7a\x60\x5c\xb6\x58\x87"
+                         "\xa4\x36\xa2\x67\x92\x8b\xa4\xb7"
+                         "\xf1\x86\xdf\xdc\xc0\x7e\x8f\x63"
+                         "\xd2\xa2\xdc\x78\xeb\x4f\xd8\x96"
+                         "\x47\xca\xb8\x91\xf9\xf7\x94\x21"
+                         "\x5f\x9a\x9f\x5b\xb8\x40\x41\x4b"
+                         "\x66\x69\x6a\x72\xd0\xcb\x70\xb7"
+                         "\x93\xb5\x37\x96\x05\x37\x4f\xe5"
+                         "\x8c\xa7\x5a\x4e\x8b\xb7\x84\xea"
+                         "\xc7\xfc\x19\x6e\x1f\x5a\xa1\xac"
+                         "\x18\x7d\x52\x3b\xb3\x34\x62\x99"
+                         "\xe4\x9e\x31\x04\x3f\xc0\x8d\x84"
+                         "\x17\x7c\x25\x48\x52\x67\x11\x27"
+                         "\x67\xbb\x5a\x85\xca\x56\xb2\x5c"
+                         "\xe6\xec\xd5\x96\x3d\x15\xfc\xfb"
+                         "\x22\x25\xf4\x13\xe5\x93\x4b\x9a"
+                         "\x77\xf1\x52\x18\xfa\x16\x5e\x49"
+                         "\x03\x45\xa8\x08\xfa\xb3\x41\x92"
+                         "\x79\x50\x33\xca\xd0\xd7\x42\x55"
+                         "\xc3\x9a\x0c\x4e\xd9\xa4\x3c\x86"
+                         "\x80\x9f\x53\xd1\xa4\x2e\xd1\xbc"
+                         "\xf1\x54\x6e\x93\xa4\x65\x99\x8e"
+                         "\xdf\x29\xc0\x64\x63\x07\xbb\xea",
+               .ctext  = "\xe0\x33\xf6\xe0\xb4\xa5\xdd\x2b"
+                         "\xdd\xce\xfc\x12\x1e\xfc\x2d\xf2"
+                         "\x8b\xc7\xeb\xc1\xc4\x2a\xe8\x44"
+                         "\x0f\x3d\x97\x19\x2e\x6d\xa2\x38"
+                         "\x9d\xa6\xaa\xe1\x96\xb9\x08\xe8"
+                         "\x0b\x70\x48\x5c\xed\xb5\x9b\xcb"
+                         "\x8b\x40\x88\x7e\x69\x73\xf7\x16"
+                         "\x71\xbb\x5b\xfc\xa3\x47\x5d\xa6"
+                         "\xae\x3a\x64\xc4\xe7\xb8\xa8\xe7"
+                         "\xb1\x32\x19\xdb\xe3\x01\xb8\xf0"
+                         "\xa4\x86\xb4\x4c\xc2\xde\x5c\xd2"
+                         "\x6c\x77\xd2\xe8\x18\xb7\x0a\xc9"
+                         "\x3d\x53\xb5\xc4\x5c\xf0\x8c\x06"
+                         "\xdc\x90\xe0\x74\x47\x1b\x0b\xf6"
+                         "\xd2\x71\x6b\xc4\xf1\x97\x00\x2d"
+                         "\x63\x57\x44\x1f\x8c\xf4\xe6\x9b"
+                         "\xe0\x7a\xdd\xec\x32\x73\x42\x32"
+                         "\x7f\x35\x67\x60\x0d\xcf\x10\x52"
+                         "\x61\x22\x53\x8d\x8e\xbb\x33\x76"
+                         "\x59\xd9\x10\xce\xdf\xef\xc0\x41"
+                         "\xd5\x33\x29\x6a\xda\x46\xa4\x51"
+                         "\xf0\x99\x3d\x96\x31\xdd\xb5\xcb"
+                         "\x3e\x2a\x1f\xc7\x5c\x79\xd3\xc5"
+                         "\x20\xa1\xb1\x39\x1b\xc6\x0a\x70"
+                         "\x26\x39\x95\x07\xad\x7a\xc9\x69"
+                         "\xfe\x81\xc7\x88\x08\x38\xaf\xad"
+                         "\x9e\x8d\xfb\xe8\x24\x0d\x22\xb8"
+                         "\x0e\xed\xbe\x37\x53\x7c\xa6\xc6"
+                         "\x78\x62\xec\xa3\x59\xd9\xc6\x9d"
+                         "\xb8\x0e\x69\x77\x84\x2d\x6a\x4c"
+                         "\xc5\xd9\xb2\xa0\x2b\xa8\x80\xcc"
+                         "\xe9\x1e\x9c\x5a\xc4\xa1\xb2\x37"
+                         "\x06\x9b\x30\x32\x67\xf7\xe7\xd2"
+                         "\x42\xc7\xdf\x4e\xd4\xcb\xa0\x12"
+                         "\x94\xa1\x34\x85\x93\x50\x4b\x0a"
+                         "\x3c\x7d\x49\x25\x01\x41\x6b\x96"
+                         "\xa9\x12\xbb\x0b\xc0\xd7\xd0\x93"
+                         "\x1f\x70\x38\xb8\x21\xee\xf6\xa7"
+                         "\xee\xeb\xe7\x81\xa4\x13\xb4\x87"
+                         "\xfa\xc1\xb0\xb5\x37\x8b\x74\xa2"
+                         "\x4e\xc7\xc2\xad\x3d\x62\x3f\xf8"
+                         "\x34\x42\xe5\xae\x45\x13\x63\xfe"
+                         "\xfc\x2a\x17\x46\x61\xa9\xd3\x1c"
+                         "\x4c\xaf\xf0\x09\x62\x26\x66\x1e"
+                         "\x74\xcf\xd6\x68\x3d\x7d\xd8\xb7"
+                         "\xe7\xe6\xf8\xf0\x08\x20\xf7\x47"
+                         "\x1c\x52\xaa\x0f\x3e\x21\xa3\xf2"
+                         "\xbf\x2f\x95\x16\xa8\xc8\xc8\x8c"
+                         "\x99\x0f\x5d\xfb\xfa\x2b\x58\x8a"
+                         "\x7e\xd6\x74\x02\x60\xf0\xd0\x5b"
+                         "\x65\xa8\xac\xea\x8d\x68\x46\x34"
+                         "\x26\x9d\x4f\xb1\x9a\x8e\xc0\x1a"
+                         "\xf1\xed\xc6\x7a\x83\xfd\x8a\x57"
+                         "\xf2\xe6\xe4\xba\xfc\xc6\x3c\xad"
+                         "\x5b\x19\x50\x2f\x3a\xcc\x06\x46"
+                         "\x04\x51\x3f\x91\x97\xf0\xd2\x07"
+                         "\xe7\x93\x89\x7e\xb5\x32\x0f\x03"
+                         "\xe5\x58\x9e\x74\x72\xeb\xc2\x38"
+                         "\x00\x0c\x91\x72\x69\xed\x7d\x6d"
+                         "\xc8\x71\xf0\xec\xff\x80\xd9\x1c"
+                         "\x9e\xd2\xfa\x15\xfc\x6c\x4e\xbc"
+                         "\xb1\xa6\xbd\xbd\x70\x40\xca\x20"
+                         "\xb8\x78\xd2\xa3\xc6\xf3\x79\x9c"
+                         "\xc7\x27\xe1\x6a\x29\xad\xa4\x03",
+               .len    = 512,
+       }
+};
+
 /*
  * CTS (Cipher Text Stealing) mode tests
  */