michael_mic.c 3.65 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5
/*
 * Cryptographic API
 *
 * Michael MIC (IEEE 802.11i/TKIP) keyed digest
 *
6
 * Copyright (c) 2004 Jouni Malinen <j@w1.fi>
Linus Torvalds's avatar
Linus Torvalds committed
7 8 9 10 11
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
12
#include <crypto/internal/hash.h>
13
#include <asm/byteorder.h>
Linus Torvalds's avatar
Linus Torvalds committed
14 15 16
#include <linux/init.h>
#include <linux/module.h>
#include <linux/string.h>
17
#include <linux/types.h>
Linus Torvalds's avatar
Linus Torvalds committed
18 19 20


struct michael_mic_ctx {
21 22 23 24
	u32 l, r;
};

struct michael_mic_desc_ctx {
Linus Torvalds's avatar
Linus Torvalds committed
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
	u8 pending[4];
	size_t pending_len;

	u32 l, r;
};

static inline u32 xswap(u32 val)
{
	return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8);
}


#define michael_block(l, r)	\
do {				\
	r ^= rol32(l, 17);	\
	l += r;			\
	r ^= xswap(l);		\
	l += r;			\
	r ^= rol32(l, 3);	\
	l += r;			\
	r ^= ror32(l, 2);	\
	l += r;			\
} while (0)


50
static int michael_init(struct shash_desc *desc)
Linus Torvalds's avatar
Linus Torvalds committed
51
{
52 53
	struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc);
	struct michael_mic_ctx *ctx = crypto_shash_ctx(desc->tfm);
Linus Torvalds's avatar
Linus Torvalds committed
54
	mctx->pending_len = 0;
55 56 57 58
	mctx->l = ctx->l;
	mctx->r = ctx->r;

	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
59 60 61
}


62
static int michael_update(struct shash_desc *desc, const u8 *data,
63
			   unsigned int len)
Linus Torvalds's avatar
Linus Torvalds committed
64
{
65
	struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc);
66
	const __le32 *src;
Linus Torvalds's avatar
Linus Torvalds committed
67 68 69 70 71 72 73 74 75 76 77

	if (mctx->pending_len) {
		int flen = 4 - mctx->pending_len;
		if (flen > len)
			flen = len;
		memcpy(&mctx->pending[mctx->pending_len], data, flen);
		mctx->pending_len += flen;
		data += flen;
		len -= flen;

		if (mctx->pending_len < 4)
78
			return 0;
Linus Torvalds's avatar
Linus Torvalds committed
79

80 81
		src = (const __le32 *)mctx->pending;
		mctx->l ^= le32_to_cpup(src);
Linus Torvalds's avatar
Linus Torvalds committed
82 83 84 85
		michael_block(mctx->l, mctx->r);
		mctx->pending_len = 0;
	}

86 87
	src = (const __le32 *)data;

Linus Torvalds's avatar
Linus Torvalds committed
88
	while (len >= 4) {
89
		mctx->l ^= le32_to_cpup(src++);
Linus Torvalds's avatar
Linus Torvalds committed
90 91 92 93 94 95
		michael_block(mctx->l, mctx->r);
		len -= 4;
	}

	if (len > 0) {
		mctx->pending_len = len;
96
		memcpy(mctx->pending, src, len);
Linus Torvalds's avatar
Linus Torvalds committed
97
	}
98 99

	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
100 101 102
}


103
static int michael_final(struct shash_desc *desc, u8 *out)
Linus Torvalds's avatar
Linus Torvalds committed
104
{
105
	struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc);
Linus Torvalds's avatar
Linus Torvalds committed
106
	u8 *data = mctx->pending;
107
	__le32 *dst = (__le32 *)out;
Linus Torvalds's avatar
Linus Torvalds committed
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128

	/* Last block and padding (0x5a, 4..7 x 0) */
	switch (mctx->pending_len) {
	case 0:
		mctx->l ^= 0x5a;
		break;
	case 1:
		mctx->l ^= data[0] | 0x5a00;
		break;
	case 2:
		mctx->l ^= data[0] | (data[1] << 8) | 0x5a0000;
		break;
	case 3:
		mctx->l ^= data[0] | (data[1] << 8) | (data[2] << 16) |
			0x5a000000;
		break;
	}
	michael_block(mctx->l, mctx->r);
	/* l ^= 0; */
	michael_block(mctx->l, mctx->r);

129 130
	dst[0] = cpu_to_le32(mctx->l);
	dst[1] = cpu_to_le32(mctx->r);
131 132

	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
133 134 135
}


136
static int michael_setkey(struct crypto_shash *tfm, const u8 *key,
137
			  unsigned int keylen)
Linus Torvalds's avatar
Linus Torvalds committed
138
{
139 140
	struct michael_mic_ctx *mctx = crypto_shash_ctx(tfm);

141 142
	const __le32 *data = (const __le32 *)key;

Linus Torvalds's avatar
Linus Torvalds committed
143
	if (keylen != 8) {
144
		crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
Linus Torvalds's avatar
Linus Torvalds committed
145 146
		return -EINVAL;
	}
147 148 149

	mctx->l = le32_to_cpu(data[0]);
	mctx->r = le32_to_cpu(data[1]);
Linus Torvalds's avatar
Linus Torvalds committed
150 151 152
	return 0;
}

153 154 155 156 157 158 159 160 161 162 163 164 165 166
static struct shash_alg alg = {
	.digestsize		=	8,
	.setkey			=	michael_setkey,
	.init			=	michael_init,
	.update			=	michael_update,
	.final			=	michael_final,
	.descsize		=	sizeof(struct michael_mic_desc_ctx),
	.base			=	{
		.cra_name		=	"michael_mic",
		.cra_blocksize		=	8,
		.cra_alignmask		=	3,
		.cra_ctxsize		=	sizeof(struct michael_mic_ctx),
		.cra_module		=	THIS_MODULE,
	}
Linus Torvalds's avatar
Linus Torvalds committed
167 168 169 170
};

static int __init michael_mic_init(void)
{
171
	return crypto_register_shash(&alg);
Linus Torvalds's avatar
Linus Torvalds committed
172 173 174 175 176
}


static void __exit michael_mic_exit(void)
{
177
	crypto_unregister_shash(&alg);
Linus Torvalds's avatar
Linus Torvalds committed
178 179 180 181 182 183 184 185
}


module_init(michael_mic_init);
module_exit(michael_mic_exit);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Michael MIC");
186
MODULE_AUTHOR("Jouni Malinen <j@w1.fi>");
187
MODULE_ALIAS_CRYPTO("michael_mic");