1138568Ssam/*- 2178354Ssam * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting 3138568Ssam * All rights reserved. 4138568Ssam * 5138568Ssam * Redistribution and use in source and binary forms, with or without 6138568Ssam * modification, are permitted provided that the following conditions 7138568Ssam * are met: 8138568Ssam * 1. Redistributions of source code must retain the above copyright 9138568Ssam * notice, this list of conditions and the following disclaimer. 10138568Ssam * 2. Redistributions in binary form must reproduce the above copyright 11138568Ssam * notice, this list of conditions and the following disclaimer in the 12138568Ssam * documentation and/or other materials provided with the distribution. 13138568Ssam * 14138568Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15138568Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16138568Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17138568Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18138568Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19138568Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20138568Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21138568Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22138568Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23138568Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24138568Ssam */ 25138568Ssam 26138568Ssam#include <sys/cdefs.h> 27138568Ssam__FBSDID("$FreeBSD$"); 28138568Ssam 29138568Ssam/* 30138568Ssam * IEEE 802.11i AES-CCMP crypto support. 31138568Ssam * 32138568Ssam * Part of this module is derived from similar code in the Host 33138568Ssam * AP driver. The code is used with the consent of the author and 34138568Ssam * it's license is included below. 35138568Ssam */ 36178354Ssam#include "opt_wlan.h" 37178354Ssam 38138568Ssam#include <sys/param.h> 39138568Ssam#include <sys/systm.h> 40138568Ssam#include <sys/mbuf.h> 41138568Ssam#include <sys/malloc.h> 42138568Ssam#include <sys/kernel.h> 43138568Ssam#include <sys/module.h> 44138568Ssam 45138568Ssam#include <sys/socket.h> 46138568Ssam 47138568Ssam#include <net/if.h> 48138568Ssam#include <net/if_media.h> 49138568Ssam#include <net/ethernet.h> 50138568Ssam 51138568Ssam#include <net80211/ieee80211_var.h> 52138568Ssam 53138568Ssam#include <crypto/rijndael/rijndael.h> 54138568Ssam 55138568Ssam#define AES_BLOCK_LEN 16 56138568Ssam 57138568Ssamstruct ccmp_ctx { 58178354Ssam struct ieee80211vap *cc_vap; /* for diagnostics+statistics */ 59178354Ssam struct ieee80211com *cc_ic; 60138568Ssam rijndael_ctx cc_aes; 61138568Ssam}; 62138568Ssam 63178354Ssamstatic void *ccmp_attach(struct ieee80211vap *, struct ieee80211_key *); 64138568Ssamstatic void ccmp_detach(struct ieee80211_key *); 65138568Ssamstatic int ccmp_setkey(struct ieee80211_key *); 66170530Ssamstatic int ccmp_encap(struct ieee80211_key *k, struct mbuf *, uint8_t keyid); 67147252Ssamstatic int ccmp_decap(struct ieee80211_key *, struct mbuf *, int); 68147045Ssamstatic int ccmp_enmic(struct ieee80211_key *, struct mbuf *, int); 69147045Ssamstatic int ccmp_demic(struct ieee80211_key *, struct mbuf *, int); 70138568Ssam 71138568Ssamstatic const struct ieee80211_cipher ccmp = { 72138568Ssam .ic_name = "AES-CCM", 73138568Ssam .ic_cipher = IEEE80211_CIPHER_AES_CCM, 74138568Ssam .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + 75138568Ssam IEEE80211_WEP_EXTIVLEN, 76138568Ssam .ic_trailer = IEEE80211_WEP_MICLEN, 77138568Ssam .ic_miclen = 0, 78138568Ssam .ic_attach = ccmp_attach, 79138568Ssam .ic_detach = ccmp_detach, 80138568Ssam .ic_setkey = ccmp_setkey, 81138568Ssam .ic_encap = ccmp_encap, 82138568Ssam .ic_decap = ccmp_decap, 83138568Ssam .ic_enmic = ccmp_enmic, 84138568Ssam .ic_demic = ccmp_demic, 85138568Ssam}; 86138568Ssam 87138568Ssamstatic int ccmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); 88138568Ssamstatic int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn, 89138568Ssam struct mbuf *, int hdrlen); 90138568Ssam 91153353Ssam/* number of references from net80211 layer */ 92153353Ssamstatic int nrefs = 0; 93153353Ssam 94138568Ssamstatic void * 95178354Ssamccmp_attach(struct ieee80211vap *vap, struct ieee80211_key *k) 96138568Ssam{ 97138568Ssam struct ccmp_ctx *ctx; 98138568Ssam 99186302Ssam ctx = (struct ccmp_ctx *) malloc(sizeof(struct ccmp_ctx), 100178354Ssam M_80211_CRYPTO, M_NOWAIT | M_ZERO); 101138568Ssam if (ctx == NULL) { 102178354Ssam vap->iv_stats.is_crypto_nomem++; 103138568Ssam return NULL; 104138568Ssam } 105178354Ssam ctx->cc_vap = vap; 106178354Ssam ctx->cc_ic = vap->iv_ic; 107153353Ssam nrefs++; /* NB: we assume caller locking */ 108138568Ssam return ctx; 109138568Ssam} 110138568Ssam 111138568Ssamstatic void 112138568Ssamccmp_detach(struct ieee80211_key *k) 113138568Ssam{ 114138568Ssam struct ccmp_ctx *ctx = k->wk_private; 115138568Ssam 116186302Ssam free(ctx, M_80211_CRYPTO); 117153353Ssam KASSERT(nrefs > 0, ("imbalanced attach/detach")); 118153353Ssam nrefs--; /* NB: we assume caller locking */ 119138568Ssam} 120138568Ssam 121138568Ssamstatic int 122138568Ssamccmp_setkey(struct ieee80211_key *k) 123138568Ssam{ 124138568Ssam struct ccmp_ctx *ctx = k->wk_private; 125138568Ssam 126138568Ssam if (k->wk_keylen != (128/NBBY)) { 127178354Ssam IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO, 128138568Ssam "%s: Invalid key length %u, expecting %u\n", 129138568Ssam __func__, k->wk_keylen, 128/NBBY); 130138568Ssam return 0; 131138568Ssam } 132179394Ssam if (k->wk_flags & IEEE80211_KEY_SWENCRYPT) 133138568Ssam rijndael_set_key(&ctx->cc_aes, k->wk_key, k->wk_keylen*NBBY); 134138568Ssam return 1; 135138568Ssam} 136138568Ssam 137138568Ssam/* 138138568Ssam * Add privacy headers appropriate for the specified key. 139138568Ssam */ 140138568Ssamstatic int 141170530Ssamccmp_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid) 142138568Ssam{ 143139508Ssam struct ccmp_ctx *ctx = k->wk_private; 144139508Ssam struct ieee80211com *ic = ctx->cc_ic; 145170530Ssam uint8_t *ivp; 146138568Ssam int hdrlen; 147138568Ssam 148139508Ssam hdrlen = ieee80211_hdrspace(ic, mtod(m, void *)); 149138568Ssam 150138568Ssam /* 151138568Ssam * Copy down 802.11 header and add the IV, KeyID, and ExtIV. 152138568Ssam */ 153138568Ssam M_PREPEND(m, ccmp.ic_header, M_NOWAIT); 154138568Ssam if (m == NULL) 155138568Ssam return 0; 156170530Ssam ivp = mtod(m, uint8_t *); 157138568Ssam ovbcopy(ivp + ccmp.ic_header, ivp, hdrlen); 158138568Ssam ivp += hdrlen; 159138568Ssam 160138568Ssam k->wk_keytsc++; /* XXX wrap at 48 bits */ 161138568Ssam ivp[0] = k->wk_keytsc >> 0; /* PN0 */ 162138568Ssam ivp[1] = k->wk_keytsc >> 8; /* PN1 */ 163138568Ssam ivp[2] = 0; /* Reserved */ 164138568Ssam ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */ 165138568Ssam ivp[4] = k->wk_keytsc >> 16; /* PN2 */ 166138568Ssam ivp[5] = k->wk_keytsc >> 24; /* PN3 */ 167138568Ssam ivp[6] = k->wk_keytsc >> 32; /* PN4 */ 168138568Ssam ivp[7] = k->wk_keytsc >> 40; /* PN5 */ 169138568Ssam 170138568Ssam /* 171138568Ssam * Finally, do software encrypt if neeed. 172138568Ssam */ 173179394Ssam if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) && 174138568Ssam !ccmp_encrypt(k, m, hdrlen)) 175138568Ssam return 0; 176138568Ssam 177138568Ssam return 1; 178138568Ssam} 179138568Ssam 180138568Ssam/* 181138568Ssam * Add MIC to the frame as needed. 182138568Ssam */ 183138568Ssamstatic int 184147045Ssamccmp_enmic(struct ieee80211_key *k, struct mbuf *m, int force) 185138568Ssam{ 186138568Ssam 187138568Ssam return 1; 188138568Ssam} 189138568Ssam 190138568Ssamstatic __inline uint64_t 191138568SsamREAD_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) 192138568Ssam{ 193138568Ssam uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24); 194138568Ssam uint16_t iv16 = (b4 << 0) | (b5 << 8); 195138568Ssam return (((uint64_t)iv16) << 32) | iv32; 196138568Ssam} 197138568Ssam 198138568Ssam/* 199138568Ssam * Validate and strip privacy headers (and trailer) for a 200138568Ssam * received frame. The specified key should be correct but 201138568Ssam * is also verified. 202138568Ssam */ 203138568Ssamstatic int 204147252Ssamccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) 205138568Ssam{ 206138568Ssam struct ccmp_ctx *ctx = k->wk_private; 207178354Ssam struct ieee80211vap *vap = ctx->cc_vap; 208138568Ssam struct ieee80211_frame *wh; 209178354Ssam uint8_t *ivp, tid; 210138568Ssam uint64_t pn; 211138568Ssam 212138568Ssam /* 213138568Ssam * Header should have extended IV and sequence number; 214138568Ssam * verify the former and validate the latter. 215138568Ssam */ 216138568Ssam wh = mtod(m, struct ieee80211_frame *); 217138568Ssam ivp = mtod(m, uint8_t *) + hdrlen; 218138568Ssam if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) { 219138568Ssam /* 220138568Ssam * No extended IV; discard frame. 221138568Ssam */ 222178354Ssam IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, 223178354Ssam "%s", "missing ExtIV for AES-CCM cipher"); 224178354Ssam vap->iv_stats.is_rx_ccmpformat++; 225138568Ssam return 0; 226138568Ssam } 227178354Ssam tid = ieee80211_gettid(wh); 228138568Ssam pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]); 229209636Sbschmidt if (pn <= k->wk_keyrsc[tid] && 230209636Sbschmidt (k->wk_flags & IEEE80211_KEY_NOREPLAY) == 0) { 231138568Ssam /* 232138568Ssam * Replay violation. 233138568Ssam */ 234193541Ssam ieee80211_notify_replay_failure(vap, wh, k, pn, tid); 235178354Ssam vap->iv_stats.is_rx_ccmpreplay++; 236138568Ssam return 0; 237138568Ssam } 238138568Ssam 239138568Ssam /* 240138568Ssam * Check if the device handled the decrypt in hardware. 241138568Ssam * If so we just strip the header; otherwise we need to 242138568Ssam * handle the decrypt in software. Note that for the 243138568Ssam * latter we leave the header in place for use in the 244138568Ssam * decryption work. 245138568Ssam */ 246179394Ssam if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) && 247138568Ssam !ccmp_decrypt(k, pn, m, hdrlen)) 248138568Ssam return 0; 249138568Ssam 250138568Ssam /* 251138568Ssam * Copy up 802.11 header and strip crypto bits. 252138568Ssam */ 253170530Ssam ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + ccmp.ic_header, hdrlen); 254138568Ssam m_adj(m, ccmp.ic_header); 255138568Ssam m_adj(m, -ccmp.ic_trailer); 256138568Ssam 257138568Ssam /* 258138568Ssam * Ok to update rsc now. 259138568Ssam */ 260178354Ssam k->wk_keyrsc[tid] = pn; 261138568Ssam 262138568Ssam return 1; 263138568Ssam} 264138568Ssam 265138568Ssam/* 266138568Ssam * Verify and strip MIC from the frame. 267138568Ssam */ 268138568Ssamstatic int 269147045Ssamccmp_demic(struct ieee80211_key *k, struct mbuf *m, int force) 270138568Ssam{ 271138568Ssam return 1; 272138568Ssam} 273138568Ssam 274138568Ssamstatic __inline void 275138568Ssamxor_block(uint8_t *b, const uint8_t *a, size_t len) 276138568Ssam{ 277138568Ssam int i; 278138568Ssam for (i = 0; i < len; i++) 279138568Ssam b[i] ^= a[i]; 280138568Ssam} 281138568Ssam 282138568Ssam/* 283138568Ssam * Host AP crypt: host-based CCMP encryption implementation for Host AP driver 284138568Ssam * 285138568Ssam * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> 286138568Ssam * 287138568Ssam * This program is free software; you can redistribute it and/or modify 288138568Ssam * it under the terms of the GNU General Public License version 2 as 289138568Ssam * published by the Free Software Foundation. See README and COPYING for 290138568Ssam * more details. 291138568Ssam * 292138568Ssam * Alternatively, this software may be distributed under the terms of BSD 293138568Ssam * license. 294138568Ssam */ 295138568Ssam 296138568Ssamstatic void 297138568Ssamccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame *wh, 298138568Ssam u_int64_t pn, size_t dlen, 299138568Ssam uint8_t b0[AES_BLOCK_LEN], uint8_t aad[2 * AES_BLOCK_LEN], 300138568Ssam uint8_t auth[AES_BLOCK_LEN], uint8_t s0[AES_BLOCK_LEN]) 301138568Ssam{ 302138568Ssam#define IS_QOS_DATA(wh) IEEE80211_QOS_HAS_SEQ(wh) 303138568Ssam 304138568Ssam /* CCM Initial Block: 305138568Ssam * Flag (Include authentication header, M=3 (8-octet MIC), 306138568Ssam * L=1 (2-octet Dlen)) 307138568Ssam * Nonce: 0x00 | A2 | PN 308138568Ssam * Dlen */ 309138568Ssam b0[0] = 0x59; 310138568Ssam /* NB: b0[1] set below */ 311138568Ssam IEEE80211_ADDR_COPY(b0 + 2, wh->i_addr2); 312138568Ssam b0[8] = pn >> 40; 313138568Ssam b0[9] = pn >> 32; 314138568Ssam b0[10] = pn >> 24; 315138568Ssam b0[11] = pn >> 16; 316138568Ssam b0[12] = pn >> 8; 317138568Ssam b0[13] = pn >> 0; 318138568Ssam b0[14] = (dlen >> 8) & 0xff; 319138568Ssam b0[15] = dlen & 0xff; 320138568Ssam 321138568Ssam /* AAD: 322138568Ssam * FC with bits 4..6 and 11..13 masked to zero; 14 is always one 323138568Ssam * A1 | A2 | A3 324138568Ssam * SC with bits 4..15 (seq#) masked to zero 325138568Ssam * A4 (if present) 326138568Ssam * QC (if present) 327138568Ssam */ 328138568Ssam aad[0] = 0; /* AAD length >> 8 */ 329138568Ssam /* NB: aad[1] set below */ 330138568Ssam aad[2] = wh->i_fc[0] & 0x8f; /* XXX magic #s */ 331138568Ssam aad[3] = wh->i_fc[1] & 0xc7; /* XXX magic #s */ 332138568Ssam /* NB: we know 3 addresses are contiguous */ 333138568Ssam memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN); 334138568Ssam aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK; 335138568Ssam aad[23] = 0; /* all bits masked */ 336138568Ssam /* 337138568Ssam * Construct variable-length portion of AAD based 338138568Ssam * on whether this is a 4-address frame/QOS frame. 339138568Ssam * We always zero-pad to 32 bytes before running it 340138568Ssam * through the cipher. 341138568Ssam * 342138568Ssam * We also fill in the priority bits of the CCM 343138568Ssam * initial block as we know whether or not we have 344138568Ssam * a QOS frame. 345138568Ssam */ 346193840Ssam if (IEEE80211_IS_DSTODS(wh)) { 347138568Ssam IEEE80211_ADDR_COPY(aad + 24, 348138568Ssam ((struct ieee80211_frame_addr4 *)wh)->i_addr4); 349138568Ssam if (IS_QOS_DATA(wh)) { 350138568Ssam struct ieee80211_qosframe_addr4 *qwh4 = 351138568Ssam (struct ieee80211_qosframe_addr4 *) wh; 352138568Ssam aad[30] = qwh4->i_qos[0] & 0x0f;/* just priority bits */ 353138568Ssam aad[31] = 0; 354138568Ssam b0[1] = aad[30]; 355138568Ssam aad[1] = 22 + IEEE80211_ADDR_LEN + 2; 356138568Ssam } else { 357170530Ssam *(uint16_t *)&aad[30] = 0; 358138568Ssam b0[1] = 0; 359138568Ssam aad[1] = 22 + IEEE80211_ADDR_LEN; 360138568Ssam } 361138568Ssam } else { 362138568Ssam if (IS_QOS_DATA(wh)) { 363138568Ssam struct ieee80211_qosframe *qwh = 364138568Ssam (struct ieee80211_qosframe*) wh; 365138568Ssam aad[24] = qwh->i_qos[0] & 0x0f; /* just priority bits */ 366138568Ssam aad[25] = 0; 367138568Ssam b0[1] = aad[24]; 368138568Ssam aad[1] = 22 + 2; 369138568Ssam } else { 370170530Ssam *(uint16_t *)&aad[24] = 0; 371138568Ssam b0[1] = 0; 372138568Ssam aad[1] = 22; 373138568Ssam } 374170530Ssam *(uint16_t *)&aad[26] = 0; 375170530Ssam *(uint32_t *)&aad[28] = 0; 376138568Ssam } 377138568Ssam 378138568Ssam /* Start with the first block and AAD */ 379138568Ssam rijndael_encrypt(ctx, b0, auth); 380138568Ssam xor_block(auth, aad, AES_BLOCK_LEN); 381138568Ssam rijndael_encrypt(ctx, auth, auth); 382138568Ssam xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); 383138568Ssam rijndael_encrypt(ctx, auth, auth); 384138568Ssam b0[0] &= 0x07; 385138568Ssam b0[14] = b0[15] = 0; 386138568Ssam rijndael_encrypt(ctx, b0, s0); 387138568Ssam#undef IS_QOS_DATA 388138568Ssam} 389138568Ssam 390138568Ssam#define CCMP_ENCRYPT(_i, _b, _b0, _pos, _e, _len) do { \ 391138568Ssam /* Authentication */ \ 392138568Ssam xor_block(_b, _pos, _len); \ 393138568Ssam rijndael_encrypt(&ctx->cc_aes, _b, _b); \ 394138568Ssam /* Encryption, with counter */ \ 395138568Ssam _b0[14] = (_i >> 8) & 0xff; \ 396138568Ssam _b0[15] = _i & 0xff; \ 397138568Ssam rijndael_encrypt(&ctx->cc_aes, _b0, _e); \ 398138568Ssam xor_block(_pos, _e, _len); \ 399138568Ssam} while (0) 400138568Ssam 401138568Ssamstatic int 402138568Ssamccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) 403138568Ssam{ 404138568Ssam struct ccmp_ctx *ctx = key->wk_private; 405138568Ssam struct ieee80211_frame *wh; 406138568Ssam struct mbuf *m = m0; 407147892Ssam int data_len, i, space; 408138568Ssam uint8_t aad[2 * AES_BLOCK_LEN], b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], 409138568Ssam e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN]; 410138568Ssam uint8_t *pos; 411138568Ssam 412178354Ssam ctx->cc_vap->iv_stats.is_crypto_ccmp++; 413138568Ssam 414138568Ssam wh = mtod(m, struct ieee80211_frame *); 415138568Ssam data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header); 416138568Ssam ccmp_init_blocks(&ctx->cc_aes, wh, key->wk_keytsc, 417138568Ssam data_len, b0, aad, b, s0); 418138568Ssam 419138568Ssam i = 1; 420138568Ssam pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header; 421138568Ssam /* NB: assumes header is entirely in first mbuf */ 422138568Ssam space = m->m_len - (hdrlen + ccmp.ic_header); 423138568Ssam for (;;) { 424138568Ssam if (space > data_len) 425138568Ssam space = data_len; 426138568Ssam /* 427138568Ssam * Do full blocks. 428138568Ssam */ 429138568Ssam while (space >= AES_BLOCK_LEN) { 430138568Ssam CCMP_ENCRYPT(i, b, b0, pos, e, AES_BLOCK_LEN); 431138568Ssam pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN; 432138568Ssam data_len -= AES_BLOCK_LEN; 433138568Ssam i++; 434138568Ssam } 435138568Ssam if (data_len <= 0) /* no more data */ 436138568Ssam break; 437138568Ssam m = m->m_next; 438138568Ssam if (m == NULL) { /* last buffer */ 439138568Ssam if (space != 0) { 440138568Ssam /* 441138568Ssam * Short last block. 442138568Ssam */ 443138568Ssam CCMP_ENCRYPT(i, b, b0, pos, e, space); 444138568Ssam } 445138568Ssam break; 446138568Ssam } 447138568Ssam if (space != 0) { 448138568Ssam uint8_t *pos_next; 449147892Ssam int space_next; 450147892Ssam int len, dl, sp; 451147892Ssam struct mbuf *n; 452138568Ssam 453138568Ssam /* 454147892Ssam * Block straddles one or more mbufs, gather data 455147892Ssam * into the block buffer b, apply the cipher, then 456147892Ssam * scatter the results back into the mbuf chain. 457147892Ssam * The buffer will automatically get space bytes 458147892Ssam * of data at offset 0 copied in+out by the 459147892Ssam * CCMP_ENCRYPT request so we must take care of 460147892Ssam * the remaining data. 461138568Ssam */ 462147892Ssam n = m; 463147892Ssam dl = data_len; 464147892Ssam sp = space; 465147892Ssam for (;;) { 466147892Ssam pos_next = mtod(n, uint8_t *); 467147892Ssam len = min(dl, AES_BLOCK_LEN); 468147892Ssam space_next = len > sp ? len - sp : 0; 469147892Ssam if (n->m_len >= space_next) { 470147892Ssam /* 471147892Ssam * This mbuf has enough data; just grab 472147892Ssam * what we need and stop. 473147892Ssam */ 474147892Ssam xor_block(b+sp, pos_next, space_next); 475147892Ssam break; 476147892Ssam } 477147892Ssam /* 478147892Ssam * This mbuf's contents are insufficient, 479147892Ssam * take 'em all and prepare to advance to 480147892Ssam * the next mbuf. 481147892Ssam */ 482147892Ssam xor_block(b+sp, pos_next, n->m_len); 483147892Ssam sp += n->m_len, dl -= n->m_len; 484147892Ssam n = n->m_next; 485147892Ssam if (n == NULL) 486147892Ssam break; 487147892Ssam } 488138568Ssam 489138568Ssam CCMP_ENCRYPT(i, b, b0, pos, e, space); 490147892Ssam 491147892Ssam /* NB: just like above, but scatter data to mbufs */ 492147892Ssam dl = data_len; 493147892Ssam sp = space; 494147892Ssam for (;;) { 495147892Ssam pos_next = mtod(m, uint8_t *); 496147892Ssam len = min(dl, AES_BLOCK_LEN); 497147892Ssam space_next = len > sp ? len - sp : 0; 498147892Ssam if (m->m_len >= space_next) { 499147892Ssam xor_block(pos_next, e+sp, space_next); 500147892Ssam break; 501147892Ssam } 502147892Ssam xor_block(pos_next, e+sp, m->m_len); 503147892Ssam sp += m->m_len, dl -= m->m_len; 504147892Ssam m = m->m_next; 505147892Ssam if (m == NULL) 506147892Ssam goto done; 507147892Ssam } 508147892Ssam /* 509147892Ssam * Do bookkeeping. m now points to the last mbuf 510147892Ssam * we grabbed data from. We know we consumed a 511147892Ssam * full block of data as otherwise we'd have hit 512147892Ssam * the end of the mbuf chain, so deduct from data_len. 513147892Ssam * Otherwise advance the block number (i) and setup 514147892Ssam * pos+space to reflect contents of the new mbuf. 515147892Ssam */ 516147892Ssam data_len -= AES_BLOCK_LEN; 517138568Ssam i++; 518138568Ssam pos = pos_next + space_next; 519138568Ssam space = m->m_len - space_next; 520138568Ssam } else { 521138568Ssam /* 522138568Ssam * Setup for next buffer. 523138568Ssam */ 524138568Ssam pos = mtod(m, uint8_t *); 525138568Ssam space = m->m_len; 526138568Ssam } 527138568Ssam } 528147892Ssamdone: 529138568Ssam /* tack on MIC */ 530138568Ssam xor_block(b, s0, ccmp.ic_trailer); 531138568Ssam return m_append(m0, ccmp.ic_trailer, b); 532138568Ssam} 533138568Ssam#undef CCMP_ENCRYPT 534138568Ssam 535138568Ssam#define CCMP_DECRYPT(_i, _b, _b0, _pos, _a, _len) do { \ 536138568Ssam /* Decrypt, with counter */ \ 537138568Ssam _b0[14] = (_i >> 8) & 0xff; \ 538138568Ssam _b0[15] = _i & 0xff; \ 539138568Ssam rijndael_encrypt(&ctx->cc_aes, _b0, _b); \ 540138568Ssam xor_block(_pos, _b, _len); \ 541138568Ssam /* Authentication */ \ 542138568Ssam xor_block(_a, _pos, _len); \ 543138568Ssam rijndael_encrypt(&ctx->cc_aes, _a, _a); \ 544138568Ssam} while (0) 545138568Ssam 546138568Ssamstatic int 547138568Ssamccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen) 548138568Ssam{ 549138568Ssam struct ccmp_ctx *ctx = key->wk_private; 550178354Ssam struct ieee80211vap *vap = ctx->cc_vap; 551138568Ssam struct ieee80211_frame *wh; 552138568Ssam uint8_t aad[2 * AES_BLOCK_LEN]; 553138568Ssam uint8_t b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN]; 554138568Ssam uint8_t mic[AES_BLOCK_LEN]; 555138568Ssam size_t data_len; 556138568Ssam int i; 557138568Ssam uint8_t *pos; 558138568Ssam u_int space; 559138568Ssam 560178354Ssam ctx->cc_vap->iv_stats.is_crypto_ccmp++; 561138568Ssam 562138568Ssam wh = mtod(m, struct ieee80211_frame *); 563138568Ssam data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer); 564138568Ssam ccmp_init_blocks(&ctx->cc_aes, wh, pn, data_len, b0, aad, a, b); 565138568Ssam m_copydata(m, m->m_pkthdr.len - ccmp.ic_trailer, ccmp.ic_trailer, mic); 566138568Ssam xor_block(mic, b, ccmp.ic_trailer); 567138568Ssam 568138568Ssam i = 1; 569138568Ssam pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header; 570138568Ssam space = m->m_len - (hdrlen + ccmp.ic_header); 571138568Ssam for (;;) { 572138568Ssam if (space > data_len) 573138568Ssam space = data_len; 574138568Ssam while (space >= AES_BLOCK_LEN) { 575138568Ssam CCMP_DECRYPT(i, b, b0, pos, a, AES_BLOCK_LEN); 576138568Ssam pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN; 577138568Ssam data_len -= AES_BLOCK_LEN; 578138568Ssam i++; 579138568Ssam } 580138568Ssam if (data_len <= 0) /* no more data */ 581138568Ssam break; 582138568Ssam m = m->m_next; 583138568Ssam if (m == NULL) { /* last buffer */ 584138568Ssam if (space != 0) /* short last block */ 585138568Ssam CCMP_DECRYPT(i, b, b0, pos, a, space); 586138568Ssam break; 587138568Ssam } 588138568Ssam if (space != 0) { 589138568Ssam uint8_t *pos_next; 590138568Ssam u_int space_next; 591138568Ssam u_int len; 592138568Ssam 593138568Ssam /* 594138568Ssam * Block straddles buffers, split references. We 595147892Ssam * do not handle splits that require >2 buffers 596147892Ssam * since rx'd frames are never badly fragmented 597147892Ssam * because drivers typically recv in clusters. 598138568Ssam */ 599138568Ssam pos_next = mtod(m, uint8_t *); 600138568Ssam len = min(data_len, AES_BLOCK_LEN); 601138568Ssam space_next = len > space ? len - space : 0; 602138568Ssam KASSERT(m->m_len >= space_next, 603138568Ssam ("not enough data in following buffer, " 604138568Ssam "m_len %u need %u\n", m->m_len, space_next)); 605138568Ssam 606138568Ssam xor_block(b+space, pos_next, space_next); 607138568Ssam CCMP_DECRYPT(i, b, b0, pos, a, space); 608138568Ssam xor_block(pos_next, b+space, space_next); 609138568Ssam data_len -= len; 610138568Ssam i++; 611138568Ssam 612138568Ssam pos = pos_next + space_next; 613138568Ssam space = m->m_len - space_next; 614138568Ssam } else { 615138568Ssam /* 616138568Ssam * Setup for next buffer. 617138568Ssam */ 618138568Ssam pos = mtod(m, uint8_t *); 619138568Ssam space = m->m_len; 620138568Ssam } 621138568Ssam } 622138568Ssam if (memcmp(mic, a, ccmp.ic_trailer) != 0) { 623178354Ssam IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, 624178354Ssam "%s", "AES-CCM decrypt failed; MIC mismatch"); 625178354Ssam vap->iv_stats.is_rx_ccmpmic++; 626138568Ssam return 0; 627138568Ssam } 628138568Ssam return 1; 629138568Ssam} 630138568Ssam#undef CCMP_DECRYPT 631138568Ssam 632138568Ssam/* 633138568Ssam * Module glue. 634138568Ssam */ 635170530SsamIEEE80211_CRYPTO_MODULE(ccmp, 1); 636