iPXE
wpa_ccmp.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  */
00019 
00020 FILE_LICENCE ( GPL2_OR_LATER );
00021 
00022 #include <string.h>
00023 #include <ipxe/net80211.h>
00024 #include <ipxe/crypto.h>
00025 #include <ipxe/hmac.h>
00026 #include <ipxe/sha1.h>
00027 #include <ipxe/aes.h>
00028 #include <ipxe/wpa.h>
00029 #include <byteswap.h>
00030 #include <errno.h>
00031 
00032 /** @file
00033  *
00034  * Backend for WPA using the CCMP encryption method
00035  */
00036 
00037 /** Context for CCMP encryption and decryption */
00038 struct ccmp_ctx
00039 {
00040         /** AES context - only ever used for encryption */
00041         u8 aes_ctx[AES_CTX_SIZE];
00042 
00043         /** Most recently sent packet number */
00044         u64 tx_seq;
00045 
00046         /** Most recently received packet number */
00047         u64 rx_seq;
00048 };
00049 
00050 /** Header structure at the beginning of CCMP frame data */
00051 struct ccmp_head
00052 {
00053         u8 pn_lo[2];            /**< Bytes 0 and 1 of packet number */
00054         u8 _rsvd;               /**< Reserved byte */
00055         u8 kid;                 /**< Key ID and ExtIV byte */
00056         u8 pn_hi[4];            /**< Bytes 2-5 (2 first) of packet number */
00057 } __attribute__ (( packed ));
00058 
00059 
00060 /** CCMP header overhead */
00061 #define CCMP_HEAD_LEN   8
00062 
00063 /** CCMP MIC trailer overhead */
00064 #define CCMP_MIC_LEN    8
00065 
00066 /** CCMP nonce length */
00067 #define CCMP_NONCE_LEN  13
00068 
00069 /** CCMP nonce structure */
00070 struct ccmp_nonce
00071 {
00072         u8 prio;                /**< Packet priority, 0 for non-QoS */
00073         u8 a2[ETH_ALEN];        /**< Address 2 from packet header (sender) */
00074         u8 pn[6];               /**< Packet number */
00075 } __attribute__ (( packed ));
00076 
00077 /** CCMP additional authentication data length (for non-QoS, non-WDS frames) */
00078 #define CCMP_AAD_LEN    22
00079 
00080 /** CCMP additional authentication data structure */
00081 struct ccmp_aad
00082 {
00083         u16 fc;                 /**< Frame Control field */
00084         u8 a1[6];               /**< Address 1 */
00085         u8 a2[6];               /**< Address 2 */
00086         u8 a3[6];               /**< Address 3 */
00087         u16 seq;                /**< Sequence Control field */
00088         /* Address 4 and QoS Control are included if present */
00089 } __attribute__ (( packed ));
00090 
00091 /** Mask for Frame Control field in AAD */
00092 #define CCMP_AAD_FC_MASK        0xC38F
00093 
00094 /** Mask for Sequence Control field in AAD */
00095 #define CCMP_AAD_SEQ_MASK       0x000F
00096 
00097 
00098 /**
00099  * Convert 6-byte LSB packet number to 64-bit integer
00100  *
00101  * @v pn        Pointer to 6-byte packet number
00102  * @ret v       64-bit integer value of @a pn
00103  */
00104 static u64 pn_to_u64 ( const u8 *pn )
00105 {
00106         int i;
00107         u64 ret = 0;
00108 
00109         for ( i = 5; i >= 0; i-- ) {
00110                 ret <<= 8;
00111                 ret |= pn[i];
00112         }
00113 
00114         return ret;
00115 }
00116 
00117 /**
00118  * Convert 64-bit integer to 6-byte packet number
00119  *
00120  * @v v         64-bit integer
00121  * @v msb       If TRUE, reverse the output PN to be in MSB order
00122  * @ret pn      6-byte packet number
00123  *
00124  * The PN is stored in LSB order in the packet header and in MSB order
00125  * in the nonce. WHYYYYY?
00126  */
00127 static void u64_to_pn ( u64 v, u8 *pn, int msb )
00128 {
00129         int i;
00130         u8 *pnp = pn + ( msb ? 5 : 0 );
00131         int delta = ( msb ? -1 : +1 );
00132 
00133         for ( i = 0; i < 6; i++ ) {
00134                 *pnp = v & 0xFF;
00135                 pnp += delta;
00136                 v >>= 8;
00137         }
00138 }
00139 
00140 /** Value for @a msb argument of u64_to_pn() for MSB output */
00141 #define PN_MSB  1
00142 
00143 /** Value for @a msb argument of u64_to_pn() for LSB output */
00144 #define PN_LSB  0
00145 
00146 
00147 
00148 /**
00149  * Initialise CCMP state and install key
00150  *
00151  * @v crypto    CCMP cryptosystem structure
00152  * @v key       Pointer to 16-byte temporal key to install
00153  * @v keylen    Length of key (16 bytes)
00154  * @v rsc       Initial receive sequence counter
00155  */
00156 static int ccmp_init ( struct net80211_crypto *crypto, const void *key,
00157                        int keylen, const void *rsc )
00158 {
00159         struct ccmp_ctx *ctx = crypto->priv;
00160 
00161         if ( keylen != 16 )
00162                 return -EINVAL;
00163 
00164         if ( rsc )
00165                 ctx->rx_seq = pn_to_u64 ( rsc );
00166 
00167         cipher_setkey ( &aes_algorithm, ctx->aes_ctx, key, keylen );
00168 
00169         return 0;
00170 }
00171 
00172 
00173 /**
00174  * Encrypt or decrypt data stream using AES in Counter mode
00175  *
00176  * @v ctx       CCMP cryptosystem context
00177  * @v nonce     Nonce value, 13 bytes
00178  * @v srcv      Data to encrypt or decrypt
00179  * @v len       Number of bytes pointed to by @a src
00180  * @v msrcv     MIC value to encrypt or decrypt (may be NULL)
00181  * @ret destv   Encrypted or decrypted data
00182  * @ret mdestv  Encrypted or decrypted MIC value
00183  *
00184  * This assumes CCMP parameters of L=2 and M=8. The algorithm is
00185  * defined in RFC 3610.
00186  */
00187 static void ccmp_ctr_xor ( struct ccmp_ctx *ctx, const void *nonce,
00188                            const void *srcv, void *destv, int len,
00189                            const void *msrcv, void *mdestv )
00190 {
00191         u8 A[16], S[16];
00192         u16 ctr;
00193         int i;
00194         const u8 *src = srcv, *msrc = msrcv;
00195         u8 *dest = destv, *mdest = mdestv;
00196 
00197         A[0] = 0x01;            /* flags, L' = L - 1 = 1, other bits rsvd */
00198         memcpy ( A + 1, nonce, CCMP_NONCE_LEN );
00199 
00200         if ( msrcv ) {
00201                 A[14] = A[15] = 0;
00202 
00203                 cipher_encrypt ( &aes_algorithm, ctx->aes_ctx, A, S, 16 );
00204 
00205                 for ( i = 0; i < 8; i++ ) {
00206                         *mdest++ = *msrc++ ^ S[i];
00207                 }
00208         }
00209 
00210         for ( ctr = 1 ;; ctr++ ) {
00211                 A[14] = ctr >> 8;
00212                 A[15] = ctr & 0xFF;
00213 
00214                 cipher_encrypt ( &aes_algorithm, ctx->aes_ctx, A, S, 16 );
00215 
00216                 for ( i = 0; i < len && i < 16; i++ )
00217                         *dest++ = *src++ ^ S[i];
00218 
00219                 if ( len <= 16 )
00220                         break;  /* we're done */
00221 
00222                 len -= 16;
00223         }
00224 }
00225 
00226 
00227 /**
00228  * Advance one block in CBC-MAC calculation
00229  *
00230  * @v aes_ctx   AES encryption context with key set
00231  * @v B         Cleartext block to incorporate (16 bytes)
00232  * @v X         Previous ciphertext block (16 bytes)
00233  * @ret B       Clobbered
00234  * @ret X       New ciphertext block (16 bytes)
00235  *
00236  * This function does X := E[key] ( X ^ B ).
00237  */
00238 static void ccmp_feed_cbc_mac ( void *aes_ctx, u8 *B, u8 *X )
00239 {
00240         int i;
00241         for ( i = 0; i < 16; i++ )
00242                 B[i] ^= X[i];
00243         cipher_encrypt ( &aes_algorithm, aes_ctx, B, X, 16 );
00244 }
00245 
00246 
00247 /**
00248  * Calculate MIC on plaintext data using CBC-MAC
00249  *
00250  * @v ctx       CCMP cryptosystem context
00251  * @v nonce     Nonce value, 13 bytes
00252  * @v data      Data to calculate MIC over
00253  * @v datalen   Length of @a data
00254  * @v aad       Additional authentication data, for MIC but not encryption
00255  * @ret mic     MIC value (unencrypted), 8 bytes
00256  *
00257  * @a aadlen is assumed to be 22 bytes long, as it always is for
00258  * 802.11 use when transmitting non-QoS, not-between-APs frames (the
00259  * only type we deal with).
00260  */
00261 static void ccmp_cbc_mac ( struct ccmp_ctx *ctx, const void *nonce,
00262                            const void *data, u16 datalen,
00263                            const void *aad, void *mic )
00264 {
00265         u8 X[16], B[16];
00266 
00267         /* Zeroth block: flags, nonce, length */
00268 
00269         /* Rsv AAD - M'-  - L'-
00270          *  0   1  0 1 1  0 0 1   for an 8-byte MAC and 2-byte message length
00271          */
00272         B[0] = 0x59;
00273         memcpy ( B + 1, nonce, CCMP_NONCE_LEN );
00274         B[14] = datalen >> 8;
00275         B[15] = datalen & 0xFF;
00276 
00277         cipher_encrypt ( &aes_algorithm, ctx->aes_ctx, B, X, 16 );
00278 
00279         /* First block: AAD length field and 14 bytes of AAD */
00280         B[0] = 0;
00281         B[1] = CCMP_AAD_LEN;
00282         memcpy ( B + 2, aad, 14 );
00283 
00284         ccmp_feed_cbc_mac ( ctx->aes_ctx, B, X );
00285 
00286         /* Second block: Remaining 8 bytes of AAD, 8 bytes zero pad */
00287         memcpy ( B, aad + 14, 8 );
00288         memset ( B + 8, 0, 8 );
00289 
00290         ccmp_feed_cbc_mac ( ctx->aes_ctx, B, X );
00291 
00292         /* Message blocks */
00293         while ( datalen ) {
00294                 if ( datalen >= 16 ) {
00295                         memcpy ( B, data, 16 );
00296                         datalen -= 16;
00297                 } else {
00298                         memcpy ( B, data, datalen );
00299                         memset ( B + datalen, 0, 16 - datalen );
00300                         datalen = 0;
00301                 }
00302 
00303                 ccmp_feed_cbc_mac ( ctx->aes_ctx, B, X );
00304 
00305                 data += 16;
00306         }
00307 
00308         /* Get MIC from final value of X */
00309         memcpy ( mic, X, 8 );
00310 }
00311 
00312 
00313 /**
00314  * Encapsulate and encrypt a packet using CCMP
00315  *
00316  * @v crypto    CCMP cryptosystem
00317  * @v iob       I/O buffer containing cleartext packet
00318  * @ret eiob    I/O buffer containing encrypted packet
00319  */
00320 struct io_buffer * ccmp_encrypt ( struct net80211_crypto *crypto,
00321                                   struct io_buffer *iob )
00322 {
00323         struct ccmp_ctx *ctx = crypto->priv;
00324         struct ieee80211_frame *hdr = iob->data;
00325         struct io_buffer *eiob;
00326         const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN;
00327         int datalen = iob_len ( iob ) - hdrlen;
00328         struct ccmp_head head;
00329         struct ccmp_nonce nonce;
00330         struct ccmp_aad aad;
00331         u8 mic[8], tx_pn[6];
00332         void *edata, *emic;
00333 
00334         ctx->tx_seq++;
00335         u64_to_pn ( ctx->tx_seq, tx_pn, PN_LSB );
00336 
00337         /* Allocate memory */
00338         eiob = alloc_iob ( iob_len ( iob ) + CCMP_HEAD_LEN + CCMP_MIC_LEN );
00339         if ( ! eiob )
00340                 return NULL;
00341 
00342         /* Copy frame header */
00343         memcpy ( iob_put ( eiob, hdrlen ), iob->data, hdrlen );
00344         hdr = eiob->data;
00345         hdr->fc |= IEEE80211_FC_PROTECTED;
00346 
00347         /* Fill in packet number and extended IV */
00348         memcpy ( head.pn_lo, tx_pn, 2 );
00349         memcpy ( head.pn_hi, tx_pn + 2, 4 );
00350         head.kid = 0x20;        /* have Extended IV, key ID 0 */
00351         head._rsvd = 0;
00352         memcpy ( iob_put ( eiob, sizeof ( head ) ), &head, sizeof ( head ) );
00353 
00354         /* Form nonce */
00355         nonce.prio = 0;
00356         memcpy ( nonce.a2, hdr->addr2, ETH_ALEN );
00357         u64_to_pn ( ctx->tx_seq, nonce.pn, PN_MSB );
00358 
00359         /* Form additional authentication data */
00360         aad.fc = hdr->fc & CCMP_AAD_FC_MASK;
00361         memcpy ( aad.a1, hdr->addr1, 3 * ETH_ALEN ); /* all 3 at once */
00362         aad.seq = hdr->seq & CCMP_AAD_SEQ_MASK;
00363 
00364         /* Calculate MIC over the data */
00365         ccmp_cbc_mac ( ctx, &nonce, iob->data + hdrlen, datalen, &aad, mic );
00366 
00367         /* Copy and encrypt data and MIC */
00368         edata = iob_put ( eiob, datalen );
00369         emic = iob_put ( eiob, CCMP_MIC_LEN );
00370         ccmp_ctr_xor ( ctx, &nonce,
00371                        iob->data + hdrlen, edata, datalen,
00372                        mic, emic );
00373 
00374         /* Done! */
00375         DBGC2 ( ctx, "WPA-CCMP %p: encrypted packet %p -> %p\n", ctx,
00376                 iob, eiob );
00377 
00378         return eiob;
00379 }
00380 
00381 /**
00382  * Decrypt a packet using CCMP
00383  *
00384  * @v crypto    CCMP cryptosystem
00385  * @v eiob      I/O buffer containing encrypted packet
00386  * @ret iob     I/O buffer containing cleartext packet
00387  */
00388 static struct io_buffer * ccmp_decrypt ( struct net80211_crypto *crypto,
00389                                          struct io_buffer *eiob )
00390 {
00391         struct ccmp_ctx *ctx = crypto->priv;
00392         struct ieee80211_frame *hdr;
00393         struct io_buffer *iob;
00394         const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN;
00395         int datalen = iob_len ( eiob ) - hdrlen - CCMP_HEAD_LEN - CCMP_MIC_LEN;
00396         struct ccmp_head *head;
00397         struct ccmp_nonce nonce;
00398         struct ccmp_aad aad;
00399         u8 rx_pn[6], their_mic[8], our_mic[8];
00400 
00401         iob = alloc_iob ( hdrlen + datalen );
00402         if ( ! iob )
00403                 return NULL;
00404 
00405         /* Copy frame header */
00406         memcpy ( iob_put ( iob, hdrlen ), eiob->data, hdrlen );
00407         hdr = iob->data;
00408         hdr->fc &= ~IEEE80211_FC_PROTECTED;
00409 
00410         /* Check and update RX packet number */
00411         head = eiob->data + hdrlen;
00412         memcpy ( rx_pn, head->pn_lo, 2 );
00413         memcpy ( rx_pn + 2, head->pn_hi, 4 );
00414 
00415         if ( pn_to_u64 ( rx_pn ) <= ctx->rx_seq ) {
00416                 DBGC ( ctx, "WPA-CCMP %p: packet received out of order "
00417                        "(%012llx <= %012llx)\n", ctx, pn_to_u64 ( rx_pn ),
00418                        ctx->rx_seq );
00419                 free_iob ( iob );
00420                 return NULL;
00421         }
00422 
00423         ctx->rx_seq = pn_to_u64 ( rx_pn );
00424         DBGC2 ( ctx, "WPA-CCMP %p: RX packet number %012llx\n", ctx, ctx->rx_seq );
00425 
00426         /* Form nonce */
00427         nonce.prio = 0;
00428         memcpy ( nonce.a2, hdr->addr2, ETH_ALEN );
00429         u64_to_pn ( ctx->rx_seq, nonce.pn, PN_MSB );
00430 
00431         /* Form additional authentication data */
00432         aad.fc = ( hdr->fc & CCMP_AAD_FC_MASK ) | IEEE80211_FC_PROTECTED;
00433         memcpy ( aad.a1, hdr->addr1, 3 * ETH_ALEN ); /* all 3 at once */
00434         aad.seq = hdr->seq & CCMP_AAD_SEQ_MASK;
00435 
00436         /* Copy-decrypt data and MIC */
00437         ccmp_ctr_xor ( ctx, &nonce, eiob->data + hdrlen + sizeof ( *head ),
00438                        iob_put ( iob, datalen ), datalen,
00439                        eiob->tail - CCMP_MIC_LEN, their_mic );
00440 
00441         /* Check MIC */
00442         ccmp_cbc_mac ( ctx, &nonce, iob->data + hdrlen, datalen, &aad,
00443                        our_mic );
00444 
00445         if ( memcmp ( their_mic, our_mic, CCMP_MIC_LEN ) != 0 ) {
00446                 DBGC2 ( ctx, "WPA-CCMP %p: MIC failure\n", ctx );
00447                 free_iob ( iob );
00448                 return NULL;
00449         }
00450 
00451         DBGC2 ( ctx, "WPA-CCMP %p: decrypted packet %p -> %p\n", ctx,
00452                 eiob, iob );
00453 
00454         return iob;
00455 }
00456 
00457 
00458 /** CCMP cryptosystem */
00459 struct net80211_crypto ccmp_crypto __net80211_crypto = {
00460         .algorithm = NET80211_CRYPT_CCMP,
00461         .init = ccmp_init,
00462         .encrypt = ccmp_encrypt,
00463         .decrypt = ccmp_decrypt,
00464         .priv_len = sizeof ( struct ccmp_ctx ),
00465 };
00466 
00467 
00468 
00469 
00470 /**
00471  * Calculate HMAC-SHA1 MIC for EAPOL-Key frame
00472  *
00473  * @v kck       Key Confirmation Key, 16 bytes
00474  * @v msg       Message to calculate MIC over
00475  * @v len       Number of bytes to calculate MIC over
00476  * @ret mic     Calculated MIC, 16 bytes long
00477  */
00478 static void ccmp_kie_mic ( const void *kck, const void *msg, size_t len,
00479                            void *mic )
00480 {
00481         u8 sha1_ctx[SHA1_CTX_SIZE];
00482         u8 kckb[16];
00483         u8 hash[SHA1_DIGEST_SIZE];
00484         size_t kck_len = 16;
00485 
00486         memcpy ( kckb, kck, kck_len );
00487 
00488         hmac_init ( &sha1_algorithm, sha1_ctx, kckb, &kck_len );
00489         hmac_update ( &sha1_algorithm, sha1_ctx, msg, len );
00490         hmac_final ( &sha1_algorithm, sha1_ctx, kckb, &kck_len, hash );
00491 
00492         memcpy ( mic, hash, 16 );
00493 }
00494 
00495 /**
00496  * Decrypt key data in EAPOL-Key frame
00497  *
00498  * @v kek       Key Encryption Key, 16 bytes
00499  * @v iv        Initialisation vector, 16 bytes (unused)
00500  * @v msg       Message to decrypt
00501  * @v len       Length of message
00502  * @ret msg     Decrypted message in place of original
00503  * @ret len     Adjusted downward for 8 bytes of overhead
00504  * @ret rc      Return status code
00505  *
00506  * The returned message may still contain padding of 0xDD followed by
00507  * zero or more 0x00 octets. It is impossible to remove the padding
00508  * without parsing the IEs in the packet (another design decision that
00509  * tends to make one question the 802.11i committee's intelligence...)
00510  */
00511 static int ccmp_kie_decrypt ( const void *kek, const void *iv __unused,
00512                               void *msg, u16 *len )
00513 {
00514         if ( *len % 8 != 0 )
00515                 return -EINVAL;
00516 
00517         if ( aes_unwrap ( kek, msg, msg, *len / 8 - 1 ) != 0 )
00518                 return -EINVAL;
00519 
00520         *len -= 8;
00521 
00522         return 0;
00523 }
00524 
00525 /** CCMP-style key integrity and encryption handler */
00526 struct wpa_kie ccmp_kie __wpa_kie = {
00527         .version = EAPOL_KEY_VERSION_WPA2,
00528         .mic = ccmp_kie_mic,
00529         .decrypt = ccmp_kie_decrypt,
00530 };