/*-
 * 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: test_wep.c 3846 2008-08-27 15:58:30Z proski $
 */

/*
 * WEP test module.
 *
 * Test vectors come from section I.7.2 of P802.11i/D7.0, October 2003.
 *
 * To use this tester load the net80211 layer (either as a module or
 * by statically configuring it into your kernel), then insmod this
 * module.  It should automatically run all test cases and print
 * information for each.  To run one or more tests you can specify a
 * tests parameter to the module that is a bit mask of the set of tests
 * you want; e.g. insmod wep_test tests=7 will run only test MPDUs
 * 1, 2, and 3.
 */
#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>

/*
MPDU data
 aa aa 03 00 00 00 08 00 45 00 00 4e 66 1a 00 00 80 11 be 64 0a 00 01 22
 0a ff ff ff 00 89 00 89 00 3a 00 00 80 a6 01 10 00 01 00 00 00 00 00 00
 20 45 43 45 4a 45 48 45 43 46 43 45 50 46 45 45 49 45 46 46 43 43 41 43
 41 43 41 43 41 43 41 41 41 00 00 20 00 01

RC4 encryption is performed as follows:
17
18  Key  fb 02 9e 30 31 32 33 34 
Plaintext
 aa aa 03 00 00 00 08 00 45 00 00 4e 66 1a 00 00 80 11 be 64 0a 00 01
 22 0a ff ff ff 00 89 00 89 00 3a 00 00 80 a6 01 10 00 01 00 00 00 00
 00 00 20 45 43 45 4a 45 48 45 43 46 43 45 50 46 45 45 49 45 46 46 43
 43 41 43 41 43 41 43 41 43 41 41 41 00 00 20 00 01 1b d0 b6 04
Ciphertext
 f6 9c 58 06 bd 6c e8 46 26 bc be fb 94 74 65 0a ad 1f 79 09 b0 f6 4d
 5f 58 a5 03 a2 58 b7 ed 22 eb 0e a6 49 30 d3 a0 56 a5 57 42 fc ce 14
 1d 48 5f 8a a8 36 de a1 8d f4 2c 53 80 80 5a d0 c6 1a 5d 6f 58 f4 10
 40 b2 4b 7d 1a 69 38 56 ed 0d 43 98 e7 ae e3 bf 0e 2a 2c a8 f7
The plaintext consists of the MPDU data, followed by a 4-octet CRC-32
calculated over the MPDU data.
19  The expanded MPDU, after WEP encapsulation, is as follows:
20
21  IV  fb 02 9e 80
MPDU  data
 f6 9c 58 06 bd 6c e8 46 26 bc be fb 94 74 65 0a ad 1f 79 09 b0 f6 4d 5f 58 a5
 03 a2 58 b7 ed 22 eb 0e a6 49 30 d3 a0 56 a5 57 42 fc ce 14 1d 48 5f 8a a8 36
 de a1 8d f4 2c 53 80 80 5a d0 c6 1a 5d 6f 58 f4 10 40 b2 4b 7d 1a 69 38 56 ed
 0d 43 98 e7 ae e3 bf 0e
ICV  2a 2c a8 f7  
*/
static const u_int8_t test1_key[] = {		/* TK (w/o IV) */
	0x30, 0x31, 0x32, 0x33, 0x34,
};
static const u_int8_t test1_plaintext[] = {	/* Plaintext MPDU */
	0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,	/* 802.11 Header */
	0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
	0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33,
	0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00,	/* Plaintext data */
	0x45, 0x00, 0x00, 0x4e, 0x66, 0x1a, 0x00, 0x00,
	0x80, 0x11, 0xbe, 0x64, 0x0a, 0x00, 0x01, 0x22,
	0x0a, 0xff, 0xff, 0xff, 0x00, 0x89, 0x00, 0x89,
	0x00, 0x3a, 0x00, 0x00, 0x80, 0xa6, 0x01, 0x10,
	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x20, 0x45, 0x43, 0x45, 0x4a, 0x45, 0x48, 0x45,
	0x43, 0x46, 0x43, 0x45, 0x50, 0x46, 0x45, 0x45,
	0x49, 0x45, 0x46, 0x46, 0x43, 0x43, 0x41, 0x43,
	0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x41,
	0x41, 0x00, 0x00, 0x20, 0x00, 0x01,
};
static const u_int8_t test1_encrypted[] = {	/* Encrypted MPDU */
	0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,
	0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
	0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33,
	0xfb, 0x02, 0x9e, 0x80, 0xf6, 0x9c, 0x58, 0x06,
	0xbd, 0x6c, 0xe8, 0x46, 0x26, 0xbc, 0xbe, 0xfb,
	0x94, 0x74, 0x65, 0x0a, 0xad, 0x1f, 0x79, 0x09,
	0xb0, 0xf6, 0x4d, 0x5f, 0x58, 0xa5, 0x03, 0xa2,
	0x58, 0xb7, 0xed, 0x22, 0xeb, 0x0e, 0xa6, 0x49,
	0x30, 0xd3, 0xa0, 0x56, 0xa5, 0x57, 0x42, 0xfc,
	0xce, 0x14, 0x1d, 0x48, 0x5f, 0x8a, 0xa8, 0x36,
	0xde, 0xa1, 0x8d, 0xf4, 0x2c, 0x53, 0x80, 0x80,
	0x5a, 0xd0, 0xc6, 0x1a, 0x5d, 0x6f, 0x58, 0xf4,
	0x10, 0x40, 0xb2, 0x4b, 0x7d, 0x1a, 0x69, 0x38,
	0x56, 0xed, 0x0d, 0x43, 0x98, 0xe7, 0xae, 0xe3,
	0xbf, 0x0e, 0x2a, 0x2c, 0xa8, 0xf7,
};

/* XXX fix byte order of iv */
#define	TEST(n,name,cipher,keyix,iv0,iv1,iv2,iv3) { \
	name, IEEE80211_CIPHER_##cipher,keyix, { iv2,iv1,iv0,iv3 }, \
	test##n##_key,   sizeof(test##n##_key), \
	test##n##_plaintext, sizeof(test##n##_plaintext), \
	test##n##_encrypted, sizeof(test##n##_encrypted) \
}

static struct ciphertest {
	const char	*name;
	int		cipher;
	int		keyix;
	u_int8_t		iv[4];
	const u_int8_t	*key;
	size_t		key_len;
	const u_int8_t	*plaintext;
	size_t		plaintext_len;
	const u_int8_t	*encrypted;
	size_t		encrypted_len;
} weptests[] = {
	TEST(1, "WEP test mpdu 1", WEP, 2, 0xfb, 0x02, 0x9e, 0x80),
};

static void
dumpdata(const char *tag, const void *p, size_t len)
{
	int i;

	printk("%s: 0x%p len %zu", 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);
}

struct wep_ctx_hw {			/* for use with h/w support */
	struct ieee80211vap *wc_vap;	/* for diagnostics + statistics */
	struct ieee80211com *wc_ic;	/* for diagnostics */
	u_int32_t	wc_iv;		/* initial vector for crypto */
};

static int
runtest(struct ieee80211vap *vap, struct ciphertest *t)
{
	struct ieee80211_key *key;
	struct sk_buff *skb = NULL;
	const struct ieee80211_cipher *cip;
	u_int8_t mac[IEEE80211_ADDR_LEN];
	struct wep_ctx_hw *ctx;
	int hdrlen;

	printk("%s: ", t->name);

	if (!ieee80211_crypto_available(vap, t->cipher)) {
		printk("FAIL: ieee80211_crypto_available failed\n");
		return 0;
	}

	/*
	 * Setup key.
	 */
	key = &vap->iv_nw_keys[t->keyix];
	key->wk_keyix = t->keyix;
	if (!ieee80211_crypto_newkey(vap, t->cipher,
				     IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV,
				     key)) {
		printk("FAIL: ieee80211_crypto_newkey failed\n");
		goto bad;
	}

	memcpy(key->wk_key, t->key, t->key_len);
	key->wk_keylen = t->key_len;
	if (!ieee80211_crypto_setkey(vap, key, mac, NULL)) {
		printk("FAIL: ieee80211_crypto_setkey failed\n");
		goto bad;
	}
	cip = key->wk_cipher;

	/*
	 * Craft encrypted frame from known data.
	 */
	skb = ieee80211_dev_alloc_skb(t->encrypted_len);
	if (skb == NULL) {
		printk("FAIL: unable to allocate skbuff\n");
		goto bad;
	}
	memcpy(skb_put(skb, t->encrypted_len), t->encrypted, t->encrypted_len);

	/*
	 * Decrypt frame.
	 */
	hdrlen = ieee80211_hdrspace(vap->iv_ic, skb->data);
	if (!(*cip->ic_decap)(key, skb, hdrlen)) {
		printk("FAIL: wep decap failed\n");
		cmpfail(skb->data, skb->len,
			t->plaintext, t->plaintext_len);
		goto bad;
	}
	/*
	 * Verify: frame length, frame contents.
	 */
	if (skb->len != t->plaintext_len) {
		printk("FAIL: decap botch; length mismatch\n");
		cmpfail(skb->data, skb->len,
			t->plaintext, t->plaintext_len);
		goto bad;
	} else if (memcmp(skb->data, t->plaintext, t->plaintext_len)) {
		printk("FAIL: decap botch; data does not compare\n");
		cmpfail(skb->data, skb->len,
			t->plaintext, sizeof(t->plaintext));
		goto bad;
	}

	/*
	 * Encrypt frame.
	 */
	ctx = (struct wep_ctx_hw *)key->wk_private;
	memcpy(&ctx->wc_iv, t->iv, sizeof(t->iv));	/* for encap/encrypt */
	if (!(*cip->ic_encap)(key, skb, t->keyix << 6)) {
		printk("FAIL: wep encap failed\n");
		goto bad;
	}
	/*
	 * Verify: frame length, frame contents.
	 */
	if (skb->len != t->encrypted_len) {
		printk("FAIL: encap data length mismatch\n");
		cmpfail(skb->data, skb->len,
			t->encrypted, t->encrypted_len);
		goto bad;
	} else if (memcmp(skb->data, t->encrypted, skb->len)) {
		printk("FAIL: encrypt data does not compare\n");
		cmpfail(skb->data, skb->len,
			t->encrypted, t->encrypted_len);
		dumpdata("Plaintext", t->plaintext, t->plaintext_len);
		goto bad;
	}
	if (skb != NULL)
		ieee80211_dev_kfree_skb(&skb);
	ieee80211_crypto_delkey(vap, key, NULL);
	printk("PASS\n");
	return 1;
bad:
	if (skb != NULL)
		ieee80211_dev_kfree_skb(&skb);
	ieee80211_crypto_delkey(vap, key, NULL);
	return 0;
}

/*
 * Module glue.
 */
MODULE_AUTHOR("Errno Consulting, Sam Leffler");
MODULE_DESCRIPTION("802.11 wireless support: WEP cipher tester");
#ifdef MODULE_LICENSE
MODULE_LICENSE("Dual BSD/GPL");
#endif

static int tests = -1;
static int debug = 0;

#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,52))
MODULE_PARM(tests, "i");
MODULE_PARM(debug, "i");
#else
#include <linux/moduleparam.h>
module_param(tests, int, 0600);
module_param(debug, int, 0600);
#endif

MODULE_PARM_DESC(tests, "Specify which tests to run");
MODULE_PARM_DESC(debug, "Enable IEEE80211_MSG_CRYPTO");

static int __init
init_crypto_wep_test(void)
{
	struct ieee80211com *ic;
	struct ieee80211vap *vap;
	int i, pass, total;

	ic = kzalloc(sizeof(*ic), GFP_KERNEL);
	if (!ic)
		return -ENOMEM;

	ieee80211_crypto_attach(ic);

	vap = kzalloc(sizeof(*vap), GFP_KERNEL);
	if (!vap) {
		kfree(ic);
		return -ENOMEM;
	}

	vap->iv_ic = ic;
	if (debug)
		vap->iv_debug = IEEE80211_MSG_CRYPTO;

	ieee80211_crypto_vattach(vap);

	pass = 0;
	total = 0;
	for (i = 0; i < ARRAY_SIZE(weptests); i++)
		if (tests & (1 << i)) {
			total++;
			pass += runtest(vap, &weptests[i]);
		}
	printk("%u of %u 802.11i WEP test vectors passed\n", pass, total);
	ieee80211_crypto_vdetach(vap);
	ieee80211_crypto_detach(ic);
	kfree(vap);
	kfree(ic);
	return (pass == total ? 0 : -ENXIO);
}
module_init(init_crypto_wep_test);

static void __exit
exit_crypto_wep_test(void)
{
}
module_exit(exit_crypto_wep_test);
