blob: c1e5897fcf7ca2936756b87921d8926849809144 [file] [log] [blame]
Jiri Bencf0706e82007-05-05 11:45:53 -07001/*
2 * Michael MIC implementation - optimized for TKIP MIC operations
3 * Copyright 2002-2003, Instant802 Networks, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9
10#include <linux/types.h>
Harvey Harrison1bd3dff2008-05-14 16:26:16 -070011#include <linux/bitops.h>
12#include <asm/unaligned.h>
Jiri Bencf0706e82007-05-05 11:45:53 -070013
14#include "michael.h"
15
Jiri Bencf0706e82007-05-05 11:45:53 -070016#define michael_block(l, r) \
17do { \
Harvey Harrison1bd3dff2008-05-14 16:26:16 -070018 r ^= rol32(l, 17); \
Jiri Bencf0706e82007-05-05 11:45:53 -070019 l += r; \
Harvey Harrison1bd3dff2008-05-14 16:26:16 -070020 r ^= ((l & 0xff00ff00) >> 8) | ((l & 0x00ff00ff) << 8); \
Jiri Bencf0706e82007-05-05 11:45:53 -070021 l += r; \
Harvey Harrison1bd3dff2008-05-14 16:26:16 -070022 r ^= rol32(l, 3); \
Jiri Bencf0706e82007-05-05 11:45:53 -070023 l += r; \
Harvey Harrison1bd3dff2008-05-14 16:26:16 -070024 r ^= ror32(l, 2); \
Jiri Bencf0706e82007-05-05 11:45:53 -070025 l += r; \
26} while (0)
27
Jiri Bencf0706e82007-05-05 11:45:53 -070028void michael_mic(u8 *key, u8 *da, u8 *sa, u8 priority,
29 u8 *data, size_t data_len, u8 *mic)
30{
31 u32 l, r, val;
32 size_t block, blocks, left;
33
Harvey Harrison1bd3dff2008-05-14 16:26:16 -070034 l = get_unaligned_le32(key);
35 r = get_unaligned_le32(key + 4);
Jiri Bencf0706e82007-05-05 11:45:53 -070036
37 /* A pseudo header (DA, SA, Priority, 0, 0, 0) is used in Michael MIC
38 * calculation, but it is _not_ transmitted */
Harvey Harrison1bd3dff2008-05-14 16:26:16 -070039 l ^= get_unaligned_le32(da);
Jiri Bencf0706e82007-05-05 11:45:53 -070040 michael_block(l, r);
Harvey Harrison1bd3dff2008-05-14 16:26:16 -070041 l ^= get_unaligned_le16(&da[4]) | (get_unaligned_le16(sa) << 16);
Jiri Bencf0706e82007-05-05 11:45:53 -070042 michael_block(l, r);
Harvey Harrison1bd3dff2008-05-14 16:26:16 -070043 l ^= get_unaligned_le32(&sa[2]);
Jiri Bencf0706e82007-05-05 11:45:53 -070044 michael_block(l, r);
45 l ^= priority;
46 michael_block(l, r);
47
48 /* Real data */
49 blocks = data_len / 4;
50 left = data_len % 4;
51
52 for (block = 0; block < blocks; block++) {
Harvey Harrison1bd3dff2008-05-14 16:26:16 -070053 l ^= get_unaligned_le32(&data[block * 4]);
Jiri Bencf0706e82007-05-05 11:45:53 -070054 michael_block(l, r);
55 }
56
57 /* Partial block of 0..3 bytes and padding: 0x5a + 4..7 zeros to make
58 * total length a multiple of 4. */
59 val = 0x5a;
60 while (left > 0) {
61 val <<= 8;
62 left--;
63 val |= data[blocks * 4 + left];
64 }
65 l ^= val;
66 michael_block(l, r);
67 /* last block is zero, so l ^ 0 = l */
68 michael_block(l, r);
69
Harvey Harrison1bd3dff2008-05-14 16:26:16 -070070 put_unaligned_le32(l, mic);
71 put_unaligned_le32(r, mic + 4);
Jiri Bencf0706e82007-05-05 11:45:53 -070072}