162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Cryptographic API 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Michael MIC (IEEE 802.11i/TKIP) keyed digest 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2004 Jouni Malinen <j@w1.fi> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <crypto/internal/hash.h> 1062306a36Sopenharmony_ci#include <asm/unaligned.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct michael_mic_ctx { 1862306a36Sopenharmony_ci u32 l, r; 1962306a36Sopenharmony_ci}; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct michael_mic_desc_ctx { 2262306a36Sopenharmony_ci __le32 pending; 2362306a36Sopenharmony_ci size_t pending_len; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci u32 l, r; 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic inline u32 xswap(u32 val) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define michael_block(l, r) \ 3562306a36Sopenharmony_cido { \ 3662306a36Sopenharmony_ci r ^= rol32(l, 17); \ 3762306a36Sopenharmony_ci l += r; \ 3862306a36Sopenharmony_ci r ^= xswap(l); \ 3962306a36Sopenharmony_ci l += r; \ 4062306a36Sopenharmony_ci r ^= rol32(l, 3); \ 4162306a36Sopenharmony_ci l += r; \ 4262306a36Sopenharmony_ci r ^= ror32(l, 2); \ 4362306a36Sopenharmony_ci l += r; \ 4462306a36Sopenharmony_ci} while (0) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int michael_init(struct shash_desc *desc) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc); 5062306a36Sopenharmony_ci struct michael_mic_ctx *ctx = crypto_shash_ctx(desc->tfm); 5162306a36Sopenharmony_ci mctx->pending_len = 0; 5262306a36Sopenharmony_ci mctx->l = ctx->l; 5362306a36Sopenharmony_ci mctx->r = ctx->r; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return 0; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic int michael_update(struct shash_desc *desc, const u8 *data, 6062306a36Sopenharmony_ci unsigned int len) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (mctx->pending_len) { 6562306a36Sopenharmony_ci int flen = 4 - mctx->pending_len; 6662306a36Sopenharmony_ci if (flen > len) 6762306a36Sopenharmony_ci flen = len; 6862306a36Sopenharmony_ci memcpy((u8 *)&mctx->pending + mctx->pending_len, data, flen); 6962306a36Sopenharmony_ci mctx->pending_len += flen; 7062306a36Sopenharmony_ci data += flen; 7162306a36Sopenharmony_ci len -= flen; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (mctx->pending_len < 4) 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci mctx->l ^= le32_to_cpu(mctx->pending); 7762306a36Sopenharmony_ci michael_block(mctx->l, mctx->r); 7862306a36Sopenharmony_ci mctx->pending_len = 0; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci while (len >= 4) { 8262306a36Sopenharmony_ci mctx->l ^= get_unaligned_le32(data); 8362306a36Sopenharmony_ci michael_block(mctx->l, mctx->r); 8462306a36Sopenharmony_ci data += 4; 8562306a36Sopenharmony_ci len -= 4; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (len > 0) { 8962306a36Sopenharmony_ci mctx->pending_len = len; 9062306a36Sopenharmony_ci memcpy(&mctx->pending, data, len); 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int michael_final(struct shash_desc *desc, u8 *out) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc); 10062306a36Sopenharmony_ci u8 *data = (u8 *)&mctx->pending; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* Last block and padding (0x5a, 4..7 x 0) */ 10362306a36Sopenharmony_ci switch (mctx->pending_len) { 10462306a36Sopenharmony_ci case 0: 10562306a36Sopenharmony_ci mctx->l ^= 0x5a; 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci case 1: 10862306a36Sopenharmony_ci mctx->l ^= data[0] | 0x5a00; 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci case 2: 11162306a36Sopenharmony_ci mctx->l ^= data[0] | (data[1] << 8) | 0x5a0000; 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci case 3: 11462306a36Sopenharmony_ci mctx->l ^= data[0] | (data[1] << 8) | (data[2] << 16) | 11562306a36Sopenharmony_ci 0x5a000000; 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci michael_block(mctx->l, mctx->r); 11962306a36Sopenharmony_ci /* l ^= 0; */ 12062306a36Sopenharmony_ci michael_block(mctx->l, mctx->r); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci put_unaligned_le32(mctx->l, out); 12362306a36Sopenharmony_ci put_unaligned_le32(mctx->r, out + 4); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int michael_setkey(struct crypto_shash *tfm, const u8 *key, 13062306a36Sopenharmony_ci unsigned int keylen) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct michael_mic_ctx *mctx = crypto_shash_ctx(tfm); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (keylen != 8) 13562306a36Sopenharmony_ci return -EINVAL; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci mctx->l = get_unaligned_le32(key); 13862306a36Sopenharmony_ci mctx->r = get_unaligned_le32(key + 4); 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic struct shash_alg alg = { 14362306a36Sopenharmony_ci .digestsize = 8, 14462306a36Sopenharmony_ci .setkey = michael_setkey, 14562306a36Sopenharmony_ci .init = michael_init, 14662306a36Sopenharmony_ci .update = michael_update, 14762306a36Sopenharmony_ci .final = michael_final, 14862306a36Sopenharmony_ci .descsize = sizeof(struct michael_mic_desc_ctx), 14962306a36Sopenharmony_ci .base = { 15062306a36Sopenharmony_ci .cra_name = "michael_mic", 15162306a36Sopenharmony_ci .cra_driver_name = "michael_mic-generic", 15262306a36Sopenharmony_ci .cra_blocksize = 8, 15362306a36Sopenharmony_ci .cra_ctxsize = sizeof(struct michael_mic_ctx), 15462306a36Sopenharmony_ci .cra_module = THIS_MODULE, 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int __init michael_mic_init(void) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci return crypto_register_shash(&alg); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void __exit michael_mic_exit(void) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci crypto_unregister_shash(&alg); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cisubsys_initcall(michael_mic_init); 17162306a36Sopenharmony_cimodule_exit(michael_mic_exit); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 17462306a36Sopenharmony_ciMODULE_DESCRIPTION("Michael MIC"); 17562306a36Sopenharmony_ciMODULE_AUTHOR("Jouni Malinen <j@w1.fi>"); 17662306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("michael_mic"); 177