The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/net80211/ieee80211_crypto_ccmp.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $OpenBSD: ieee80211_crypto_ccmp.c,v 1.22 2020/05/15 14:21:09 stsp Exp $ */
    2 
    3 /*-
    4  * Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr>
    5  *
    6  * Permission to use, copy, modify, and distribute this software for any
    7  * purpose with or without fee is hereby granted, provided that the above
    8  * copyright notice and this permission notice appear in all copies.
    9  *
   10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   17  */
   18 
   19 /*
   20  * This code implements the CTR with CBC-MAC protocol (CCMP) defined in
   21  * IEEE Std 802.11-2007 section 8.3.3.
   22  */
   23 
   24 #include <sys/param.h>
   25 #include <sys/systm.h>
   26 #include <sys/mbuf.h>
   27 #include <sys/malloc.h>
   28 #include <sys/kernel.h>
   29 #include <sys/socket.h>
   30 #include <sys/endian.h>
   31 
   32 #include <net/if.h>
   33 #include <net/if_dl.h>
   34 #include <net/if_media.h>
   35 
   36 #include <netinet/in.h>
   37 #include <netinet/if_ether.h>
   38 
   39 #include <net80211/ieee80211_var.h>
   40 #include <net80211/ieee80211_crypto.h>
   41 
   42 #include <crypto/aes.h>
   43 
   44 /* CCMP software crypto context */
   45 struct ieee80211_ccmp_ctx {
   46         AES_CTX         aesctx;
   47 };
   48 
   49 /*
   50  * Initialize software crypto context.  This function can be overridden
   51  * by drivers doing hardware crypto.
   52  */
   53 int
   54 ieee80211_ccmp_set_key(struct ieee80211com *ic, struct ieee80211_key *k)
   55 {
   56         struct ieee80211_ccmp_ctx *ctx;
   57 
   58         ctx = malloc(sizeof(*ctx), M_DEVBUF, M_NOWAIT | M_ZERO);
   59         if (ctx == NULL)
   60                 return ENOMEM;
   61         AES_Setkey(&ctx->aesctx, k->k_key, 16);
   62         k->k_priv = ctx;
   63         return 0;
   64 }
   65 
   66 void
   67 ieee80211_ccmp_delete_key(struct ieee80211com *ic, struct ieee80211_key *k)
   68 {
   69         if (k->k_priv != NULL) {
   70                 explicit_bzero(k->k_priv, sizeof(struct ieee80211_ccmp_ctx));
   71                 free(k->k_priv, M_DEVBUF, sizeof(struct ieee80211_ccmp_ctx));
   72         }
   73         k->k_priv = NULL;
   74 }
   75 
   76 /*-
   77  * Counter with CBC-MAC (CCM) - see RFC3610.
   78  * CCMP uses the following CCM parameters: M = 8, L = 2
   79  */
   80 static void
   81 ieee80211_ccmp_phase1(AES_CTX *ctx, const struct ieee80211_frame *wh,
   82     u_int64_t pn, int lm, u_int8_t b[16], u_int8_t a[16], u_int8_t s0[16])
   83 {
   84         u_int8_t auth[32], nonce[13];
   85         u_int8_t *aad;
   86         u_int8_t tid = 0;
   87         int la, i;
   88 
   89         /* construct AAD (additional authenticated data) */
   90         aad = &auth[2]; /* skip l(a), will be filled later */
   91         *aad = wh->i_fc[0];
   92         /* 11w: conditionally mask subtype field */
   93         if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
   94             IEEE80211_FC0_TYPE_DATA)
   95                 *aad &= ~IEEE80211_FC0_SUBTYPE_MASK |
   96                    IEEE80211_FC0_SUBTYPE_QOS;
   97         aad++;
   98         /* protected bit is already set in wh */
   99         *aad = wh->i_fc[1];
  100         *aad &= ~(IEEE80211_FC1_RETRY | IEEE80211_FC1_PWR_MGT |
  101             IEEE80211_FC1_MORE_DATA);
  102         /* 11n: conditionally mask order bit */
  103         if (ieee80211_has_qos(wh))
  104                 *aad &= ~IEEE80211_FC1_ORDER;
  105         aad++;
  106         IEEE80211_ADDR_COPY(aad, wh->i_addr1); aad += IEEE80211_ADDR_LEN;
  107         IEEE80211_ADDR_COPY(aad, wh->i_addr2); aad += IEEE80211_ADDR_LEN;
  108         IEEE80211_ADDR_COPY(aad, wh->i_addr3); aad += IEEE80211_ADDR_LEN;
  109         *aad++ = wh->i_seq[0] & ~0xf0;
  110         *aad++ = 0;
  111         if (ieee80211_has_addr4(wh)) {
  112                 IEEE80211_ADDR_COPY(aad,
  113                     ((const struct ieee80211_frame_addr4 *)wh)->i_addr4);
  114                 aad += IEEE80211_ADDR_LEN;
  115         }
  116         if (ieee80211_has_qos(wh)) {
  117                 /* 
  118                  * XXX 802.11-2012 11.4.3.3.3 g says the A-MSDU present bit
  119                  * must be set here if both STAs are SPP A-MSDU capable.
  120                  */
  121                 *aad++ = tid = ieee80211_get_qos(wh) & IEEE80211_QOS_TID;
  122                 *aad++ = 0;
  123         }
  124 
  125         /* construct CCM nonce */
  126         nonce[ 0] = tid;
  127         if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
  128             IEEE80211_FC0_TYPE_MGT)
  129                 nonce[0] |= 1 << 4;     /* 11w: set management bit */
  130         IEEE80211_ADDR_COPY(&nonce[1], wh->i_addr2);
  131         nonce[ 7] = pn >> 40;   /* PN5 */
  132         nonce[ 8] = pn >> 32;   /* PN4 */
  133         nonce[ 9] = pn >> 24;   /* PN3 */
  134         nonce[10] = pn >> 16;   /* PN2 */
  135         nonce[11] = pn >> 8;    /* PN1 */
  136         nonce[12] = pn;         /* PN0 */
  137 
  138         /* add 2 authentication blocks (including l(a) and padded AAD) */
  139         la = aad - &auth[2];            /* fill l(a) */
  140         auth[0] = la >> 8;
  141         auth[1] = la & 0xff;
  142         memset(aad, 0, 30 - la);        /* pad AAD with zeros */
  143 
  144         /* construct first block B_0 */
  145         b[ 0] = 89;     /* Flags = 64*Adata + 8*((M-2)/2) + (L-1) */
  146         memcpy(&b[1], nonce, 13);
  147         b[14] = lm >> 8;
  148         b[15] = lm & 0xff;
  149         AES_Encrypt(ctx, b, b);
  150 
  151         for (i = 0; i < 16; i++)
  152                 b[i] ^= auth[i];
  153         AES_Encrypt(ctx, b, b);
  154         for (i = 0; i < 16; i++)
  155                 b[i] ^= auth[16 + i];
  156         AES_Encrypt(ctx, b, b);
  157 
  158         /* construct S_0 */
  159         a[ 0] = 1;      /* Flags = L' = (L-1) */
  160         memcpy(&a[1], nonce, 13);
  161         a[14] = a[15] = 0;
  162         AES_Encrypt(ctx, a, s0);
  163 }
  164 
  165 struct mbuf *
  166 ieee80211_ccmp_encrypt(struct ieee80211com *ic, struct mbuf *m0,
  167     struct ieee80211_key *k)
  168 {
  169         struct ieee80211_ccmp_ctx *ctx = k->k_priv;
  170         const struct ieee80211_frame *wh;
  171         const u_int8_t *src;
  172         u_int8_t *ivp, *mic, *dst;
  173         u_int8_t a[16], b[16], s0[16], s[16];
  174         struct mbuf *n0, *m, *n;
  175         int hdrlen, left, moff, noff, len;
  176         u_int16_t ctr;
  177         int i, j;
  178 
  179         MGET(n0, M_DONTWAIT, m0->m_type);
  180         if (n0 == NULL)
  181                 goto nospace;
  182         if (m_dup_pkthdr(n0, m0, M_DONTWAIT))
  183                 goto nospace;
  184         n0->m_pkthdr.len += IEEE80211_CCMP_HDRLEN;
  185         n0->m_len = MHLEN;
  186         if (n0->m_pkthdr.len >= MINCLSIZE - IEEE80211_CCMP_MICLEN) {
  187                 MCLGET(n0, M_DONTWAIT);
  188                 if (n0->m_flags & M_EXT)
  189                         n0->m_len = n0->m_ext.ext_size;
  190         }
  191         if (n0->m_len > n0->m_pkthdr.len)
  192                 n0->m_len = n0->m_pkthdr.len;
  193 
  194         /* copy 802.11 header */
  195         wh = mtod(m0, struct ieee80211_frame *);
  196         hdrlen = ieee80211_get_hdrlen(wh);
  197         memcpy(mtod(n0, caddr_t), wh, hdrlen);
  198 
  199         k->k_tsc++;     /* increment the 48-bit PN */
  200 
  201         /* construct CCMP header */
  202         ivp = mtod(n0, u_int8_t *) + hdrlen;
  203         ivp[0] = k->k_tsc;              /* PN0 */
  204         ivp[1] = k->k_tsc >> 8;         /* PN1 */
  205         ivp[2] = 0;                     /* Rsvd */
  206         ivp[3] = k->k_id << 6 | IEEE80211_WEP_EXTIV;    /* KeyID | ExtIV */
  207         ivp[4] = k->k_tsc >> 16;        /* PN2 */
  208         ivp[5] = k->k_tsc >> 24;        /* PN3 */
  209         ivp[6] = k->k_tsc >> 32;        /* PN4 */
  210         ivp[7] = k->k_tsc >> 40;        /* PN5 */
  211 
  212         /* construct initial B, A and S_0 blocks */
  213         ieee80211_ccmp_phase1(&ctx->aesctx, wh, k->k_tsc,
  214             m0->m_pkthdr.len - hdrlen, b, a, s0);
  215 
  216         /* construct S_1 */
  217         ctr = 1;
  218         a[14] = ctr >> 8;
  219         a[15] = ctr & 0xff;
  220         AES_Encrypt(&ctx->aesctx, a, s);
  221 
  222         /* encrypt frame body and compute MIC */
  223         j = 0;
  224         m = m0;
  225         n = n0;
  226         moff = hdrlen;
  227         noff = hdrlen + IEEE80211_CCMP_HDRLEN;
  228         left = m0->m_pkthdr.len - moff;
  229         while (left > 0) {
  230                 if (moff == m->m_len) {
  231                         /* nothing left to copy from m */
  232                         m = m->m_next;
  233                         moff = 0;
  234                 }
  235                 if (noff == n->m_len) {
  236                         /* n is full and there's more data to copy */
  237                         MGET(n->m_next, M_DONTWAIT, n->m_type);
  238                         if (n->m_next == NULL)
  239                                 goto nospace;
  240                         n = n->m_next;
  241                         n->m_len = MLEN;
  242                         if (left >= MINCLSIZE - IEEE80211_CCMP_MICLEN) {
  243                                 MCLGET(n, M_DONTWAIT);
  244                                 if (n->m_flags & M_EXT)
  245                                         n->m_len = n->m_ext.ext_size;
  246                         }
  247                         if (n->m_len > left)
  248                                 n->m_len = left;
  249                         noff = 0;
  250                 }
  251                 len = min(m->m_len - moff, n->m_len - noff);
  252 
  253                 src = mtod(m, u_int8_t *) + moff;
  254                 dst = mtod(n, u_int8_t *) + noff;
  255                 for (i = 0; i < len; i++) {
  256                         /* update MIC with clear text */
  257                         b[j] ^= src[i];
  258                         /* encrypt message */
  259                         dst[i] = src[i] ^ s[j];
  260                         if (++j < 16)
  261                                 continue;
  262                         /* we have a full block, encrypt MIC */
  263                         AES_Encrypt(&ctx->aesctx, b, b);
  264                         /* construct a new S_ctr block */
  265                         ctr++;
  266                         a[14] = ctr >> 8;
  267                         a[15] = ctr & 0xff;
  268                         AES_Encrypt(&ctx->aesctx, a, s);
  269                         j = 0;
  270                 }
  271 
  272                 moff += len;
  273                 noff += len;
  274                 left -= len;
  275         }
  276         if (j != 0)     /* partial block, encrypt MIC */
  277                 AES_Encrypt(&ctx->aesctx, b, b);
  278 
  279         /* reserve trailing space for MIC */
  280         if (m_trailingspace(n) < IEEE80211_CCMP_MICLEN) {
  281                 MGET(n->m_next, M_DONTWAIT, n->m_type);
  282                 if (n->m_next == NULL)
  283                         goto nospace;
  284                 n = n->m_next;
  285                 n->m_len = 0;
  286         }
  287         /* finalize MIC, U := T XOR first-M-bytes( S_0 ) */
  288         mic = mtod(n, u_int8_t *) + n->m_len;
  289         for (i = 0; i < IEEE80211_CCMP_MICLEN; i++)
  290                 mic[i] = b[i] ^ s0[i];
  291         n->m_len += IEEE80211_CCMP_MICLEN;
  292         n0->m_pkthdr.len += IEEE80211_CCMP_MICLEN;
  293 
  294         m_freem(m0);
  295         return n0;
  296  nospace:
  297         ic->ic_stats.is_tx_nombuf++;
  298         m_freem(m0);
  299         m_freem(n0);
  300         return NULL;
  301 }
  302 
  303 int
  304 ieee80211_ccmp_get_pn(uint64_t *pn, uint64_t **prsc, struct mbuf *m,
  305     struct ieee80211_key *k)
  306 {
  307         struct ieee80211_frame *wh;
  308         int hdrlen;
  309         const u_int8_t *ivp;
  310 
  311         wh = mtod(m, struct ieee80211_frame *);
  312         hdrlen = ieee80211_get_hdrlen(wh);
  313         if (m->m_pkthdr.len < hdrlen + IEEE80211_CCMP_HDRLEN)
  314                 return EINVAL;
  315 
  316         ivp = (u_int8_t *)wh + hdrlen;
  317 
  318         /* check that ExtIV bit is set */
  319         if (!(ivp[3] & IEEE80211_WEP_EXTIV))
  320                 return EINVAL;
  321 
  322         /* retrieve last seen packet number for this frame type/priority */
  323         if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
  324             IEEE80211_FC0_TYPE_DATA) {
  325                 u_int8_t tid = ieee80211_has_qos(wh) ?
  326                     ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
  327                 *prsc = &k->k_rsc[tid];
  328         } else  /* 11w: management frames have their own counters */
  329                 *prsc = &k->k_mgmt_rsc;
  330 
  331         /* extract the 48-bit PN from the CCMP header */
  332         *pn = (u_int64_t)ivp[0]      |
  333              (u_int64_t)ivp[1] <<  8 |
  334              (u_int64_t)ivp[4] << 16 |
  335              (u_int64_t)ivp[5] << 24 |
  336              (u_int64_t)ivp[6] << 32 |
  337              (u_int64_t)ivp[7] << 40;
  338 
  339         return 0;
  340 }
  341 
  342 struct mbuf *
  343 ieee80211_ccmp_decrypt(struct ieee80211com *ic, struct mbuf *m0,
  344     struct ieee80211_key *k)
  345 {
  346         struct ieee80211_ccmp_ctx *ctx = k->k_priv;
  347         struct ieee80211_frame *wh;
  348         u_int64_t pn, *prsc;
  349         const u_int8_t *src;
  350         u_int8_t *dst;
  351         u_int8_t mic0[IEEE80211_CCMP_MICLEN];
  352         u_int8_t a[16], b[16], s0[16], s[16];
  353         struct mbuf *n0, *m, *n;
  354         int hdrlen, left, moff, noff, len;
  355         u_int16_t ctr;
  356         int i, j;
  357 
  358         wh = mtod(m0, struct ieee80211_frame *);
  359         hdrlen = ieee80211_get_hdrlen(wh);
  360         if (m0->m_pkthdr.len < hdrlen + IEEE80211_CCMP_HDRLEN +
  361             IEEE80211_CCMP_MICLEN) {
  362                 m_freem(m0);
  363                 return NULL;
  364         }
  365 
  366         /*
  367          * Get the frame's Packet Number (PN) and a pointer to our last-seen
  368          * Receive Sequence Counter (RSC) which we can use to detect replays.
  369          */
  370         if (ieee80211_ccmp_get_pn(&pn, &prsc, m0, k) != 0) {
  371                 m_freem(m0);
  372                 return NULL;
  373         }
  374         if (pn <= *prsc) {
  375                 /* replayed frame, discard */
  376                 ic->ic_stats.is_ccmp_replays++;
  377                 m_freem(m0);
  378                 return NULL;
  379         }
  380 
  381         MGET(n0, M_DONTWAIT, m0->m_type);
  382         if (n0 == NULL)
  383                 goto nospace;
  384         if (m_dup_pkthdr(n0, m0, M_DONTWAIT))
  385                 goto nospace;
  386         n0->m_pkthdr.len -= IEEE80211_CCMP_HDRLEN + IEEE80211_CCMP_MICLEN;
  387         n0->m_len = MHLEN;
  388         if (n0->m_pkthdr.len >= MINCLSIZE) {
  389                 MCLGET(n0, M_DONTWAIT);
  390                 if (n0->m_flags & M_EXT)
  391                         n0->m_len = n0->m_ext.ext_size;
  392         }
  393         if (n0->m_len > n0->m_pkthdr.len)
  394                 n0->m_len = n0->m_pkthdr.len;
  395 
  396         /* construct initial B, A and S_0 blocks */
  397         ieee80211_ccmp_phase1(&ctx->aesctx, wh, pn,
  398             n0->m_pkthdr.len - hdrlen, b, a, s0);
  399 
  400         /* copy 802.11 header and clear protected bit */
  401         memcpy(mtod(n0, caddr_t), wh, hdrlen);
  402         wh = mtod(n0, struct ieee80211_frame *);
  403         wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
  404 
  405         /* construct S_1 */
  406         ctr = 1;
  407         a[14] = ctr >> 8;
  408         a[15] = ctr & 0xff;
  409         AES_Encrypt(&ctx->aesctx, a, s);
  410 
  411         /* decrypt frame body and compute MIC */
  412         j = 0;
  413         m = m0;
  414         n = n0;
  415         moff = hdrlen + IEEE80211_CCMP_HDRLEN;
  416         noff = hdrlen;
  417         left = n0->m_pkthdr.len - noff;
  418         while (left > 0) {
  419                 if (moff == m->m_len) {
  420                         /* nothing left to copy from m */
  421                         m = m->m_next;
  422                         moff = 0;
  423                 }
  424                 if (noff == n->m_len) {
  425                         /* n is full and there's more data to copy */
  426                         MGET(n->m_next, M_DONTWAIT, n->m_type);
  427                         if (n->m_next == NULL)
  428                                 goto nospace;
  429                         n = n->m_next;
  430                         n->m_len = MLEN;
  431                         if (left >= MINCLSIZE) {
  432                                 MCLGET(n, M_DONTWAIT);
  433                                 if (n->m_flags & M_EXT)
  434                                         n->m_len = n->m_ext.ext_size;
  435                         }
  436                         if (n->m_len > left)
  437                                 n->m_len = left;
  438                         noff = 0;
  439                 }
  440                 len = min(m->m_len - moff, n->m_len - noff);
  441 
  442                 src = mtod(m, u_int8_t *) + moff;
  443                 dst = mtod(n, u_int8_t *) + noff;
  444                 for (i = 0; i < len; i++) {
  445                         /* decrypt message */
  446                         dst[i] = src[i] ^ s[j];
  447                         /* update MIC with clear text */
  448                         b[j] ^= dst[i];
  449                         if (++j < 16)
  450                                 continue;
  451                         /* we have a full block, encrypt MIC */
  452                         AES_Encrypt(&ctx->aesctx, b, b);
  453                         /* construct a new S_ctr block */
  454                         ctr++;
  455                         a[14] = ctr >> 8;
  456                         a[15] = ctr & 0xff;
  457                         AES_Encrypt(&ctx->aesctx, a, s);
  458                         j = 0;
  459                 }
  460 
  461                 moff += len;
  462                 noff += len;
  463                 left -= len;
  464         }
  465         if (j != 0)     /* partial block, encrypt MIC */
  466                 AES_Encrypt(&ctx->aesctx, b, b);
  467 
  468         /* finalize MIC, U := T XOR first-M-bytes( S_0 ) */
  469         for (i = 0; i < IEEE80211_CCMP_MICLEN; i++)
  470                 b[i] ^= s0[i];
  471 
  472         /* check that it matches the MIC in received frame */
  473         m_copydata(m, moff, IEEE80211_CCMP_MICLEN, mic0);
  474         if (timingsafe_bcmp(mic0, b, IEEE80211_CCMP_MICLEN) != 0) {
  475                 ic->ic_stats.is_ccmp_dec_errs++;
  476                 m_freem(m0);
  477                 m_freem(n0);
  478                 return NULL;
  479         }
  480 
  481         /* update last seen packet number (MIC is validated) */
  482         *prsc = pn;
  483 
  484         m_freem(m0);
  485         return n0;
  486  nospace:
  487         ic->ic_stats.is_rx_nombuf++;
  488         m_freem(m0);
  489         m_freem(n0);
  490         return NULL;
  491 }

Cache object: 5fdbdd90334545e59a09f99e0ec794dc


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.