xref: /linux/crypto/michael_mic.c (revision e1b2d980)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Cryptographic API
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Michael MIC (IEEE 802.11i/TKIP) keyed digest
61da177e4SLinus Torvalds  *
785d32e7bSJouni Malinen  * Copyright (c) 2004 Jouni Malinen <j@w1.fi>
81da177e4SLinus Torvalds  */
919e2bf14SAdrian-Ken Rueegsegger #include <crypto/internal/hash.h>
10*e1b2d980SArd Biesheuvel #include <asm/unaligned.h>
111da177e4SLinus Torvalds #include <linux/init.h>
121da177e4SLinus Torvalds #include <linux/module.h>
131da177e4SLinus Torvalds #include <linux/string.h>
1406ace7a9SHerbert Xu #include <linux/types.h>
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds struct michael_mic_ctx {
1819e2bf14SAdrian-Ken Rueegsegger 	u32 l, r;
1919e2bf14SAdrian-Ken Rueegsegger };
2019e2bf14SAdrian-Ken Rueegsegger 
2119e2bf14SAdrian-Ken Rueegsegger struct michael_mic_desc_ctx {
22*e1b2d980SArd Biesheuvel 	__le32 pending;
231da177e4SLinus Torvalds 	size_t pending_len;
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds 	u32 l, r;
261da177e4SLinus Torvalds };
271da177e4SLinus Torvalds 
xswap(u32 val)281da177e4SLinus Torvalds static inline u32 xswap(u32 val)
291da177e4SLinus Torvalds {
301da177e4SLinus Torvalds 	return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8);
311da177e4SLinus Torvalds }
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds #define michael_block(l, r)	\
351da177e4SLinus Torvalds do {				\
361da177e4SLinus Torvalds 	r ^= rol32(l, 17);	\
371da177e4SLinus Torvalds 	l += r;			\
381da177e4SLinus Torvalds 	r ^= xswap(l);		\
391da177e4SLinus Torvalds 	l += r;			\
401da177e4SLinus Torvalds 	r ^= rol32(l, 3);	\
411da177e4SLinus Torvalds 	l += r;			\
421da177e4SLinus Torvalds 	r ^= ror32(l, 2);	\
431da177e4SLinus Torvalds 	l += r;			\
441da177e4SLinus Torvalds } while (0)
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds 
michael_init(struct shash_desc * desc)4719e2bf14SAdrian-Ken Rueegsegger static int michael_init(struct shash_desc *desc)
481da177e4SLinus Torvalds {
4919e2bf14SAdrian-Ken Rueegsegger 	struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc);
5019e2bf14SAdrian-Ken Rueegsegger 	struct michael_mic_ctx *ctx = crypto_shash_ctx(desc->tfm);
511da177e4SLinus Torvalds 	mctx->pending_len = 0;
5219e2bf14SAdrian-Ken Rueegsegger 	mctx->l = ctx->l;
5319e2bf14SAdrian-Ken Rueegsegger 	mctx->r = ctx->r;
5419e2bf14SAdrian-Ken Rueegsegger 
5519e2bf14SAdrian-Ken Rueegsegger 	return 0;
561da177e4SLinus Torvalds }
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 
michael_update(struct shash_desc * desc,const u8 * data,unsigned int len)5919e2bf14SAdrian-Ken Rueegsegger static int michael_update(struct shash_desc *desc, const u8 *data,
606c2bb98bSHerbert Xu 			   unsigned int len)
611da177e4SLinus Torvalds {
6219e2bf14SAdrian-Ken Rueegsegger 	struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc);
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds 	if (mctx->pending_len) {
651da177e4SLinus Torvalds 		int flen = 4 - mctx->pending_len;
661da177e4SLinus Torvalds 		if (flen > len)
671da177e4SLinus Torvalds 			flen = len;
68*e1b2d980SArd Biesheuvel 		memcpy((u8 *)&mctx->pending + mctx->pending_len, data, flen);
691da177e4SLinus Torvalds 		mctx->pending_len += flen;
701da177e4SLinus Torvalds 		data += flen;
711da177e4SLinus Torvalds 		len -= flen;
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 		if (mctx->pending_len < 4)
7419e2bf14SAdrian-Ken Rueegsegger 			return 0;
751da177e4SLinus Torvalds 
76*e1b2d980SArd Biesheuvel 		mctx->l ^= le32_to_cpu(mctx->pending);
771da177e4SLinus Torvalds 		michael_block(mctx->l, mctx->r);
781da177e4SLinus Torvalds 		mctx->pending_len = 0;
791da177e4SLinus Torvalds 	}
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds 	while (len >= 4) {
82*e1b2d980SArd Biesheuvel 		mctx->l ^= get_unaligned_le32(data);
831da177e4SLinus Torvalds 		michael_block(mctx->l, mctx->r);
84*e1b2d980SArd Biesheuvel 		data += 4;
851da177e4SLinus Torvalds 		len -= 4;
861da177e4SLinus Torvalds 	}
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 	if (len > 0) {
891da177e4SLinus Torvalds 		mctx->pending_len = len;
90*e1b2d980SArd Biesheuvel 		memcpy(&mctx->pending, data, len);
911da177e4SLinus Torvalds 	}
9219e2bf14SAdrian-Ken Rueegsegger 
9319e2bf14SAdrian-Ken Rueegsegger 	return 0;
941da177e4SLinus Torvalds }
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds 
michael_final(struct shash_desc * desc,u8 * out)9719e2bf14SAdrian-Ken Rueegsegger static int michael_final(struct shash_desc *desc, u8 *out)
981da177e4SLinus Torvalds {
9919e2bf14SAdrian-Ken Rueegsegger 	struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc);
100*e1b2d980SArd Biesheuvel 	u8 *data = (u8 *)&mctx->pending;
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds 	/* Last block and padding (0x5a, 4..7 x 0) */
1031da177e4SLinus Torvalds 	switch (mctx->pending_len) {
1041da177e4SLinus Torvalds 	case 0:
1051da177e4SLinus Torvalds 		mctx->l ^= 0x5a;
1061da177e4SLinus Torvalds 		break;
1071da177e4SLinus Torvalds 	case 1:
1081da177e4SLinus Torvalds 		mctx->l ^= data[0] | 0x5a00;
1091da177e4SLinus Torvalds 		break;
1101da177e4SLinus Torvalds 	case 2:
1111da177e4SLinus Torvalds 		mctx->l ^= data[0] | (data[1] << 8) | 0x5a0000;
1121da177e4SLinus Torvalds 		break;
1131da177e4SLinus Torvalds 	case 3:
1141da177e4SLinus Torvalds 		mctx->l ^= data[0] | (data[1] << 8) | (data[2] << 16) |
1151da177e4SLinus Torvalds 			0x5a000000;
1161da177e4SLinus Torvalds 		break;
1171da177e4SLinus Torvalds 	}
1181da177e4SLinus Torvalds 	michael_block(mctx->l, mctx->r);
1191da177e4SLinus Torvalds 	/* l ^= 0; */
1201da177e4SLinus Torvalds 	michael_block(mctx->l, mctx->r);
1211da177e4SLinus Torvalds 
122*e1b2d980SArd Biesheuvel 	put_unaligned_le32(mctx->l, out);
123*e1b2d980SArd Biesheuvel 	put_unaligned_le32(mctx->r, out + 4);
12419e2bf14SAdrian-Ken Rueegsegger 
12519e2bf14SAdrian-Ken Rueegsegger 	return 0;
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds 
michael_setkey(struct crypto_shash * tfm,const u8 * key,unsigned int keylen)12919e2bf14SAdrian-Ken Rueegsegger static int michael_setkey(struct crypto_shash *tfm, const u8 *key,
130560c06aeSHerbert Xu 			  unsigned int keylen)
1311da177e4SLinus Torvalds {
13219e2bf14SAdrian-Ken Rueegsegger 	struct michael_mic_ctx *mctx = crypto_shash_ctx(tfm);
13319e2bf14SAdrian-Ken Rueegsegger 
134674f368aSEric Biggers 	if (keylen != 8)
1351da177e4SLinus Torvalds 		return -EINVAL;
13606ace7a9SHerbert Xu 
137*e1b2d980SArd Biesheuvel 	mctx->l = get_unaligned_le32(key);
138*e1b2d980SArd Biesheuvel 	mctx->r = get_unaligned_le32(key + 4);
1391da177e4SLinus Torvalds 	return 0;
1401da177e4SLinus Torvalds }
1411da177e4SLinus Torvalds 
14219e2bf14SAdrian-Ken Rueegsegger static struct shash_alg alg = {
14319e2bf14SAdrian-Ken Rueegsegger 	.digestsize		=	8,
14419e2bf14SAdrian-Ken Rueegsegger 	.setkey			=	michael_setkey,
14519e2bf14SAdrian-Ken Rueegsegger 	.init			=	michael_init,
14619e2bf14SAdrian-Ken Rueegsegger 	.update			=	michael_update,
14719e2bf14SAdrian-Ken Rueegsegger 	.final			=	michael_final,
14819e2bf14SAdrian-Ken Rueegsegger 	.descsize		=	sizeof(struct michael_mic_desc_ctx),
14919e2bf14SAdrian-Ken Rueegsegger 	.base			=	{
1501da177e4SLinus Torvalds 		.cra_name		=	"michael_mic",
151d6ebf528SEric Biggers 		.cra_driver_name	=	"michael_mic-generic",
1521da177e4SLinus Torvalds 		.cra_blocksize		=	8,
1531da177e4SLinus Torvalds 		.cra_ctxsize		=	sizeof(struct michael_mic_ctx),
1541da177e4SLinus Torvalds 		.cra_module		=	THIS_MODULE,
15519e2bf14SAdrian-Ken Rueegsegger 	}
1561da177e4SLinus Torvalds };
1571da177e4SLinus Torvalds 
michael_mic_init(void)1581da177e4SLinus Torvalds static int __init michael_mic_init(void)
1591da177e4SLinus Torvalds {
16019e2bf14SAdrian-Ken Rueegsegger 	return crypto_register_shash(&alg);
1611da177e4SLinus Torvalds }
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds 
michael_mic_exit(void)1641da177e4SLinus Torvalds static void __exit michael_mic_exit(void)
1651da177e4SLinus Torvalds {
16619e2bf14SAdrian-Ken Rueegsegger 	crypto_unregister_shash(&alg);
1671da177e4SLinus Torvalds }
1681da177e4SLinus Torvalds 
1691da177e4SLinus Torvalds 
170c4741b23SEric Biggers subsys_initcall(michael_mic_init);
1711da177e4SLinus Torvalds module_exit(michael_mic_exit);
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds MODULE_LICENSE("GPL v2");
1741da177e4SLinus Torvalds MODULE_DESCRIPTION("Michael MIC");
17585d32e7bSJouni Malinen MODULE_AUTHOR("Jouni Malinen <j@w1.fi>");
1765d26a105SKees Cook MODULE_ALIAS_CRYPTO("michael_mic");
177