[802.11] Add support for WEP-protected networks
authorJoshua Oreman <oremanj@rwcr.net>
Wed, 24 Jun 2009 07:37:52 +0000 (00:37 -0700)
committerMarty Connor <mdc@etherboot.org>
Tue, 5 Jan 2010 14:14:08 +0000 (09:14 -0500)
WEP is a highly flawed cryptosystem, barely better than no encryption at all,
but many people still use it. It does have the advantage of being very simple
and small in code size.

Signed-off-by: Marty Connor <mdc@etherboot.org>
src/config/config_net80211.c
src/config/general.h
src/include/gpxe/errfile.h
src/net/80211/wep.c [new file with mode: 0644]

index bcb76a7..59986b9 100644 (file)
@@ -31,3 +31,10 @@ REQUIRE_OBJECT ( iwmgmt_cmd );
 REQUIRE_OBJECT ( wireless_errors );
 #endif
 
+/*
+ * Drag in 802.11 cryptosystems and handshaking protocols
+ *
+ */
+#ifdef CRYPTO_80211_WEP
+REQUIRE_OBJECT ( wep );
+#endif
index 0acc00d..c0370ca 100644 (file)
@@ -64,6 +64,12 @@ FILE_LICENCE ( GPL2_OR_LATER );
 //#undef       SANBOOT_PROTO_AOE       /* AoE protocol */
 //#undef       SANBOOT_PROTO_IB_SRP    /* Infiniband SCSI RDMA protocol */
 
+/*
+ * 802.11 cryptosystems and handshaking protocols
+ *
+ */
+#define        CRYPTO_80211_WEP        /* WEP encryption (deprecated and insecure!) */
+
 /*
  * Name resolution modules
  *
index 55dbb6f..8a997e1 100644 (file)
@@ -159,6 +159,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #define ERRFILE_ib_cmrc                        ( ERRFILE_NET | 0x00210000 )
 #define ERRFILE_ib_srp                 ( ERRFILE_NET | 0x00220000 )
 #define ERRFILE_sec80211               ( ERRFILE_NET | 0x00230000 )
+#define ERRFILE_wep                    ( ERRFILE_NET | 0x00240000 )
 
 #define ERRFILE_image                ( ERRFILE_IMAGE | 0x00000000 )
 #define ERRFILE_elf                  ( ERRFILE_IMAGE | 0x00010000 )
diff --git a/src/net/80211/wep.c b/src/net/80211/wep.c
new file mode 100644 (file)
index 0000000..1c37e0c
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <gpxe/net80211.h>
+#include <gpxe/sec80211.h>
+#include <gpxe/crypto.h>
+#include <gpxe/arc4.h>
+#include <gpxe/crc32.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/** @file
+ *
+ * The WEP wireless encryption method (insecure!)
+ *
+ * The data field in a WEP-encrypted packet contains a 3-byte
+ * initialisation vector, one-byte Key ID field (only the bottom two
+ * bits are ever used), encrypted data, and a 4-byte encrypted CRC of
+ * the plaintext data, called the ICV. To decrypt it, the IV is
+ * prepended to the shared key and the data stream (including ICV) is
+ * run through the ARC4 stream cipher; if the ICV matches a CRC32
+ * calculated on the plaintext, the packet is valid.
+ *
+ * For efficiency and code-size reasons, this file assumes it is
+ * running on a little-endian machine.
+ */
+
+/** Length of WEP initialisation vector */
+#define WEP_IV_LEN     3
+
+/** Length of WEP key ID byte */
+#define WEP_KID_LEN    1
+
+/** Length of WEP ICV checksum */
+#define WEP_ICV_LEN    4
+
+/** Maximum length of WEP key */
+#define WEP_MAX_KEY    16
+
+/** Amount of data placed before the encrypted bytes */
+#define WEP_HEADER_LEN 4
+
+/** Amount of data placed after the encrypted bytes */
+#define WEP_TRAILER_LEN        4
+
+/** Total WEP overhead bytes */
+#define WEP_OVERHEAD   8
+
+/** Context for WEP encryption and decryption */
+struct wep_ctx
+{
+       /** Encoded WEP key
+        *
+        * The actual key bytes are stored beginning at offset 3, to
+        * leave room for easily inserting the IV before a particular
+        * operation.
+        */
+       u8 key[WEP_IV_LEN + WEP_MAX_KEY];
+
+       /** Length of WEP key (not including IV bytes) */
+       int keylen;
+
+       /** ARC4 context */
+       struct arc4_ctx arc4;
+};
+
+/**
+ * Initialize WEP algorithm
+ *
+ * @v crypto   802.11 cryptographic algorithm
+ * @v key      WEP key to use
+ * @v keylen   Length of WEP key
+ * @v rsc      Initial receive sequence counter (unused)
+ * @ret rc     Return status code
+ *
+ * Standard key lengths are 5 and 13 bytes; 16-byte keys are
+ * occasionally supported as an extension to the standard.
+ */
+static int wep_init ( struct net80211_crypto *crypto, const void *key,
+                     int keylen, const void *rsc __unused )
+{
+       struct wep_ctx *ctx = crypto->priv;
+
+       ctx->keylen = ( keylen > WEP_MAX_KEY ? WEP_MAX_KEY : keylen );
+       memcpy ( ctx->key + WEP_IV_LEN, key, ctx->keylen );
+
+       return 0;
+}
+
+/**
+ * Encrypt packet using WEP
+ *
+ * @v crypto   802.11 cryptographic algorithm
+ * @v iob      I/O buffer of plaintext packet
+ * @ret eiob   Newly allocated I/O buffer for encrypted packet, or NULL
+ *
+ * If memory allocation fails, @c NULL is returned.
+ */
+static struct io_buffer * wep_encrypt ( struct net80211_crypto *crypto,
+                                       struct io_buffer *iob )
+{
+       struct wep_ctx *ctx = crypto->priv;
+       struct io_buffer *eiob;
+       struct ieee80211_frame *hdr;
+       const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN;
+       int datalen = iob_len ( iob ) - hdrlen;
+       int newlen = hdrlen + datalen + WEP_OVERHEAD;
+       u32 iv, icv;
+
+       eiob = alloc_iob ( newlen );
+       if ( ! eiob )
+               return NULL;
+
+       memcpy ( iob_put ( eiob, hdrlen ), iob->data, hdrlen );
+       hdr = eiob->data;
+       hdr->fc |= IEEE80211_FC_PROTECTED;
+
+       /* Calculate IV, put it in the header (with key ID byte = 0), and
+          set it up at the start of the encryption key. */
+       iv = random() & 0xffffff; /* IV in bottom 3 bytes, top byte = KID = 0 */
+       memcpy ( iob_put ( eiob, WEP_HEADER_LEN ), &iv, WEP_HEADER_LEN );
+       memcpy ( ctx->key, &iv, WEP_IV_LEN );
+
+       /* Encrypt the data using RC4 */
+       cipher_setkey ( &arc4_algorithm, &ctx->arc4, ctx->key,
+                       ctx->keylen + WEP_IV_LEN );
+       cipher_encrypt ( &arc4_algorithm, &ctx->arc4, iob->data + hdrlen,
+                        iob_put ( eiob, datalen ), datalen );
+
+       /* Add ICV */
+       icv = ~crc32_le ( ~0, iob->data + hdrlen, datalen );
+       cipher_encrypt ( &arc4_algorithm, &ctx->arc4, &icv,
+                        iob_put ( eiob, WEP_ICV_LEN ), WEP_ICV_LEN );
+
+       return eiob;
+}
+
+/**
+ * Decrypt packet using WEP
+ *
+ * @v crypto   802.11 cryptographic algorithm
+ * @v eiob     I/O buffer of encrypted packet
+ * @ret iob    Newly allocated I/O buffer for plaintext packet, or NULL
+ *
+ * If a consistency check for the decryption fails (usually indicating
+ * an invalid key), @c NULL is returned.
+ */
+static struct io_buffer * wep_decrypt ( struct net80211_crypto *crypto,
+                                       struct io_buffer *eiob )
+{
+       struct wep_ctx *ctx = crypto->priv;
+       struct io_buffer *iob;
+       struct ieee80211_frame *hdr;
+       const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN;
+       int datalen = iob_len ( eiob ) - hdrlen - WEP_OVERHEAD;
+       int newlen = hdrlen + datalen;
+       u32 iv, icv, crc;
+
+       iob = alloc_iob ( newlen );
+       if ( ! iob )
+               return NULL;
+
+       memcpy ( iob_put ( iob, hdrlen ), eiob->data, hdrlen );
+       hdr = iob->data;
+       hdr->fc &= ~IEEE80211_FC_PROTECTED;
+
+       /* Strip off IV and use it to initialize cryptosystem */
+       memcpy ( &iv, eiob->data + hdrlen, 4 );
+       iv &= 0xffffff;         /* ignore key ID byte */
+       memcpy ( ctx->key, &iv, WEP_IV_LEN );
+
+       /* Decrypt the data using RC4 */
+       cipher_setkey ( &arc4_algorithm, &ctx->arc4, ctx->key,
+                       ctx->keylen + WEP_IV_LEN );
+       cipher_decrypt ( &arc4_algorithm, &ctx->arc4, eiob->data + hdrlen +
+                        WEP_HEADER_LEN, iob_put ( iob, datalen ), datalen );
+
+       /* Strip off ICV and verify it */
+       cipher_decrypt ( &arc4_algorithm, &ctx->arc4, eiob->data + hdrlen +
+                        WEP_HEADER_LEN + datalen, &icv, WEP_ICV_LEN );
+       crc = ~crc32_le ( ~0, iob->data + hdrlen, datalen );
+       if ( crc != icv ) {
+               DBGC ( crypto, "WEP %p CRC mismatch: expect %08x, get %08x\n",
+                      crypto, icv, crc );
+               free_iob ( iob );
+               return NULL;
+       }
+       return iob;
+}
+
+/** WEP cryptosystem for 802.11 */
+struct net80211_crypto wep_crypto __net80211_crypto = {
+       .algorithm = NET80211_CRYPT_WEP,
+       .init = wep_init,
+       .encrypt = wep_encrypt,
+       .decrypt = wep_decrypt,
+       .priv_len = sizeof ( struct wep_ctx ),
+};
+
+/**
+ * Initialize trivial 802.11 security handshaker
+ *
+ * @v dev      802.11 device
+ * @v ctx      Security handshaker
+ *
+ * This simply fetches a WEP key from netX/key, and if it exists,
+ * installs WEP cryptography on the 802.11 device. No real handshaking
+ * is performed.
+ */
+static int trivial_init ( struct net80211_device *dev )
+{
+       u8 key[WEP_MAX_KEY];    /* support up to 128-bit keys */
+       int len;
+       int rc;
+
+       if ( dev->associating &&
+            dev->associating->crypto == NET80211_CRYPT_NONE )
+               return 0;       /* no crypto? OK. */
+
+       len = fetch_setting ( netdev_settings ( dev->netdev ),
+                             &net80211_key_setting, key, WEP_MAX_KEY );
+
+       if ( len <= 0 ) {
+               DBGC ( dev, "802.11 %p cannot do WEP without a key\n", dev );
+               return -EACCES;
+       }
+
+       /* Full 128-bit keys are a nonstandard extension, but they're
+          utterly trivial to support, so we do. */
+       if ( len != 5 && len != 13 && len != 16 ) {
+               DBGC ( dev, "802.11 %p invalid WEP key length %d\n",
+                      dev, len );
+               return -EINVAL;
+       }
+
+       DBGC ( dev, "802.11 %p installing %d-bit WEP\n", dev, len * 8 );
+
+       rc = sec80211_install ( &dev->crypto, NET80211_CRYPT_WEP, key, len,
+                               NULL );
+       if ( rc < 0 )
+               return rc;
+
+       return 0;
+}
+
+/**
+ * Check for key change on trivial 802.11 security handshaker
+ *
+ * @v dev      802.11 device
+ * @v ctx      Security handshaker
+ */
+static int trivial_change_key ( struct net80211_device *dev )
+{
+       u8 key[WEP_MAX_KEY];
+       int len;
+       int change = 0;
+
+       /* If going from WEP to clear, or something else to WEP, reassociate. */
+       if ( ! dev->crypto || ( dev->crypto->init != wep_init ) )
+               change ^= 1;
+
+       len = fetch_setting ( netdev_settings ( dev->netdev ),
+                             &net80211_key_setting, key, WEP_MAX_KEY );
+       if ( len <= 0 )
+               change ^= 1;
+
+       /* Changing crypto type => return nonzero to reassociate. */
+       if ( change )
+               return -EINVAL;
+
+       /* Going from no crypto to still no crypto => nothing to do. */
+       if ( len <= 0 )
+               return 0;
+
+       /* Otherwise, reinitialise WEP with new key. */
+       return wep_init ( dev->crypto, key, len, NULL );
+}
+
+/** Trivial 802.11 security handshaker */
+struct net80211_handshaker trivial_handshaker __net80211_handshaker = {
+       .protocol = NET80211_SECPROT_NONE,
+       .init = trivial_init,
+       .change_key = trivial_change_key,
+       .priv_len = 0,
+};