18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Michael MIC implementation - optimized for TKIP MIC operations
48c2ecf20Sopenharmony_ci * Copyright 2002-2003, Instant802 Networks, Inc.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <linux/types.h>
78c2ecf20Sopenharmony_ci#include <linux/bitops.h>
88c2ecf20Sopenharmony_ci#include <linux/ieee80211.h>
98c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "michael.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic void michael_block(struct michael_mic_ctx *mctx, u32 val)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	mctx->l ^= val;
168c2ecf20Sopenharmony_ci	mctx->r ^= rol32(mctx->l, 17);
178c2ecf20Sopenharmony_ci	mctx->l += mctx->r;
188c2ecf20Sopenharmony_ci	mctx->r ^= ((mctx->l & 0xff00ff00) >> 8) |
198c2ecf20Sopenharmony_ci		   ((mctx->l & 0x00ff00ff) << 8);
208c2ecf20Sopenharmony_ci	mctx->l += mctx->r;
218c2ecf20Sopenharmony_ci	mctx->r ^= rol32(mctx->l, 3);
228c2ecf20Sopenharmony_ci	mctx->l += mctx->r;
238c2ecf20Sopenharmony_ci	mctx->r ^= ror32(mctx->l, 2);
248c2ecf20Sopenharmony_ci	mctx->l += mctx->r;
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic void michael_mic_hdr(struct michael_mic_ctx *mctx, const u8 *key,
288c2ecf20Sopenharmony_ci			    struct ieee80211_hdr *hdr)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	u8 *da, *sa, tid;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	da = ieee80211_get_DA(hdr);
338c2ecf20Sopenharmony_ci	sa = ieee80211_get_SA(hdr);
348c2ecf20Sopenharmony_ci	if (ieee80211_is_data_qos(hdr->frame_control))
358c2ecf20Sopenharmony_ci		tid = ieee80211_get_tid(hdr);
368c2ecf20Sopenharmony_ci	else
378c2ecf20Sopenharmony_ci		tid = 0;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	mctx->l = get_unaligned_le32(key);
408c2ecf20Sopenharmony_ci	mctx->r = get_unaligned_le32(key + 4);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/*
438c2ecf20Sopenharmony_ci	 * A pseudo header (DA, SA, Priority, 0, 0, 0) is used in Michael MIC
448c2ecf20Sopenharmony_ci	 * calculation, but it is _not_ transmitted
458c2ecf20Sopenharmony_ci	 */
468c2ecf20Sopenharmony_ci	michael_block(mctx, get_unaligned_le32(da));
478c2ecf20Sopenharmony_ci	michael_block(mctx, get_unaligned_le16(&da[4]) |
488c2ecf20Sopenharmony_ci			    (get_unaligned_le16(sa) << 16));
498c2ecf20Sopenharmony_ci	michael_block(mctx, get_unaligned_le32(&sa[2]));
508c2ecf20Sopenharmony_ci	michael_block(mctx, tid);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_civoid michael_mic(const u8 *key, struct ieee80211_hdr *hdr,
548c2ecf20Sopenharmony_ci		 const u8 *data, size_t data_len, u8 *mic)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	u32 val;
578c2ecf20Sopenharmony_ci	size_t block, blocks, left;
588c2ecf20Sopenharmony_ci	struct michael_mic_ctx mctx;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	michael_mic_hdr(&mctx, key, hdr);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/* Real data */
638c2ecf20Sopenharmony_ci	blocks = data_len / 4;
648c2ecf20Sopenharmony_ci	left = data_len % 4;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	for (block = 0; block < blocks; block++)
678c2ecf20Sopenharmony_ci		michael_block(&mctx, get_unaligned_le32(&data[block * 4]));
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* Partial block of 0..3 bytes and padding: 0x5a + 4..7 zeros to make
708c2ecf20Sopenharmony_ci	 * total length a multiple of 4. */
718c2ecf20Sopenharmony_ci	val = 0x5a;
728c2ecf20Sopenharmony_ci	while (left > 0) {
738c2ecf20Sopenharmony_ci		val <<= 8;
748c2ecf20Sopenharmony_ci		left--;
758c2ecf20Sopenharmony_ci		val |= data[blocks * 4 + left];
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	michael_block(&mctx, val);
798c2ecf20Sopenharmony_ci	michael_block(&mctx, 0);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	put_unaligned_le32(mctx.l, mic);
828c2ecf20Sopenharmony_ci	put_unaligned_le32(mctx.r, mic + 4);
838c2ecf20Sopenharmony_ci}
84