2007-10-04 17:07:51 +04:00
|
|
|
/*-
|
|
|
|
* Copyright (c) 2004 Sam Leffler, Errno Consulting
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* Alternatively, this software may be distributed under the terms of the
|
|
|
|
* GNU General Public License ("GPL") version 2 as published by the Free
|
|
|
|
* Software Foundation.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* $Id$
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TKIP test module.
|
|
|
|
*/
|
|
|
|
#ifndef AUTOCONF_INCLUDED
|
|
|
|
#include <linux/config.h>
|
|
|
|
#endif
|
|
|
|
#include <linux/version.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include "if_media.h"
|
|
|
|
#include <net80211/ieee80211_var.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
Key 12 34 56 78 90 12 34 56 78 90 12 34 56 78 90 12
|
|
|
|
34 56 78 90 12 34 56 78 90 12 34 56 78 90 12 34
|
|
|
|
PN 0x000000000001
|
|
|
|
IV 00 20 01 20 00 00 00 00
|
|
|
|
Phase1 bb 58 07 1f 9e 93 b4 38 25 4b
|
|
|
|
Phase2 00 20 01 4c fe 67 be d2 7c 86 7b 1b f8 02 8b 1c
|
|
|
|
*/
|
|
|
|
|
|
|
|
static const u_int8_t ref_key[] = {
|
|
|
|
0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
|
|
|
|
0x34, 0x56, 0x78, 0x90, 0x12,
|
|
|
|
|
|
|
|
0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, /* TX MIC */
|
|
|
|
/*
|
|
|
|
* NB: 11i test vector specifies a RX MIC key different
|
|
|
|
* from the TX key. But this doesn't work to enmic,
|
|
|
|
* encrypt, then decrypt, demic. So instead we use
|
|
|
|
* the same key for doing the MIC in each direction.
|
|
|
|
*
|
|
|
|
* XXX need additional vectors to test alternate MIC keys
|
|
|
|
*/
|
|
|
|
#if 0
|
|
|
|
0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, /* 11i RX MIC */
|
|
|
|
#else
|
|
|
|
0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, /* TX copy */
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
static const u_int8_t ref_phase1[] = {
|
|
|
|
0xbb, 0x58, 0x07, 0x1f, 0x9e, 0x93, 0xb4, 0x38, 0x25, 0x4b
|
|
|
|
};
|
|
|
|
static const u_int8_t ref_phase2[] = {
|
|
|
|
0x00, 0x20, 0x01, 0x4c, 0xfe, 0x67, 0xbe, 0xd2, 0x7c, 0x86,
|
|
|
|
0x7b, 0x1b, 0xf8, 0x02, 0x8b, 0x1c,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Plaintext MPDU with MIC */
|
|
|
|
static const u_int8_t ref_plaintext[] = {
|
|
|
|
0x08,0x42,0x2c,0x00,0x02,0x03,0x04,0x05,0x06,0x08,0x02,0x03,0x04,0x05,0x06,0x07,
|
|
|
|
0x02,0x03,0x04,0x05,0x06,0x07,0xd0,0x02,
|
|
|
|
0xaa,0xaa,0x03,0x00,0x00,0x00,0x08,0x00,0x45,0x00,0x00,0x54,0x00,0x00,0x40,0x00,
|
|
|
|
0x40,0x01,0xa5,0x55,0xc0,0xa8,0x0a,0x02,0xc0,0xa8,0x0a,0x01,0x08,0x00,0x3a,0xb0,
|
|
|
|
0x00,0x00,0x00,0x00,0xcd,0x4c,0x05,0x00,0x00,0x00,0x00,0x00,0x08,0x09,0x0a,0x0b,
|
|
|
|
0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,
|
|
|
|
0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,
|
|
|
|
0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
|
|
|
|
/* MIC */ 0x68,0x81,0xa3,0xf3,0xd6,0x48,0xd0,0x3c
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Encrypted MPDU with MIC and ICV */
|
|
|
|
static const u_int8_t ref_encrypted[] = {
|
|
|
|
0x08,0x42,0x2c,0x00,0x02,0x03,0x04,0x05,0x06,0x08,0x02,0x03,0x04,0x05,0x06,0x07,
|
|
|
|
0x02,0x03,0x04,0x05,0x06,0x07,0xd0,0x02,0x00,0x20,0x01,0x20,0x00,0x00,0x00,0x00,
|
|
|
|
0xc0,0x0e,0x14,0xfc,0xe7,0xcf,0xab,0xc7,0x75,0x47,0xe6,0x66,0xe5,0x7c,0x0d,0xac,
|
|
|
|
0x70,0x4a,0x1e,0x35,0x8a,0x88,0xc1,0x1c,0x8e,0x2e,0x28,0x2e,0x38,0x01,0x02,0x7a,
|
|
|
|
0x46,0x56,0x05,0x5e,0xe9,0x3e,0x9c,0x25,0x47,0x02,0xe9,0x73,0x58,0x05,0xdd,0xb5,
|
|
|
|
0x76,0x9b,0xa7,0x3f,0x1e,0xbb,0x56,0xe8,0x44,0xef,0x91,0x22,0x85,0xd3,0xdd,0x6e,
|
|
|
|
0x54,0x1e,0x82,0x38,0x73,0x55,0x8a,0xdb,0xa0,0x79,0x06,0x8a,0xbd,0x7f,0x7f,0x50,
|
|
|
|
0x95,0x96,0x75,0xac,0xc4,0xb4,0xde,0x9a,0xa9,0x9c,0x05,0xf2,0x89,0xa7,0xc5,0x2f,
|
|
|
|
0xee,0x5b,0xfc,0x14,0xf6,0xf8,0xe5,0xf8
|
|
|
|
};
|
|
|
|
|
|
|
|
struct tkip_ctx {
|
|
|
|
struct ieee80211com *tc_ic; /* for diagnostics */
|
|
|
|
|
|
|
|
u16 tx_ttak[5];
|
|
|
|
int tx_phase1_done;
|
|
|
|
u8 tx_rc4key[16];
|
|
|
|
|
|
|
|
u16 rx_ttak[5];
|
|
|
|
int rx_phase1_done;
|
|
|
|
u8 rx_rc4key[16];
|
|
|
|
u_int64_t rx_rsc; /* held until MIC verified */
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
dumpdata(const char *tag, const void *p, size_t len)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
printk("%s: 0x%p len %u", tag, p, len);
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
if ((i % 16) == 0)
|
|
|
|
printk("\n%03d:", i);
|
|
|
|
printk(" %02x", ((u_int8_t *)p)[i]);
|
|
|
|
}
|
|
|
|
printk("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cmpfail(const void *gen, size_t genlen, const void *ref, size_t reflen)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < genlen; i++)
|
|
|
|
if (((u_int8_t *)gen)[i] != ((u_int8_t *)ref)[i]) {
|
|
|
|
printk("first difference at byte %u\n", i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
dumpdata("Generated", gen, genlen);
|
|
|
|
dumpdata("Reference", ref, reflen);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
tkip_test(struct ieee80211com *ic)
|
|
|
|
{
|
|
|
|
struct tkip_ctx *ctx;
|
|
|
|
struct ieee80211_key key;
|
|
|
|
struct sk_buff *skb = NULL;
|
|
|
|
const struct ieee80211_cipher *cip;
|
|
|
|
u_int8_t mac[IEEE80211_ADDR_LEN];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup key.
|
|
|
|
*/
|
|
|
|
memset(&key, 0, sizeof(key));
|
|
|
|
key.wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
|
|
|
|
key.wk_cipher = &ieee80211_cipher_none;
|
|
|
|
if (!ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_TKIP, &key)) {
|
|
|
|
printk("ieee80211_crypto_newkey failed\n");
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(key.wk_key, ref_key, sizeof(ref_key));
|
|
|
|
key.wk_keylen = 128 / NBBY;
|
|
|
|
key.wk_keyrsc = 0;
|
|
|
|
key.wk_keytsc = 0;
|
|
|
|
if (!ieee80211_crypto_setkey(ic, &key, mac)) {
|
|
|
|
printk("ieee80211_crypto_setkey failed\n");
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Craft frame from plaintext data. Note that
|
|
|
|
* we leave the MIC off as we'll add it ourself
|
|
|
|
* and then check it against the reference data.
|
|
|
|
*/
|
|
|
|
cip = key.wk_cipher;
|
2007-11-21 23:14:11 +03:00
|
|
|
skb = ieee80211_dev_alloc_skb(sizeof(ref_plaintext) +
|
2007-10-04 17:07:51 +04:00
|
|
|
cip->ic_miclen + cip->ic_header + cip->ic_trailer);
|
|
|
|
if (skb == NULL) {
|
|
|
|
printk("unable to allocate skbuff\n");
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
skb_reserve(skb, cip->ic_header);
|
|
|
|
memcpy(skb_put(skb, sizeof(ref_plaintext) - cip->ic_miclen),
|
|
|
|
ref_plaintext, sizeof(ref_plaintext) - cip->ic_miclen);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add MIC.
|
|
|
|
*/
|
|
|
|
if (!ieee80211_crypto_enmic(ic, &key, skb)) {
|
|
|
|
printk("tkip enmic failed\n");
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Verify: frame length, frame contents.
|
|
|
|
*/
|
|
|
|
if (skb->len != sizeof(ref_plaintext)) {
|
|
|
|
printk("enmic botch; length mismatch\n");
|
|
|
|
cmpfail(skb->data, skb->len,
|
|
|
|
ref_plaintext, sizeof(ref_plaintext));
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
if (memcmp(skb->data, ref_plaintext, sizeof(ref_plaintext))) {
|
|
|
|
printk("enmic botch\n");
|
|
|
|
cmpfail(skb->data, skb->len,
|
|
|
|
ref_plaintext, sizeof(ref_plaintext));
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Encrypt frame w/ MIC.
|
|
|
|
*/
|
|
|
|
if (!(*cip->ic_encap)(&key, skb, 0 << 6)) {
|
|
|
|
printk("tkip encap failed\n");
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Verify: phase1, phase2, frame length, frame contents.
|
|
|
|
*/
|
|
|
|
ctx = key.wk_private;
|
|
|
|
if (memcmp(ctx->tx_ttak, ref_phase1, sizeof(ref_phase1))) {
|
|
|
|
printk("encrypt phase1 botch\n");
|
|
|
|
cmpfail(ctx->tx_ttak, sizeof(ctx->tx_ttak),
|
|
|
|
ref_phase1, sizeof(ref_phase1));
|
|
|
|
goto bad;
|
|
|
|
} else if (memcmp(ctx->tx_rc4key, ref_phase2, sizeof(ref_phase2))) {
|
|
|
|
printf("encrypt phase2 botch\n");
|
|
|
|
cmpfail(ctx->tx_rc4key, sizeof(ctx->tx_rc4key),
|
|
|
|
ref_phase2, sizeof(ref_phase2));
|
|
|
|
goto bad;
|
|
|
|
} else if (skb->len != sizeof(ref_encrypted)) {
|
|
|
|
printk("encrypt data length mismatch\n");
|
|
|
|
cmpfail(skb->data, skb->len,
|
|
|
|
ref_encrypted, sizeof(ref_encrypted));
|
|
|
|
goto bad;
|
|
|
|
} else if (memcmp(skb->data, ref_encrypted, skb->len)) {
|
|
|
|
printk("encrypt data does not compare\n");
|
|
|
|
cmpfail(skb->data, skb->len,
|
|
|
|
ref_encrypted, sizeof(ref_encrypted));
|
|
|
|
dumpdata("Plaintext", ref_plaintext, sizeof(ref_plaintext));
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decrypt frame.
|
|
|
|
*/
|
|
|
|
if (!(*cip->ic_decap)(&key, skb)) {
|
|
|
|
printk("tkip decap failed\n");
|
|
|
|
/*
|
|
|
|
* Check reason for failure: phase1, phase2, frame data (ICV).
|
|
|
|
*/
|
|
|
|
if (memcmp(ctx->rx_ttak, ref_phase1, sizeof(ref_phase1))) {
|
|
|
|
printk("decrypt phase1 botch\n");
|
|
|
|
cmpfail(ctx->rx_ttak, sizeof(ctx->rx_ttak),
|
|
|
|
ref_phase1, sizeof(ref_phase1));
|
|
|
|
} else if (memcmp(ctx->rx_rc4key, ref_phase2, sizeof(ref_phase2))) {
|
|
|
|
printf("decrypt phase2 botch\n");
|
|
|
|
cmpfail(ctx->rx_rc4key, sizeof(ctx->rx_rc4key),
|
|
|
|
ref_phase2, sizeof(ref_phase2));
|
|
|
|
} else {
|
|
|
|
printk("decrypt data does not compare\n");
|
|
|
|
cmpfail(skb->data, skb->len,
|
|
|
|
ref_plaintext, sizeof(ref_plaintext));
|
|
|
|
}
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Verify: frame length, frame contents.
|
|
|
|
*/
|
|
|
|
if (skb->len != sizeof(ref_plaintext)) {
|
|
|
|
printk("decap botch; length mismatch\n");
|
|
|
|
cmpfail(skb->data, skb->len,
|
|
|
|
ref_plaintext, sizeof(ref_plaintext));
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
if (memcmp(skb->data, ref_plaintext, sizeof(ref_plaintext))) {
|
|
|
|
printk("decap botch; data does not compare\n");
|
|
|
|
cmpfail(skb->data, skb->len,
|
|
|
|
ref_plaintext, sizeof(ref_plaintext));
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* De-MIC decrypted frame.
|
|
|
|
*/
|
|
|
|
if (!ieee80211_crypto_demic(ic, &key, skb)) {
|
|
|
|
printk("tkip demic failed\n");
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
/* XXX check frame length and contents... */
|
|
|
|
printk("802.11i TKIP test vectors passed\n");
|
|
|
|
bad:
|
|
|
|
if (skb != NULL)
|
2007-11-21 23:14:11 +03:00
|
|
|
ieee80211_dev_kfree_skb(&skb);
|
2007-10-04 17:07:51 +04:00
|
|
|
ieee80211_crypto_delkey(ic, &key);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Module glue.
|
|
|
|
*/
|
|
|
|
MODULE_AUTHOR("Errno Consulting, Sam Leffler");
|
|
|
|
MODULE_DESCRIPTION("802.11 wireless support: TKIP cipher tester");
|
|
|
|
#ifdef MODULE_LICENSE
|
|
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int debug = 0;
|
|
|
|
MODULE_PARM(debug, "i");
|
|
|
|
MODULE_PARM_DESC(debug, "Enable IEEE80211_MSG_CRYPTO");
|
|
|
|
|
|
|
|
static int __init
|
|
|
|
init_crypto_tkip_test(void)
|
|
|
|
{
|
|
|
|
struct ieee80211com ic;
|
|
|
|
|
|
|
|
memset(&ic, 0, sizeof(ic));
|
|
|
|
if (debug)
|
|
|
|
ic.msg_enable = IEEE80211_MSG_CRYPTO;
|
|
|
|
ieee80211_crypto_attach(&ic);
|
|
|
|
|
|
|
|
tkip_test(&ic);
|
|
|
|
|
|
|
|
ieee80211_crypto_detach(&ic);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
module_init(init_crypto_tkip_test);
|
|
|
|
|
|
|
|
static void __exit
|
|
|
|
exit_crypto_tkip_test(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
module_exit(exit_crypto_tkip_test);
|