1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Michael MIC implementation - optimized for TKIP MIC operations 4 * Copyright 2002-2003, Instant802 Networks, Inc. 5 */ 6 #include <linux/types.h> 7 #include <linux/bitops.h> 8 #include <linux/ieee80211.h> 9 #include <linux/unaligned.h> 10 11 #include "michael.h" 12 13 static void michael_block(struct michael_mic_ctx *mctx, u32 val) 14 { 15 mctx->l ^= val; 16 mctx->r ^= rol32(mctx->l, 17); 17 mctx->l += mctx->r; 18 mctx->r ^= ((mctx->l & 0xff00ff00) >> 8) | 19 ((mctx->l & 0x00ff00ff) << 8); 20 mctx->l += mctx->r; 21 mctx->r ^= rol32(mctx->l, 3); 22 mctx->l += mctx->r; 23 mctx->r ^= ror32(mctx->l, 2); 24 mctx->l += mctx->r; 25 } 26 27 static void michael_mic_hdr(struct michael_mic_ctx *mctx, const u8 *key, 28 struct ieee80211_hdr *hdr) 29 { 30 u8 *da, *sa, tid; 31 32 da = ieee80211_get_DA(hdr); 33 sa = ieee80211_get_SA(hdr); 34 if (ieee80211_is_data_qos(hdr->frame_control)) 35 tid = ieee80211_get_tid(hdr); 36 else 37 tid = 0; 38 39 mctx->l = get_unaligned_le32(key); 40 mctx->r = get_unaligned_le32(key + 4); 41 42 /* 43 * A pseudo header (DA, SA, Priority, 0, 0, 0) is used in Michael MIC 44 * calculation, but it is _not_ transmitted 45 */ 46 michael_block(mctx, get_unaligned_le32(da)); 47 michael_block(mctx, get_unaligned_le16(&da[4]) | 48 (get_unaligned_le16(sa) << 16)); 49 michael_block(mctx, get_unaligned_le32(&sa[2])); 50 michael_block(mctx, tid); 51 } 52 53 void michael_mic(const u8 *key, struct ieee80211_hdr *hdr, 54 const u8 *data, size_t data_len, u8 *mic) 55 { 56 u32 val; 57 size_t block, blocks, left; 58 struct michael_mic_ctx mctx; 59 60 michael_mic_hdr(&mctx, key, hdr); 61 62 /* Real data */ 63 blocks = data_len / 4; 64 left = data_len % 4; 65 66 for (block = 0; block < blocks; block++) 67 michael_block(&mctx, get_unaligned_le32(&data[block * 4])); 68 69 /* Partial block of 0..3 bytes and padding: 0x5a + 4..7 zeros to make 70 * total length a multiple of 4. */ 71 val = 0x5a; 72 while (left > 0) { 73 val <<= 8; 74 left--; 75 val |= data[blocks * 4 + left]; 76 } 77 78 michael_block(&mctx, val); 79 michael_block(&mctx, 0); 80 81 put_unaligned_le32(mctx.l, mic); 82 put_unaligned_le32(mctx.r, mic + 4); 83 } 84