Home | History | Annotate | Line # | Download | only in net80211
      1 /*	$NetBSD: ieee80211_crypto_ccmp.c,v 1.20 2023/06/24 05:12:03 msaitoh 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.20 2023/06/24 05:12:03 msaitoh 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_intr_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_intr_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 need.
    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 /*
    270  * Host AP crypt: host-based CCMP encryption implementation for Host AP driver
    271  *
    272  * Copyright (c) 2003-2004, Jouni Malinen <jkmaline (at) cc.hut.fi>
    273  *
    274  * This program is free software; you can redistribute it and/or modify
    275  * it under the terms of the GNU General Public License version 2 as
    276  * published by the Free Software Foundation. See README and COPYING for
    277  * more details.
    278  *
    279  * Alternatively, this software may be distributed under the terms of BSD
    280  * license.
    281  */
    282 
    283 static void
    284 ccmp_init_blocks(struct aesenc *ctx, struct ieee80211_frame *wh,
    285     u_int64_t pn, size_t data_len, struct aes_ccm *aes_ccm)
    286 {
    287 	uint8_t nonce[13];
    288 	uint8_t ad[32];
    289 	uint8_t qos;
    290 	size_t adlen;
    291 
    292 #define	IS_4ADDRESS(wh) \
    293 	((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
    294 #define	IS_QOS_DATA(wh)	ieee80211_has_qos(wh)
    295 
    296 	/* nonce[0] is qos, determined later */
    297 	IEEE80211_ADDR_COPY(nonce + 1, wh->i_addr2);
    298 	nonce[7] = pn >> 40;
    299 	nonce[8] = pn >> 32;
    300 	nonce[9] = pn >> 24;
    301 	nonce[10] = pn >> 16;
    302 	nonce[11] = pn >> 8;
    303 	nonce[12] = pn >> 0;
    304 
    305 	ad[0] = wh->i_fc[0] & 0x8f;	/* XXX magic #s */
    306 	ad[1] = wh->i_fc[1] & 0xc7;	/* XXX magic #s */
    307 	/* NB: we know 3 addresses are contiguous */
    308 	memcpy(ad + 2, wh->i_addr1, 3 * IEEE80211_ADDR_LEN);
    309 	ad[20] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK;
    310 	ad[21] = 0; /* all bits masked */
    311 
    312 	/*
    313 	 * Construct variable-length portion of AAD based
    314 	 * on whether this is a 4-address frame/QOS frame.
    315 	 *
    316 	 * We also fill in the priority bits of the CCM
    317 	 * initial block as we know whether or not we have
    318 	 * a QOS frame.
    319 	 */
    320 	if (IS_4ADDRESS(wh)) {
    321 		IEEE80211_ADDR_COPY(ad + 22,
    322 		    ((const struct ieee80211_frame_addr4 *)wh)->i_addr4);
    323 		if (IS_QOS_DATA(wh)) {
    324 			const struct ieee80211_qosframe_addr4 *qwh4 =
    325 			    (const struct ieee80211_qosframe_addr4 *)wh;
    326 			qos = qwh4->i_qos[0] & 0x0f; /* just priority bits */
    327 			ad[28] = qos;
    328 			ad[29] = 0;
    329 			adlen = 22 + IEEE80211_ADDR_LEN + 2;
    330 		} else {
    331 			qos = 0;
    332 			adlen = 22 + IEEE80211_ADDR_LEN;
    333 		}
    334 	} else {
    335 		if (IS_QOS_DATA(wh)) {
    336 			const struct ieee80211_qosframe *qwh =
    337 			    (const struct ieee80211_qosframe *)wh;
    338 			qos = qwh->i_qos[0] & 0x0f; /* just priority bits */
    339 			ad[22] = qos;
    340 			ad[23] = 0;
    341 			adlen = 22 + 2;
    342 		} else {
    343 			qos = 0;
    344 			adlen = 22;
    345 		}
    346 	}
    347 	nonce[0] = qos;
    348 
    349 	aes_ccm_init(aes_ccm, AES_128_NROUNDS, ctx, 2 /* L, counter octets */,
    350 	    IEEE80211_WEP_MICLEN, nonce, sizeof nonce, ad, adlen, data_len);
    351 
    352 #undef	IS_QOS_DATA
    353 #undef	IS_4ADDRESS
    354 }
    355 
    356 static int
    357 ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m, int hdrlen)
    358 {
    359 	struct ccmp_ctx *ctx = key->wk_private;
    360 	struct ieee80211_frame *wh;
    361 	struct aes_ccm aes_ccm;
    362 	size_t data_len;
    363 	uint8_t mic[IEEE80211_WEP_MICLEN];
    364 
    365 	KASSERT(hdrlen >= 0);
    366 	KASSERT(hdrlen < m->m_pkthdr.len);
    367 	KASSERT(ccmp.ic_header <= m->m_pkthdr.len - hdrlen);
    368 
    369 	ctx->cc_ic->ic_stats.is_crypto_ccmp++;
    370 
    371 	wh = mtod(m, struct ieee80211_frame *);
    372 	data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header);
    373 	ccmp_init_blocks(&ctx->cc_aes, wh, key->wk_keytsc, data_len, &aes_ccm);
    374 	aes_ccm_enc_mbuf(&aes_ccm, m, hdrlen + ccmp.ic_header, data_len, mic);
    375 
    376 	return m_append(m, ccmp.ic_trailer, mic);
    377 }
    378 
    379 static int
    380 ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m,
    381     int hdrlen)
    382 {
    383 	struct ccmp_ctx *ctx = key->wk_private;
    384 	struct ieee80211_frame *wh;
    385 	struct aes_ccm aes_ccm;
    386 	size_t data_len;
    387 	uint8_t mic[IEEE80211_WEP_MICLEN];
    388 
    389 	KASSERT(hdrlen >= 0);
    390 	KASSERT(hdrlen < m->m_pkthdr.len);
    391 	KASSERT(ccmp.ic_header < m->m_pkthdr.len - hdrlen);
    392 	KASSERT(ccmp.ic_trailer < m->m_pkthdr.len - (hdrlen + ccmp.ic_header));
    393 
    394 	ctx->cc_ic->ic_stats.is_crypto_ccmp++;
    395 
    396 	wh = mtod(m, struct ieee80211_frame *);
    397 	data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer);
    398 	ccmp_init_blocks(&ctx->cc_aes, wh, pn, data_len, &aes_ccm);
    399 	m_copydata(m, m->m_pkthdr.len - ccmp.ic_trailer, ccmp.ic_trailer, mic);
    400 
    401 	if (!aes_ccm_dec_mbuf(&aes_ccm, m, hdrlen + ccmp.ic_header, data_len,
    402 		mic)) {
    403 		IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
    404 		    "[%s] AES-CCM decrypt failed; MIC mismatch\n",
    405 		    ether_sprintf(wh->i_addr2));
    406 		ctx->cc_ic->ic_stats.is_rx_ccmpmic++;
    407 		return 0;
    408 	}
    409 
    410 	return 1;
    411 }
    412 
    413 IEEE80211_CRYPTO_SETUP(ccmp_register)
    414 {
    415 	ieee80211_crypto_register(&ccmp);
    416 }
    417