Home | History | Annotate | Line # | Download | only in net80211
ieee80211_crypto_ccmp.c revision 1.17
      1 /*	$NetBSD: ieee80211_crypto_ccmp.c,v 1.17 2020/07/25 22:27:05 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. The name of the author may not be used to endorse or promote products
     16  *    derived from this software without specific prior written permission.
     17  *
     18  * Alternatively, this software may be distributed under the terms of the
     19  * GNU General Public License ("GPL") version 2 as published by the Free
     20  * Software Foundation.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     32  */
     33 
     34 #include <sys/cdefs.h>
     35 #ifdef __FreeBSD__
     36 __FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto_ccmp.c,v 1.7 2005/07/11 03:06:23 sam Exp $");
     37 #endif
     38 #ifdef __NetBSD__
     39 __KERNEL_RCSID(0, "$NetBSD: ieee80211_crypto_ccmp.c,v 1.17 2020/07/25 22:27:05 riastradh Exp $");
     40 #endif
     41 
     42 /*
     43  * IEEE 802.11i AES-CCMP crypto support.
     44  *
     45  * Part of this module is derived from similar code in the Host
     46  * AP driver. The code is used with the consent of the author and
     47  * its license is included below.
     48  */
     49 #include <sys/param.h>
     50 #include <sys/kernel.h>
     51 #include <sys/kmem.h>
     52 #include <sys/mbuf.h>
     53 #include <sys/systm.h>
     54 
     55 #include <sys/socket.h>
     56 
     57 #include <net/if.h>
     58 #include <net/if_ether.h>
     59 #include <net/if_media.h>
     60 
     61 #include <net80211/ieee80211_var.h>
     62 
     63 #include <crypto/aes/aes.h>
     64 #include <crypto/aes/aes_ccm.h>
     65 #include <crypto/aes/aes_ccm_mbuf.h>
     66 
     67 #define AES_BLOCK_LEN 16
     68 
     69 struct ccmp_ctx {
     70 	struct aesenc cc_aes;
     71 	struct ieee80211com *cc_ic;	/* for diagnostics */
     72 };
     73 
     74 static	void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *);
     75 static	void ccmp_detach(struct ieee80211_key *);
     76 static	int ccmp_setkey(struct ieee80211_key *);
     77 static	int ccmp_encap(struct ieee80211_key *k, struct mbuf *, u_int8_t keyid);
     78 static	int ccmp_decap(struct ieee80211_key *, struct mbuf *, int);
     79 static	int ccmp_enmic(struct ieee80211_key *, struct mbuf *, int);
     80 static	int ccmp_demic(struct ieee80211_key *, struct mbuf *, int);
     81 
     82 const struct ieee80211_cipher ieee80211_cipher_ccmp = {
     83 	.ic_name	= "AES-CCM",
     84 	.ic_cipher	= IEEE80211_CIPHER_AES_CCM,
     85 	.ic_header	= IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
     86 			  IEEE80211_WEP_EXTIVLEN,
     87 	.ic_trailer	= IEEE80211_WEP_MICLEN,
     88 	.ic_miclen	= 0,
     89 	.ic_attach	= ccmp_attach,
     90 	.ic_detach	= ccmp_detach,
     91 	.ic_setkey	= ccmp_setkey,
     92 	.ic_encap	= ccmp_encap,
     93 	.ic_decap	= ccmp_decap,
     94 	.ic_enmic	= ccmp_enmic,
     95 	.ic_demic	= ccmp_demic,
     96 };
     97 
     98 #define	ccmp	ieee80211_cipher_ccmp
     99 
    100 static	int ccmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
    101 static	int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn,
    102 		struct mbuf *, int hdrlen);
    103 
    104 static void *
    105 ccmp_attach(struct ieee80211com *ic, struct ieee80211_key *k)
    106 {
    107 	struct ccmp_ctx *ctx;
    108 
    109 	ctx = kmem_zalloc(sizeof(*ctx), KM_NOSLEEP);
    110 	if (ctx == NULL) {
    111 		ic->ic_stats.is_crypto_nomem++;
    112 		return NULL;
    113 	}
    114 	ctx->cc_ic = ic;
    115 	return ctx;
    116 }
    117 
    118 static void
    119 ccmp_detach(struct ieee80211_key *k)
    120 {
    121 	struct ccmp_ctx *ctx = k->wk_private;
    122 
    123 	kmem_free(ctx, sizeof(*ctx));
    124 }
    125 
    126 static int
    127 ccmp_setkey(struct ieee80211_key *k)
    128 {
    129 	struct ccmp_ctx *ctx = k->wk_private;
    130 
    131 	if (k->wk_keylen != (128/NBBY)) {
    132 		IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
    133 			"%s: Invalid key length %u, expecting %u\n",
    134 			__func__, k->wk_keylen, 128/NBBY);
    135 		return 0;
    136 	}
    137 	if (k->wk_flags & IEEE80211_KEY_SWCRYPT)
    138 		aes_setenckey128(&ctx->cc_aes, k->wk_key);
    139 	return 1;
    140 }
    141 
    142 /*
    143  * Add privacy headers appropriate for the specified key.
    144  */
    145 static int
    146 ccmp_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
    147 {
    148 	struct ccmp_ctx *ctx = k->wk_private;
    149 	struct ieee80211com *ic = ctx->cc_ic;
    150 	u_int8_t *ivp;
    151 	int hdrlen;
    152 
    153 	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
    154 	ivp = mtod(m, u_int8_t *) + hdrlen;
    155 
    156 	k->wk_keytsc++;		/* XXX wrap at 48 bits */
    157 	ivp[0] = k->wk_keytsc >> 0;		/* PN0 */
    158 	ivp[1] = k->wk_keytsc >> 8;		/* PN1 */
    159 	ivp[2] = 0;				/* Reserved */
    160 	ivp[3] = keyid | IEEE80211_WEP_EXTIV;	/* KeyID | ExtID */
    161 	ivp[4] = k->wk_keytsc >> 16;		/* PN2 */
    162 	ivp[5] = k->wk_keytsc >> 24;		/* PN3 */
    163 	ivp[6] = k->wk_keytsc >> 32;		/* PN4 */
    164 	ivp[7] = k->wk_keytsc >> 40;		/* PN5 */
    165 
    166 	/*
    167 	 * Finally, do software encrypt if neeed.
    168 	 */
    169 	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
    170 	    !ccmp_encrypt(k, m, hdrlen))
    171 		return 0;
    172 
    173 	return 1;
    174 }
    175 
    176 /*
    177  * Add MIC to the frame as needed.
    178  */
    179 static int
    180 ccmp_enmic(struct ieee80211_key *k, struct mbuf *m,
    181     int force)
    182 {
    183 
    184 	return 1;
    185 }
    186 
    187 static __inline uint64_t
    188 READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5)
    189 {
    190 	uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
    191 	uint16_t iv16 = (b4 << 0) | (b5 << 8);
    192 	return (((uint64_t)iv16) << 32) | iv32;
    193 }
    194 
    195 /*
    196  * Validate and strip privacy headers (and trailer) for a
    197  * received frame. The specified key should be correct but
    198  * is also verified.
    199  */
    200 static int
    201 ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
    202 {
    203 	struct ccmp_ctx *ctx = k->wk_private;
    204 	struct ieee80211_frame *wh;
    205 	uint8_t *ivp;
    206 	uint64_t pn;
    207 
    208 	/*
    209 	 * Header should have extended IV and sequence number;
    210 	 * verify the former and validate the latter.
    211 	 */
    212 	wh = mtod(m, struct ieee80211_frame *);
    213 	ivp = mtod(m, uint8_t *) + hdrlen;
    214 	if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
    215 		/*
    216 		 * No extended IV; discard frame.
    217 		 */
    218 		IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
    219 			"[%s] Missing ExtIV for AES-CCM cipher\n",
    220 			ether_sprintf(wh->i_addr2));
    221 		ctx->cc_ic->ic_stats.is_rx_ccmpformat++;
    222 		return 0;
    223 	}
    224 	pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
    225 	if (pn <= k->wk_keyrsc) {
    226 		/*
    227 		 * Replay violation.
    228 		 */
    229 		ieee80211_notify_replay_failure(ctx->cc_ic, wh, k, pn);
    230 		ctx->cc_ic->ic_stats.is_rx_ccmpreplay++;
    231 		return 0;
    232 	}
    233 
    234 	/*
    235 	 * Check if the device handled the decrypt in hardware.
    236 	 * If so we just strip the header; otherwise we need to
    237 	 * handle the decrypt in software.  Note that for the
    238 	 * latter we leave the header in place for use in the
    239 	 * decryption work.
    240 	 */
    241 	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
    242 	    !ccmp_decrypt(k, pn, m, hdrlen))
    243 		return 0;
    244 
    245 	/*
    246 	 * Copy up 802.11 header and strip crypto bits.
    247 	 */
    248 	memmove(mtod(m, u_int8_t *) + ccmp.ic_header, mtod(m, void *), hdrlen);
    249 	m_adj(m, ccmp.ic_header);
    250 	m_adj(m, -ccmp.ic_trailer);
    251 
    252 	/*
    253 	 * Ok to update rsc now.
    254 	 */
    255 	k->wk_keyrsc = pn;
    256 
    257 	return 1;
    258 }
    259 
    260 /*
    261  * Verify and strip MIC from the frame.
    262  */
    263 static int
    264 ccmp_demic(struct ieee80211_key *k, struct mbuf *m, int force)
    265 {
    266 	return 1;
    267 }
    268 
    269 static __inline void
    270 xor_block(uint8_t *b, const uint8_t *a, size_t len)
    271 {
    272 	int i;
    273 	for (i = 0; i < len; i++)
    274 		b[i] ^= a[i];
    275 }
    276 
    277 /*
    278  * Host AP crypt: host-based CCMP encryption implementation for Host AP driver
    279  *
    280  * Copyright (c) 2003-2004, Jouni Malinen <jkmaline (at) cc.hut.fi>
    281  *
    282  * This program is free software; you can redistribute it and/or modify
    283  * it under the terms of the GNU General Public License version 2 as
    284  * published by the Free Software Foundation. See README and COPYING for
    285  * more details.
    286  *
    287  * Alternatively, this software may be distributed under the terms of BSD
    288  * license.
    289  */
    290 
    291 static void
    292 ccmp_init_blocks(struct aesenc *ctx, struct ieee80211_frame *wh,
    293     u_int64_t pn, size_t data_len, struct aes_ccm *aes_ccm)
    294 {
    295 	uint8_t nonce[13];
    296 	uint8_t ad[32];
    297 	uint8_t qos;
    298 	size_t adlen;
    299 
    300 #define	IS_4ADDRESS(wh) \
    301 	((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
    302 #define	IS_QOS_DATA(wh)	ieee80211_has_qos(wh)
    303 
    304 	/* nonce[0] is qos, determined later */
    305 	IEEE80211_ADDR_COPY(nonce + 1, wh->i_addr2);
    306 	nonce[7] = pn >> 40;
    307 	nonce[8] = pn >> 32;
    308 	nonce[9] = pn >> 24;
    309 	nonce[10] = pn >> 16;
    310 	nonce[11] = pn >> 8;
    311 	nonce[12] = pn >> 0;
    312 
    313 	ad[0] = wh->i_fc[0] & 0x8f;	/* XXX magic #s */
    314 	ad[1] = wh->i_fc[1] & 0xc7;	/* XXX magic #s */
    315 	/* NB: we know 3 addresses are contiguous */
    316 	memcpy(ad + 2, wh->i_addr1, 3 * IEEE80211_ADDR_LEN);
    317 	ad[20] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK;
    318 	ad[21] = 0; /* all bits masked */
    319 
    320 	/*
    321 	 * Construct variable-length portion of AAD based
    322 	 * on whether this is a 4-address frame/QOS frame.
    323 	 *
    324 	 * We also fill in the priority bits of the CCM
    325 	 * initial block as we know whether or not we have
    326 	 * a QOS frame.
    327 	 */
    328 	if (IS_4ADDRESS(wh)) {
    329 		IEEE80211_ADDR_COPY(ad + 22,
    330 		    ((const struct ieee80211_frame_addr4 *)wh)->i_addr4);
    331 		if (IS_QOS_DATA(wh)) {
    332 			const struct ieee80211_qosframe_addr4 *qwh4 =
    333 			    (const struct ieee80211_qosframe_addr4 *)wh;
    334 			qos = qwh4->i_qos[0] & 0x0f; /* just priority bits */
    335 			ad[28] = qos;
    336 			ad[29] = 0;
    337 			adlen = 22 + IEEE80211_ADDR_LEN + 2;
    338 		} else {
    339 			qos = 0;
    340 			adlen = 22 + IEEE80211_ADDR_LEN;
    341 		}
    342 	} else {
    343 		if (IS_QOS_DATA(wh)) {
    344 			const struct ieee80211_qosframe *qwh =
    345 			    (const struct ieee80211_qosframe *)wh;
    346 			qos = qwh->i_qos[0] & 0x0f; /* just priority bits */
    347 			ad[22] = qos;
    348 			ad[23] = 0;
    349 			adlen = 22 + 2;
    350 		} else {
    351 			qos = 0;
    352 			adlen = 22;
    353 		}
    354 	}
    355 	nonce[0] = qos;
    356 
    357 	aes_ccm_init(aes_ccm, AES_128_NROUNDS, ctx, 2 /* L, counter octets */,
    358 	    IEEE80211_WEP_MICLEN, nonce, sizeof nonce, ad, adlen, data_len);
    359 
    360 #undef	IS_QOS_DATA
    361 #undef	IS_4ADDRESS
    362 }
    363 
    364 static int
    365 ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m, int hdrlen)
    366 {
    367 	struct ccmp_ctx *ctx = key->wk_private;
    368 	struct ieee80211_frame *wh;
    369 	struct aes_ccm aes_ccm;
    370 	size_t data_len;
    371 	uint8_t mic[IEEE80211_WEP_MICLEN];
    372 
    373 	KASSERT(hdrlen >= 0);
    374 	KASSERT(hdrlen < m->m_pkthdr.len);
    375 	KASSERT(ccmp.ic_header <= m->m_pkthdr.len - hdrlen);
    376 
    377 	ctx->cc_ic->ic_stats.is_crypto_ccmp++;
    378 
    379 	wh = mtod(m, struct ieee80211_frame *);
    380 	data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header);
    381 	ccmp_init_blocks(&ctx->cc_aes, wh, key->wk_keytsc, data_len, &aes_ccm);
    382 	aes_ccm_enc_mbuf(&aes_ccm, m, hdrlen + ccmp.ic_header, data_len, mic);
    383 
    384 	return m_append(m, ccmp.ic_trailer, mic);
    385 }
    386 
    387 static int
    388 ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m,
    389     int hdrlen)
    390 {
    391 	struct ccmp_ctx *ctx = key->wk_private;
    392 	struct ieee80211_frame *wh;
    393 	struct aes_ccm aes_ccm;
    394 	size_t data_len;
    395 	uint8_t mic[IEEE80211_WEP_MICLEN];
    396 
    397 	KASSERT(hdrlen >= 0);
    398 	KASSERT(hdrlen < m->m_pkthdr.len);
    399 	KASSERT(ccmp.ic_header < m->m_pkthdr.len - hdrlen);
    400 	KASSERT(ccmp.ic_trailer < m->m_pkthdr.len - (hdrlen + ccmp.ic_header));
    401 
    402 	ctx->cc_ic->ic_stats.is_crypto_ccmp++;
    403 
    404 	wh = mtod(m, struct ieee80211_frame *);
    405 	data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer);
    406 	ccmp_init_blocks(&ctx->cc_aes, wh, pn, data_len, &aes_ccm);
    407 	m_copydata(m, m->m_pkthdr.len - ccmp.ic_trailer, ccmp.ic_trailer, mic);
    408 
    409 	if (!aes_ccm_dec_mbuf(&aes_ccm, m, hdrlen + ccmp.ic_header, data_len,
    410 		mic)) {
    411 		IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
    412 		    "[%s] AES-CCM decrypt failed; MIC mismatch\n",
    413 		    ether_sprintf(wh->i_addr2));
    414 		ctx->cc_ic->ic_stats.is_rx_ccmpmic++;
    415 		return 0;
    416 	}
    417 
    418 	return 1;
    419 }
    420 
    421 IEEE80211_CRYPTO_SETUP(ccmp_register)
    422 {
    423 	ieee80211_crypto_register(&ccmp);
    424 }
    425