linux/net/mac80211/michael.c
<<
>>
Prefs
   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 <asm/unaligned.h>
  10
  11#include "michael.h"
  12
  13static 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
  27static 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
  53void 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