18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Key Wrapping: RFC3394 / NIST SP800-38F
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2015, Stephan Mueller <smueller@chronox.de>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
78c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
88c2ecf20Sopenharmony_ci * are met:
98c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
108c2ecf20Sopenharmony_ci *    notice, and the entire permission notice in its entirety,
118c2ecf20Sopenharmony_ci *    including the disclaimer of warranties.
128c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
138c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
148c2ecf20Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
158c2ecf20Sopenharmony_ci * 3. The name of the author may not be used to endorse or promote
168c2ecf20Sopenharmony_ci *    products derived from this software without specific prior
178c2ecf20Sopenharmony_ci *    written permission.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * ALTERNATIVELY, this product may be distributed under the terms of
208c2ecf20Sopenharmony_ci * the GNU General Public License, in which case the provisions of the GPL2
218c2ecf20Sopenharmony_ci * are required INSTEAD OF the above restrictions.  (This clause is
228c2ecf20Sopenharmony_ci * necessary due to a potential bad interaction between the GPL and
238c2ecf20Sopenharmony_ci * the restrictions contained in a BSD-style copyright.)
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
268c2ecf20Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
278c2ecf20Sopenharmony_ci * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
288c2ecf20Sopenharmony_ci * WHICH ARE HEREBY DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE
298c2ecf20Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
308c2ecf20Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
318c2ecf20Sopenharmony_ci * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
328c2ecf20Sopenharmony_ci * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
338c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
348c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
358c2ecf20Sopenharmony_ci * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
368c2ecf20Sopenharmony_ci * DAMAGE.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/*
408c2ecf20Sopenharmony_ci * Note for using key wrapping:
418c2ecf20Sopenharmony_ci *
428c2ecf20Sopenharmony_ci *	* The result of the encryption operation is the ciphertext starting
438c2ecf20Sopenharmony_ci *	  with the 2nd semiblock. The first semiblock is provided as the IV.
448c2ecf20Sopenharmony_ci *	  The IV used to start the encryption operation is the default IV.
458c2ecf20Sopenharmony_ci *
468c2ecf20Sopenharmony_ci *	* The input for the decryption is the first semiblock handed in as an
478c2ecf20Sopenharmony_ci *	  IV. The ciphertext is the data starting with the 2nd semiblock. The
488c2ecf20Sopenharmony_ci *	  return code of the decryption operation will be EBADMSG in case an
498c2ecf20Sopenharmony_ci *	  integrity error occurs.
508c2ecf20Sopenharmony_ci *
518c2ecf20Sopenharmony_ci * To obtain the full result of an encryption as expected by SP800-38F, the
528c2ecf20Sopenharmony_ci * caller must allocate a buffer of plaintext + 8 bytes:
538c2ecf20Sopenharmony_ci *
548c2ecf20Sopenharmony_ci *	unsigned int datalen = ptlen + crypto_skcipher_ivsize(tfm);
558c2ecf20Sopenharmony_ci *	u8 data[datalen];
568c2ecf20Sopenharmony_ci *	u8 *iv = data;
578c2ecf20Sopenharmony_ci *	u8 *pt = data + crypto_skcipher_ivsize(tfm);
588c2ecf20Sopenharmony_ci *		<ensure that pt contains the plaintext of size ptlen>
598c2ecf20Sopenharmony_ci *	sg_init_one(&sg, pt, ptlen);
608c2ecf20Sopenharmony_ci *	skcipher_request_set_crypt(req, &sg, &sg, ptlen, iv);
618c2ecf20Sopenharmony_ci *
628c2ecf20Sopenharmony_ci *	==> After encryption, data now contains full KW result as per SP800-38F.
638c2ecf20Sopenharmony_ci *
648c2ecf20Sopenharmony_ci * In case of decryption, ciphertext now already has the expected length
658c2ecf20Sopenharmony_ci * and must be segmented appropriately:
668c2ecf20Sopenharmony_ci *
678c2ecf20Sopenharmony_ci *	unsigned int datalen = CTLEN;
688c2ecf20Sopenharmony_ci *	u8 data[datalen];
698c2ecf20Sopenharmony_ci *		<ensure that data contains full ciphertext>
708c2ecf20Sopenharmony_ci *	u8 *iv = data;
718c2ecf20Sopenharmony_ci *	u8 *ct = data + crypto_skcipher_ivsize(tfm);
728c2ecf20Sopenharmony_ci *	unsigned int ctlen = datalen - crypto_skcipher_ivsize(tfm);
738c2ecf20Sopenharmony_ci *	sg_init_one(&sg, ct, ctlen);
748c2ecf20Sopenharmony_ci *	skcipher_request_set_crypt(req, &sg, &sg, ctlen, iv);
758c2ecf20Sopenharmony_ci *
768c2ecf20Sopenharmony_ci *	==> After decryption (which hopefully does not return EBADMSG), the ct
778c2ecf20Sopenharmony_ci *	pointer now points to the plaintext of size ctlen.
788c2ecf20Sopenharmony_ci *
798c2ecf20Sopenharmony_ci * Note 2: KWP is not implemented as this would defy in-place operation.
808c2ecf20Sopenharmony_ci *	   If somebody wants to wrap non-aligned data, he should simply pad
818c2ecf20Sopenharmony_ci *	   the input with zeros to fill it up to the 8 byte boundary.
828c2ecf20Sopenharmony_ci */
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#include <linux/module.h>
858c2ecf20Sopenharmony_ci#include <linux/crypto.h>
868c2ecf20Sopenharmony_ci#include <linux/scatterlist.h>
878c2ecf20Sopenharmony_ci#include <crypto/scatterwalk.h>
888c2ecf20Sopenharmony_ci#include <crypto/internal/skcipher.h>
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistruct crypto_kw_block {
918c2ecf20Sopenharmony_ci#define SEMIBSIZE 8
928c2ecf20Sopenharmony_ci	__be64 A;
938c2ecf20Sopenharmony_ci	__be64 R;
948c2ecf20Sopenharmony_ci};
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/*
978c2ecf20Sopenharmony_ci * Fast forward the SGL to the "end" length minus SEMIBSIZE.
988c2ecf20Sopenharmony_ci * The start in the SGL defined by the fast-forward is returned with
998c2ecf20Sopenharmony_ci * the walk variable
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_cistatic void crypto_kw_scatterlist_ff(struct scatter_walk *walk,
1028c2ecf20Sopenharmony_ci				     struct scatterlist *sg,
1038c2ecf20Sopenharmony_ci				     unsigned int end)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	unsigned int skip = 0;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* The caller should only operate on full SEMIBLOCKs. */
1088c2ecf20Sopenharmony_ci	BUG_ON(end < SEMIBSIZE);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	skip = end - SEMIBSIZE;
1118c2ecf20Sopenharmony_ci	while (sg) {
1128c2ecf20Sopenharmony_ci		if (sg->length > skip) {
1138c2ecf20Sopenharmony_ci			scatterwalk_start(walk, sg);
1148c2ecf20Sopenharmony_ci			scatterwalk_advance(walk, skip);
1158c2ecf20Sopenharmony_ci			break;
1168c2ecf20Sopenharmony_ci		} else
1178c2ecf20Sopenharmony_ci			skip -= sg->length;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		sg = sg_next(sg);
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int crypto_kw_decrypt(struct skcipher_request *req)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
1268c2ecf20Sopenharmony_ci	struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
1278c2ecf20Sopenharmony_ci	struct crypto_kw_block block;
1288c2ecf20Sopenharmony_ci	struct scatterlist *src, *dst;
1298c2ecf20Sopenharmony_ci	u64 t = 6 * ((req->cryptlen) >> 3);
1308c2ecf20Sopenharmony_ci	unsigned int i;
1318c2ecf20Sopenharmony_ci	int ret = 0;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/*
1348c2ecf20Sopenharmony_ci	 * Require at least 2 semiblocks (note, the 3rd semiblock that is
1358c2ecf20Sopenharmony_ci	 * required by SP800-38F is the IV.
1368c2ecf20Sopenharmony_ci	 */
1378c2ecf20Sopenharmony_ci	if (req->cryptlen < (2 * SEMIBSIZE) || req->cryptlen % SEMIBSIZE)
1388c2ecf20Sopenharmony_ci		return -EINVAL;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* Place the IV into block A */
1418c2ecf20Sopenharmony_ci	memcpy(&block.A, req->iv, SEMIBSIZE);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/*
1448c2ecf20Sopenharmony_ci	 * src scatterlist is read-only. dst scatterlist is r/w. During the
1458c2ecf20Sopenharmony_ci	 * first loop, src points to req->src and dst to req->dst. For any
1468c2ecf20Sopenharmony_ci	 * subsequent round, the code operates on req->dst only.
1478c2ecf20Sopenharmony_ci	 */
1488c2ecf20Sopenharmony_ci	src = req->src;
1498c2ecf20Sopenharmony_ci	dst = req->dst;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++) {
1528c2ecf20Sopenharmony_ci		struct scatter_walk src_walk, dst_walk;
1538c2ecf20Sopenharmony_ci		unsigned int nbytes = req->cryptlen;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci		while (nbytes) {
1568c2ecf20Sopenharmony_ci			/* move pointer by nbytes in the SGL */
1578c2ecf20Sopenharmony_ci			crypto_kw_scatterlist_ff(&src_walk, src, nbytes);
1588c2ecf20Sopenharmony_ci			/* get the source block */
1598c2ecf20Sopenharmony_ci			scatterwalk_copychunks(&block.R, &src_walk, SEMIBSIZE,
1608c2ecf20Sopenharmony_ci					       false);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci			/* perform KW operation: modify IV with counter */
1638c2ecf20Sopenharmony_ci			block.A ^= cpu_to_be64(t);
1648c2ecf20Sopenharmony_ci			t--;
1658c2ecf20Sopenharmony_ci			/* perform KW operation: decrypt block */
1668c2ecf20Sopenharmony_ci			crypto_cipher_decrypt_one(cipher, (u8 *)&block,
1678c2ecf20Sopenharmony_ci						  (u8 *)&block);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci			/* move pointer by nbytes in the SGL */
1708c2ecf20Sopenharmony_ci			crypto_kw_scatterlist_ff(&dst_walk, dst, nbytes);
1718c2ecf20Sopenharmony_ci			/* Copy block->R into place */
1728c2ecf20Sopenharmony_ci			scatterwalk_copychunks(&block.R, &dst_walk, SEMIBSIZE,
1738c2ecf20Sopenharmony_ci					       true);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci			nbytes -= SEMIBSIZE;
1768c2ecf20Sopenharmony_ci		}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci		/* we now start to operate on the dst SGL only */
1798c2ecf20Sopenharmony_ci		src = req->dst;
1808c2ecf20Sopenharmony_ci		dst = req->dst;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* Perform authentication check */
1848c2ecf20Sopenharmony_ci	if (block.A != cpu_to_be64(0xa6a6a6a6a6a6a6a6ULL))
1858c2ecf20Sopenharmony_ci		ret = -EBADMSG;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	memzero_explicit(&block, sizeof(struct crypto_kw_block));
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	return ret;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int crypto_kw_encrypt(struct skcipher_request *req)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
1958c2ecf20Sopenharmony_ci	struct crypto_cipher *cipher = skcipher_cipher_simple(tfm);
1968c2ecf20Sopenharmony_ci	struct crypto_kw_block block;
1978c2ecf20Sopenharmony_ci	struct scatterlist *src, *dst;
1988c2ecf20Sopenharmony_ci	u64 t = 1;
1998c2ecf20Sopenharmony_ci	unsigned int i;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/*
2028c2ecf20Sopenharmony_ci	 * Require at least 2 semiblocks (note, the 3rd semiblock that is
2038c2ecf20Sopenharmony_ci	 * required by SP800-38F is the IV that occupies the first semiblock.
2048c2ecf20Sopenharmony_ci	 * This means that the dst memory must be one semiblock larger than src.
2058c2ecf20Sopenharmony_ci	 * Also ensure that the given data is aligned to semiblock.
2068c2ecf20Sopenharmony_ci	 */
2078c2ecf20Sopenharmony_ci	if (req->cryptlen < (2 * SEMIBSIZE) || req->cryptlen % SEMIBSIZE)
2088c2ecf20Sopenharmony_ci		return -EINVAL;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/*
2118c2ecf20Sopenharmony_ci	 * Place the predefined IV into block A -- for encrypt, the caller
2128c2ecf20Sopenharmony_ci	 * does not need to provide an IV, but he needs to fetch the final IV.
2138c2ecf20Sopenharmony_ci	 */
2148c2ecf20Sopenharmony_ci	block.A = cpu_to_be64(0xa6a6a6a6a6a6a6a6ULL);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	/*
2178c2ecf20Sopenharmony_ci	 * src scatterlist is read-only. dst scatterlist is r/w. During the
2188c2ecf20Sopenharmony_ci	 * first loop, src points to req->src and dst to req->dst. For any
2198c2ecf20Sopenharmony_ci	 * subsequent round, the code operates on req->dst only.
2208c2ecf20Sopenharmony_ci	 */
2218c2ecf20Sopenharmony_ci	src = req->src;
2228c2ecf20Sopenharmony_ci	dst = req->dst;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++) {
2258c2ecf20Sopenharmony_ci		struct scatter_walk src_walk, dst_walk;
2268c2ecf20Sopenharmony_ci		unsigned int nbytes = req->cryptlen;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		scatterwalk_start(&src_walk, src);
2298c2ecf20Sopenharmony_ci		scatterwalk_start(&dst_walk, dst);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		while (nbytes) {
2328c2ecf20Sopenharmony_ci			/* get the source block */
2338c2ecf20Sopenharmony_ci			scatterwalk_copychunks(&block.R, &src_walk, SEMIBSIZE,
2348c2ecf20Sopenharmony_ci					       false);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci			/* perform KW operation: encrypt block */
2378c2ecf20Sopenharmony_ci			crypto_cipher_encrypt_one(cipher, (u8 *)&block,
2388c2ecf20Sopenharmony_ci						  (u8 *)&block);
2398c2ecf20Sopenharmony_ci			/* perform KW operation: modify IV with counter */
2408c2ecf20Sopenharmony_ci			block.A ^= cpu_to_be64(t);
2418c2ecf20Sopenharmony_ci			t++;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci			/* Copy block->R into place */
2448c2ecf20Sopenharmony_ci			scatterwalk_copychunks(&block.R, &dst_walk, SEMIBSIZE,
2458c2ecf20Sopenharmony_ci					       true);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci			nbytes -= SEMIBSIZE;
2488c2ecf20Sopenharmony_ci		}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci		/* we now start to operate on the dst SGL only */
2518c2ecf20Sopenharmony_ci		src = req->dst;
2528c2ecf20Sopenharmony_ci		dst = req->dst;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/* establish the IV for the caller to pick up */
2568c2ecf20Sopenharmony_ci	memcpy(req->iv, &block.A, SEMIBSIZE);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	memzero_explicit(&block, sizeof(struct crypto_kw_block));
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return 0;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic int crypto_kw_create(struct crypto_template *tmpl, struct rtattr **tb)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	struct skcipher_instance *inst;
2668c2ecf20Sopenharmony_ci	struct crypto_alg *alg;
2678c2ecf20Sopenharmony_ci	int err;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	inst = skcipher_alloc_instance_simple(tmpl, tb);
2708c2ecf20Sopenharmony_ci	if (IS_ERR(inst))
2718c2ecf20Sopenharmony_ci		return PTR_ERR(inst);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	alg = skcipher_ialg_simple(inst);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	err = -EINVAL;
2768c2ecf20Sopenharmony_ci	/* Section 5.1 requirement for KW */
2778c2ecf20Sopenharmony_ci	if (alg->cra_blocksize != sizeof(struct crypto_kw_block))
2788c2ecf20Sopenharmony_ci		goto out_free_inst;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	inst->alg.base.cra_blocksize = SEMIBSIZE;
2818c2ecf20Sopenharmony_ci	inst->alg.base.cra_alignmask = 0;
2828c2ecf20Sopenharmony_ci	inst->alg.ivsize = SEMIBSIZE;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	inst->alg.encrypt = crypto_kw_encrypt;
2858c2ecf20Sopenharmony_ci	inst->alg.decrypt = crypto_kw_decrypt;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	err = skcipher_register_instance(tmpl, inst);
2888c2ecf20Sopenharmony_ci	if (err) {
2898c2ecf20Sopenharmony_ciout_free_inst:
2908c2ecf20Sopenharmony_ci		inst->free(inst);
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	return err;
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic struct crypto_template crypto_kw_tmpl = {
2978c2ecf20Sopenharmony_ci	.name = "kw",
2988c2ecf20Sopenharmony_ci	.create = crypto_kw_create,
2998c2ecf20Sopenharmony_ci	.module = THIS_MODULE,
3008c2ecf20Sopenharmony_ci};
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic int __init crypto_kw_init(void)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	return crypto_register_template(&crypto_kw_tmpl);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic void __exit crypto_kw_exit(void)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	crypto_unregister_template(&crypto_kw_tmpl);
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cisubsys_initcall(crypto_kw_init);
3138c2ecf20Sopenharmony_cimodule_exit(crypto_kw_exit);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
3168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
3178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Key Wrapping (RFC3394 / NIST SP800-38F)");
3188c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("kw");
319