[e16e8f2] | 1 | /* |
---|
| 2 | * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. |
---|
| 3 | * |
---|
| 4 | * This program is free software; you can redistribute it and/or |
---|
| 5 | * modify it under the terms of the GNU General Public License as |
---|
| 6 | * published by the Free Software Foundation; either version 2 of the |
---|
| 7 | * License, or any later version. |
---|
| 8 | * |
---|
| 9 | * This program is distributed in the hope that it will be useful, but |
---|
| 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
| 12 | * General Public License for more details. |
---|
| 13 | * |
---|
| 14 | * You should have received a copy of the GNU General Public License |
---|
| 15 | * along with this program; if not, write to the Free Software |
---|
| 16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
| 17 | */ |
---|
| 18 | |
---|
| 19 | FILE_LICENCE ( GPL2_OR_LATER ); |
---|
| 20 | |
---|
| 21 | #include <gpxe/net80211.h> |
---|
| 22 | #include <gpxe/sec80211.h> |
---|
| 23 | #include <gpxe/crypto.h> |
---|
| 24 | #include <gpxe/arc4.h> |
---|
| 25 | #include <gpxe/crc32.h> |
---|
| 26 | #include <stdlib.h> |
---|
| 27 | #include <string.h> |
---|
| 28 | #include <errno.h> |
---|
| 29 | |
---|
| 30 | /** @file |
---|
| 31 | * |
---|
| 32 | * The WEP wireless encryption method (insecure!) |
---|
| 33 | * |
---|
| 34 | * The data field in a WEP-encrypted packet contains a 3-byte |
---|
| 35 | * initialisation vector, one-byte Key ID field (only the bottom two |
---|
| 36 | * bits are ever used), encrypted data, and a 4-byte encrypted CRC of |
---|
| 37 | * the plaintext data, called the ICV. To decrypt it, the IV is |
---|
| 38 | * prepended to the shared key and the data stream (including ICV) is |
---|
| 39 | * run through the ARC4 stream cipher; if the ICV matches a CRC32 |
---|
| 40 | * calculated on the plaintext, the packet is valid. |
---|
| 41 | * |
---|
| 42 | * For efficiency and code-size reasons, this file assumes it is |
---|
| 43 | * running on a little-endian machine. |
---|
| 44 | */ |
---|
| 45 | |
---|
| 46 | /** Length of WEP initialisation vector */ |
---|
| 47 | #define WEP_IV_LEN 3 |
---|
| 48 | |
---|
| 49 | /** Length of WEP key ID byte */ |
---|
| 50 | #define WEP_KID_LEN 1 |
---|
| 51 | |
---|
| 52 | /** Length of WEP ICV checksum */ |
---|
| 53 | #define WEP_ICV_LEN 4 |
---|
| 54 | |
---|
| 55 | /** Maximum length of WEP key */ |
---|
| 56 | #define WEP_MAX_KEY 16 |
---|
| 57 | |
---|
| 58 | /** Amount of data placed before the encrypted bytes */ |
---|
| 59 | #define WEP_HEADER_LEN 4 |
---|
| 60 | |
---|
| 61 | /** Amount of data placed after the encrypted bytes */ |
---|
| 62 | #define WEP_TRAILER_LEN 4 |
---|
| 63 | |
---|
| 64 | /** Total WEP overhead bytes */ |
---|
| 65 | #define WEP_OVERHEAD 8 |
---|
| 66 | |
---|
| 67 | /** Context for WEP encryption and decryption */ |
---|
| 68 | struct wep_ctx |
---|
| 69 | { |
---|
| 70 | /** Encoded WEP key |
---|
| 71 | * |
---|
| 72 | * The actual key bytes are stored beginning at offset 3, to |
---|
| 73 | * leave room for easily inserting the IV before a particular |
---|
| 74 | * operation. |
---|
| 75 | */ |
---|
| 76 | u8 key[WEP_IV_LEN + WEP_MAX_KEY]; |
---|
| 77 | |
---|
| 78 | /** Length of WEP key (not including IV bytes) */ |
---|
| 79 | int keylen; |
---|
| 80 | |
---|
| 81 | /** ARC4 context */ |
---|
| 82 | struct arc4_ctx arc4; |
---|
| 83 | }; |
---|
| 84 | |
---|
| 85 | /** |
---|
| 86 | * Initialize WEP algorithm |
---|
| 87 | * |
---|
| 88 | * @v crypto 802.11 cryptographic algorithm |
---|
| 89 | * @v key WEP key to use |
---|
| 90 | * @v keylen Length of WEP key |
---|
| 91 | * @v rsc Initial receive sequence counter (unused) |
---|
| 92 | * @ret rc Return status code |
---|
| 93 | * |
---|
| 94 | * Standard key lengths are 5 and 13 bytes; 16-byte keys are |
---|
| 95 | * occasionally supported as an extension to the standard. |
---|
| 96 | */ |
---|
| 97 | static int wep_init ( struct net80211_crypto *crypto, const void *key, |
---|
| 98 | int keylen, const void *rsc __unused ) |
---|
| 99 | { |
---|
| 100 | struct wep_ctx *ctx = crypto->priv; |
---|
| 101 | |
---|
| 102 | ctx->keylen = ( keylen > WEP_MAX_KEY ? WEP_MAX_KEY : keylen ); |
---|
| 103 | memcpy ( ctx->key + WEP_IV_LEN, key, ctx->keylen ); |
---|
| 104 | |
---|
| 105 | return 0; |
---|
| 106 | } |
---|
| 107 | |
---|
| 108 | /** |
---|
| 109 | * Encrypt packet using WEP |
---|
| 110 | * |
---|
| 111 | * @v crypto 802.11 cryptographic algorithm |
---|
| 112 | * @v iob I/O buffer of plaintext packet |
---|
| 113 | * @ret eiob Newly allocated I/O buffer for encrypted packet, or NULL |
---|
| 114 | * |
---|
| 115 | * If memory allocation fails, @c NULL is returned. |
---|
| 116 | */ |
---|
| 117 | static struct io_buffer * wep_encrypt ( struct net80211_crypto *crypto, |
---|
| 118 | struct io_buffer *iob ) |
---|
| 119 | { |
---|
| 120 | struct wep_ctx *ctx = crypto->priv; |
---|
| 121 | struct io_buffer *eiob; |
---|
| 122 | struct ieee80211_frame *hdr; |
---|
| 123 | const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN; |
---|
| 124 | int datalen = iob_len ( iob ) - hdrlen; |
---|
| 125 | int newlen = hdrlen + datalen + WEP_OVERHEAD; |
---|
| 126 | u32 iv, icv; |
---|
| 127 | |
---|
| 128 | eiob = alloc_iob ( newlen ); |
---|
| 129 | if ( ! eiob ) |
---|
| 130 | return NULL; |
---|
| 131 | |
---|
| 132 | memcpy ( iob_put ( eiob, hdrlen ), iob->data, hdrlen ); |
---|
| 133 | hdr = eiob->data; |
---|
| 134 | hdr->fc |= IEEE80211_FC_PROTECTED; |
---|
| 135 | |
---|
| 136 | /* Calculate IV, put it in the header (with key ID byte = 0), and |
---|
| 137 | set it up at the start of the encryption key. */ |
---|
| 138 | iv = random() & 0xffffff; /* IV in bottom 3 bytes, top byte = KID = 0 */ |
---|
| 139 | memcpy ( iob_put ( eiob, WEP_HEADER_LEN ), &iv, WEP_HEADER_LEN ); |
---|
| 140 | memcpy ( ctx->key, &iv, WEP_IV_LEN ); |
---|
| 141 | |
---|
| 142 | /* Encrypt the data using RC4 */ |
---|
| 143 | cipher_setkey ( &arc4_algorithm, &ctx->arc4, ctx->key, |
---|
| 144 | ctx->keylen + WEP_IV_LEN ); |
---|
| 145 | cipher_encrypt ( &arc4_algorithm, &ctx->arc4, iob->data + hdrlen, |
---|
| 146 | iob_put ( eiob, datalen ), datalen ); |
---|
| 147 | |
---|
| 148 | /* Add ICV */ |
---|
| 149 | icv = ~crc32_le ( ~0, iob->data + hdrlen, datalen ); |
---|
| 150 | cipher_encrypt ( &arc4_algorithm, &ctx->arc4, &icv, |
---|
| 151 | iob_put ( eiob, WEP_ICV_LEN ), WEP_ICV_LEN ); |
---|
| 152 | |
---|
| 153 | return eiob; |
---|
| 154 | } |
---|
| 155 | |
---|
| 156 | /** |
---|
| 157 | * Decrypt packet using WEP |
---|
| 158 | * |
---|
| 159 | * @v crypto 802.11 cryptographic algorithm |
---|
| 160 | * @v eiob I/O buffer of encrypted packet |
---|
| 161 | * @ret iob Newly allocated I/O buffer for plaintext packet, or NULL |
---|
| 162 | * |
---|
| 163 | * If a consistency check for the decryption fails (usually indicating |
---|
| 164 | * an invalid key), @c NULL is returned. |
---|
| 165 | */ |
---|
| 166 | static struct io_buffer * wep_decrypt ( struct net80211_crypto *crypto, |
---|
| 167 | struct io_buffer *eiob ) |
---|
| 168 | { |
---|
| 169 | struct wep_ctx *ctx = crypto->priv; |
---|
| 170 | struct io_buffer *iob; |
---|
| 171 | struct ieee80211_frame *hdr; |
---|
| 172 | const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN; |
---|
| 173 | int datalen = iob_len ( eiob ) - hdrlen - WEP_OVERHEAD; |
---|
| 174 | int newlen = hdrlen + datalen; |
---|
| 175 | u32 iv, icv, crc; |
---|
| 176 | |
---|
| 177 | iob = alloc_iob ( newlen ); |
---|
| 178 | if ( ! iob ) |
---|
| 179 | return NULL; |
---|
| 180 | |
---|
| 181 | memcpy ( iob_put ( iob, hdrlen ), eiob->data, hdrlen ); |
---|
| 182 | hdr = iob->data; |
---|
| 183 | hdr->fc &= ~IEEE80211_FC_PROTECTED; |
---|
| 184 | |
---|
| 185 | /* Strip off IV and use it to initialize cryptosystem */ |
---|
| 186 | memcpy ( &iv, eiob->data + hdrlen, 4 ); |
---|
| 187 | iv &= 0xffffff; /* ignore key ID byte */ |
---|
| 188 | memcpy ( ctx->key, &iv, WEP_IV_LEN ); |
---|
| 189 | |
---|
| 190 | /* Decrypt the data using RC4 */ |
---|
| 191 | cipher_setkey ( &arc4_algorithm, &ctx->arc4, ctx->key, |
---|
| 192 | ctx->keylen + WEP_IV_LEN ); |
---|
| 193 | cipher_decrypt ( &arc4_algorithm, &ctx->arc4, eiob->data + hdrlen + |
---|
| 194 | WEP_HEADER_LEN, iob_put ( iob, datalen ), datalen ); |
---|
| 195 | |
---|
| 196 | /* Strip off ICV and verify it */ |
---|
| 197 | cipher_decrypt ( &arc4_algorithm, &ctx->arc4, eiob->data + hdrlen + |
---|
| 198 | WEP_HEADER_LEN + datalen, &icv, WEP_ICV_LEN ); |
---|
| 199 | crc = ~crc32_le ( ~0, iob->data + hdrlen, datalen ); |
---|
| 200 | if ( crc != icv ) { |
---|
| 201 | DBGC ( crypto, "WEP %p CRC mismatch: expect %08x, get %08x\n", |
---|
| 202 | crypto, icv, crc ); |
---|
| 203 | free_iob ( iob ); |
---|
| 204 | return NULL; |
---|
| 205 | } |
---|
| 206 | return iob; |
---|
| 207 | } |
---|
| 208 | |
---|
| 209 | /** WEP cryptosystem for 802.11 */ |
---|
| 210 | struct net80211_crypto wep_crypto __net80211_crypto = { |
---|
| 211 | .algorithm = NET80211_CRYPT_WEP, |
---|
| 212 | .init = wep_init, |
---|
| 213 | .encrypt = wep_encrypt, |
---|
| 214 | .decrypt = wep_decrypt, |
---|
| 215 | .priv_len = sizeof ( struct wep_ctx ), |
---|
| 216 | }; |
---|
| 217 | |
---|
| 218 | /** |
---|
| 219 | * Initialize trivial 802.11 security handshaker |
---|
| 220 | * |
---|
| 221 | * @v dev 802.11 device |
---|
| 222 | * @v ctx Security handshaker |
---|
| 223 | * |
---|
| 224 | * This simply fetches a WEP key from netX/key, and if it exists, |
---|
| 225 | * installs WEP cryptography on the 802.11 device. No real handshaking |
---|
| 226 | * is performed. |
---|
| 227 | */ |
---|
| 228 | static int trivial_init ( struct net80211_device *dev ) |
---|
| 229 | { |
---|
| 230 | u8 key[WEP_MAX_KEY]; /* support up to 128-bit keys */ |
---|
| 231 | int len; |
---|
| 232 | int rc; |
---|
| 233 | |
---|
| 234 | if ( dev->associating && |
---|
| 235 | dev->associating->crypto == NET80211_CRYPT_NONE ) |
---|
| 236 | return 0; /* no crypto? OK. */ |
---|
| 237 | |
---|
| 238 | len = fetch_setting ( netdev_settings ( dev->netdev ), |
---|
| 239 | &net80211_key_setting, key, WEP_MAX_KEY ); |
---|
| 240 | |
---|
| 241 | if ( len <= 0 ) { |
---|
| 242 | DBGC ( dev, "802.11 %p cannot do WEP without a key\n", dev ); |
---|
| 243 | return -EACCES; |
---|
| 244 | } |
---|
| 245 | |
---|
| 246 | /* Full 128-bit keys are a nonstandard extension, but they're |
---|
| 247 | utterly trivial to support, so we do. */ |
---|
| 248 | if ( len != 5 && len != 13 && len != 16 ) { |
---|
| 249 | DBGC ( dev, "802.11 %p invalid WEP key length %d\n", |
---|
| 250 | dev, len ); |
---|
| 251 | return -EINVAL; |
---|
| 252 | } |
---|
| 253 | |
---|
| 254 | DBGC ( dev, "802.11 %p installing %d-bit WEP\n", dev, len * 8 ); |
---|
| 255 | |
---|
| 256 | rc = sec80211_install ( &dev->crypto, NET80211_CRYPT_WEP, key, len, |
---|
| 257 | NULL ); |
---|
| 258 | if ( rc < 0 ) |
---|
| 259 | return rc; |
---|
| 260 | |
---|
| 261 | return 0; |
---|
| 262 | } |
---|
| 263 | |
---|
| 264 | /** |
---|
| 265 | * Check for key change on trivial 802.11 security handshaker |
---|
| 266 | * |
---|
| 267 | * @v dev 802.11 device |
---|
| 268 | * @v ctx Security handshaker |
---|
| 269 | */ |
---|
| 270 | static int trivial_change_key ( struct net80211_device *dev ) |
---|
| 271 | { |
---|
| 272 | u8 key[WEP_MAX_KEY]; |
---|
| 273 | int len; |
---|
| 274 | int change = 0; |
---|
| 275 | |
---|
| 276 | /* If going from WEP to clear, or something else to WEP, reassociate. */ |
---|
| 277 | if ( ! dev->crypto || ( dev->crypto->init != wep_init ) ) |
---|
| 278 | change ^= 1; |
---|
| 279 | |
---|
| 280 | len = fetch_setting ( netdev_settings ( dev->netdev ), |
---|
| 281 | &net80211_key_setting, key, WEP_MAX_KEY ); |
---|
| 282 | if ( len <= 0 ) |
---|
| 283 | change ^= 1; |
---|
| 284 | |
---|
| 285 | /* Changing crypto type => return nonzero to reassociate. */ |
---|
| 286 | if ( change ) |
---|
| 287 | return -EINVAL; |
---|
| 288 | |
---|
| 289 | /* Going from no crypto to still no crypto => nothing to do. */ |
---|
| 290 | if ( len <= 0 ) |
---|
| 291 | return 0; |
---|
| 292 | |
---|
| 293 | /* Otherwise, reinitialise WEP with new key. */ |
---|
| 294 | return wep_init ( dev->crypto, key, len, NULL ); |
---|
| 295 | } |
---|
| 296 | |
---|
| 297 | /** Trivial 802.11 security handshaker */ |
---|
| 298 | struct net80211_handshaker trivial_handshaker __net80211_handshaker = { |
---|
| 299 | .protocol = NET80211_SECPROT_NONE, |
---|
| 300 | .init = trivial_init, |
---|
| 301 | .change_key = trivial_change_key, |
---|
| 302 | .priv_len = 0, |
---|
| 303 | }; |
---|