iPXE
Data Structures | Defines | Functions | Variables
wpa_ccmp.c File Reference

Backend for WPA using the CCMP encryption method. More...

#include <string.h>
#include <ipxe/net80211.h>
#include <ipxe/crypto.h>
#include <ipxe/hmac.h>
#include <ipxe/sha1.h>
#include <ipxe/aes.h>
#include <ipxe/wpa.h>
#include <byteswap.h>
#include <errno.h>

Go to the source code of this file.

Data Structures

struct  ccmp_ctx
 Context for CCMP encryption and decryption. More...
struct  ccmp_head
 Header structure at the beginning of CCMP frame data. More...
struct  ccmp_nonce
 CCMP nonce structure. More...
struct  ccmp_aad
 CCMP additional authentication data structure. More...

Defines

#define CCMP_HEAD_LEN   8
 CCMP header overhead.
#define CCMP_MIC_LEN   8
 CCMP MIC trailer overhead.
#define CCMP_NONCE_LEN   13
 CCMP nonce length.
#define CCMP_AAD_LEN   22
 CCMP additional authentication data length (for non-QoS, non-WDS frames)
#define CCMP_AAD_FC_MASK   0xC38F
 Mask for Frame Control field in AAD.
#define CCMP_AAD_SEQ_MASK   0x000F
 Mask for Sequence Control field in AAD.
#define PN_MSB   1
 Value for msb argument of u64_to_pn() for MSB output.
#define PN_LSB   0
 Value for msb argument of u64_to_pn() for LSB output.

Functions

 FILE_LICENCE (GPL2_OR_LATER)
static u64 pn_to_u64 (const u8 *pn)
 Convert 6-byte LSB packet number to 64-bit integer.
static void u64_to_pn (u64 v, u8 *pn, int msb)
 Convert 64-bit integer to 6-byte packet number.
static int ccmp_init (struct net80211_crypto *crypto, const void *key, int keylen, const void *rsc)
 Initialise CCMP state and install key.
static void ccmp_ctr_xor (struct ccmp_ctx *ctx, const void *nonce, const void *srcv, void *destv, int len, const void *msrcv, void *mdestv)
 Encrypt or decrypt data stream using AES in Counter mode.
static void ccmp_feed_cbc_mac (void *aes_ctx, u8 *B, u8 *X)
 Advance one block in CBC-MAC calculation.
static void ccmp_cbc_mac (struct ccmp_ctx *ctx, const void *nonce, const void *data, u16 datalen, const void *aad, void *mic)
 Calculate MIC on plaintext data using CBC-MAC.
struct io_bufferccmp_encrypt (struct net80211_crypto *crypto, struct io_buffer *iob)
 Encapsulate and encrypt a packet using CCMP.
static struct io_bufferccmp_decrypt (struct net80211_crypto *crypto, struct io_buffer *eiob)
 Decrypt a packet using CCMP.
static void ccmp_kie_mic (const void *kck, const void *msg, size_t len, void *mic)
 Calculate HMAC-SHA1 MIC for EAPOL-Key frame.
static int ccmp_kie_decrypt (const void *kek, const void *iv __unused, void *msg, u16 *len)
 Decrypt key data in EAPOL-Key frame.

Variables

struct net80211_crypto ccmp_crypto __net80211_crypto
 CCMP cryptosystem.
struct wpa_kie ccmp_kie __wpa_kie
 CCMP-style key integrity and encryption handler.

Detailed Description

Backend for WPA using the CCMP encryption method.

Definition in file wpa_ccmp.c.


Define Documentation

#define CCMP_HEAD_LEN   8

CCMP header overhead.

Definition at line 61 of file wpa_ccmp.c.

Referenced by ccmp_decrypt(), and ccmp_encrypt().

#define CCMP_MIC_LEN   8

CCMP MIC trailer overhead.

Definition at line 64 of file wpa_ccmp.c.

Referenced by ccmp_decrypt(), and ccmp_encrypt().

#define CCMP_NONCE_LEN   13

CCMP nonce length.

Definition at line 67 of file wpa_ccmp.c.

Referenced by ccmp_cbc_mac(), and ccmp_ctr_xor().

#define CCMP_AAD_LEN   22

CCMP additional authentication data length (for non-QoS, non-WDS frames)

Definition at line 78 of file wpa_ccmp.c.

Referenced by ccmp_cbc_mac().

#define CCMP_AAD_FC_MASK   0xC38F

Mask for Frame Control field in AAD.

Definition at line 92 of file wpa_ccmp.c.

Referenced by ccmp_decrypt(), and ccmp_encrypt().

#define CCMP_AAD_SEQ_MASK   0x000F

Mask for Sequence Control field in AAD.

Definition at line 95 of file wpa_ccmp.c.

Referenced by ccmp_decrypt(), and ccmp_encrypt().

#define PN_MSB   1

Value for msb argument of u64_to_pn() for MSB output.

Definition at line 141 of file wpa_ccmp.c.

Referenced by ccmp_decrypt(), and ccmp_encrypt().

#define PN_LSB   0

Value for msb argument of u64_to_pn() for LSB output.

Definition at line 144 of file wpa_ccmp.c.

Referenced by ccmp_encrypt().


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER  )
static u64 pn_to_u64 ( const u8 pn) [static]

Convert 6-byte LSB packet number to 64-bit integer.

Parameters:
pnPointer to 6-byte packet number
Return values:
v64-bit integer value of pn

Definition at line 104 of file wpa_ccmp.c.

References ret.

Referenced by ccmp_decrypt(), and ccmp_init().

{
        int i;
        u64 ret = 0;

        for ( i = 5; i >= 0; i-- ) {
                ret <<= 8;
                ret |= pn[i];
        }

        return ret;
}
static void u64_to_pn ( u64  v,
u8 pn,
int  msb 
) [static]

Convert 64-bit integer to 6-byte packet number.

Parameters:
v64-bit integer
msbIf TRUE, reverse the output PN to be in MSB order
Return values:
pn6-byte packet number

The PN is stored in LSB order in the packet header and in MSB order in the nonce. WHYYYYY?

Definition at line 127 of file wpa_ccmp.c.

Referenced by ccmp_decrypt(), and ccmp_encrypt().

{
        int i;
        u8 *pnp = pn + ( msb ? 5 : 0 );
        int delta = ( msb ? -1 : +1 );

        for ( i = 0; i < 6; i++ ) {
                *pnp = v & 0xFF;
                pnp += delta;
                v >>= 8;
        }
}
static int ccmp_init ( struct net80211_crypto crypto,
const void *  key,
int  keylen,
const void *  rsc 
) [static]

Initialise CCMP state and install key.

Parameters:
cryptoCCMP cryptosystem structure
keyPointer to 16-byte temporal key to install
keylenLength of key (16 bytes)
rscInitial receive sequence counter

Definition at line 156 of file wpa_ccmp.c.

References aes_algorithm, ccmp_ctx::aes_ctx, cipher_setkey(), ctx, EINVAL, pn_to_u64(), net80211_crypto::priv, and ccmp_ctx::rx_seq.

{
        struct ccmp_ctx *ctx = crypto->priv;

        if ( keylen != 16 )
                return -EINVAL;

        if ( rsc )
                ctx->rx_seq = pn_to_u64 ( rsc );

        cipher_setkey ( &aes_algorithm, ctx->aes_ctx, key, keylen );

        return 0;
}
static void ccmp_ctr_xor ( struct ccmp_ctx ctx,
const void *  nonce,
const void *  srcv,
void *  destv,
int  len,
const void *  msrcv,
void *  mdestv 
) [static]

Encrypt or decrypt data stream using AES in Counter mode.

Parameters:
ctxCCMP cryptosystem context
nonceNonce value, 13 bytes
srcvData to encrypt or decrypt
lenNumber of bytes pointed to by src
msrcvMIC value to encrypt or decrypt (may be NULL)
Return values:
destvEncrypted or decrypted data
mdestvEncrypted or decrypted MIC value

This assumes CCMP parameters of L=2 and M=8. The algorithm is defined in RFC 3610.

Definition at line 187 of file wpa_ccmp.c.

References aes_algorithm, ccmp_ctx::aes_ctx, CCMP_NONCE_LEN, cipher_encrypt, dest, memcpy(), S(), and src.

Referenced by ccmp_decrypt(), and ccmp_encrypt().

{
        u8 A[16], S[16];
        u16 ctr;
        int i;
        const u8 *src = srcv, *msrc = msrcv;
        u8 *dest = destv, *mdest = mdestv;

        A[0] = 0x01;            /* flags, L' = L - 1 = 1, other bits rsvd */
        memcpy ( A + 1, nonce, CCMP_NONCE_LEN );

        if ( msrcv ) {
                A[14] = A[15] = 0;

                cipher_encrypt ( &aes_algorithm, ctx->aes_ctx, A, S, 16 );

                for ( i = 0; i < 8; i++ ) {
                        *mdest++ = *msrc++ ^ S[i];
                }
        }

        for ( ctr = 1 ;; ctr++ ) {
                A[14] = ctr >> 8;
                A[15] = ctr & 0xFF;

                cipher_encrypt ( &aes_algorithm, ctx->aes_ctx, A, S, 16 );

                for ( i = 0; i < len && i < 16; i++ )
                        *dest++ = *src++ ^ S[i];

                if ( len <= 16 )
                        break;  /* we're done */

                len -= 16;
        }
}
static void ccmp_feed_cbc_mac ( void *  aes_ctx,
u8 B,
u8 X 
) [static]

Advance one block in CBC-MAC calculation.

Parameters:
aes_ctxAES encryption context with key set
BCleartext block to incorporate (16 bytes)
XPrevious ciphertext block (16 bytes)
Return values:
BClobbered
XNew ciphertext block (16 bytes)

This function does X := E[key] ( X ^ B ).

Definition at line 238 of file wpa_ccmp.c.

References aes_algorithm, and cipher_encrypt.

Referenced by ccmp_cbc_mac().

{
        int i;
        for ( i = 0; i < 16; i++ )
                B[i] ^= X[i];
        cipher_encrypt ( &aes_algorithm, aes_ctx, B, X, 16 );
}
static void ccmp_cbc_mac ( struct ccmp_ctx ctx,
const void *  nonce,
const void *  data,
u16  datalen,
const void *  aad,
void *  mic 
) [static]

Calculate MIC on plaintext data using CBC-MAC.

Parameters:
ctxCCMP cryptosystem context
nonceNonce value, 13 bytes
dataData to calculate MIC over
datalenLength of data
aadAdditional authentication data, for MIC but not encryption
Return values:
micMIC value (unencrypted), 8 bytes

aadlen is assumed to be 22 bytes long, as it always is for 802.11 use when transmitting non-QoS, not-between-APs frames (the only type we deal with).

Definition at line 261 of file wpa_ccmp.c.

References aes_algorithm, ccmp_ctx::aes_ctx, CCMP_AAD_LEN, ccmp_feed_cbc_mac(), CCMP_NONCE_LEN, cipher_encrypt, memcpy(), and memset().

Referenced by ccmp_decrypt(), and ccmp_encrypt().

{
        u8 X[16], B[16];

        /* Zeroth block: flags, nonce, length */

        /* Rsv AAD - M'-  - L'-
         *  0   1  0 1 1  0 0 1   for an 8-byte MAC and 2-byte message length
         */
        B[0] = 0x59;
        memcpy ( B + 1, nonce, CCMP_NONCE_LEN );
        B[14] = datalen >> 8;
        B[15] = datalen & 0xFF;

        cipher_encrypt ( &aes_algorithm, ctx->aes_ctx, B, X, 16 );

        /* First block: AAD length field and 14 bytes of AAD */
        B[0] = 0;
        B[1] = CCMP_AAD_LEN;
        memcpy ( B + 2, aad, 14 );

        ccmp_feed_cbc_mac ( ctx->aes_ctx, B, X );

        /* Second block: Remaining 8 bytes of AAD, 8 bytes zero pad */
        memcpy ( B, aad + 14, 8 );
        memset ( B + 8, 0, 8 );

        ccmp_feed_cbc_mac ( ctx->aes_ctx, B, X );

        /* Message blocks */
        while ( datalen ) {
                if ( datalen >= 16 ) {
                        memcpy ( B, data, 16 );
                        datalen -= 16;
                } else {
                        memcpy ( B, data, datalen );
                        memset ( B + datalen, 0, 16 - datalen );
                        datalen = 0;
                }

                ccmp_feed_cbc_mac ( ctx->aes_ctx, B, X );

                data += 16;
        }

        /* Get MIC from final value of X */
        memcpy ( mic, X, 8 );
}
struct io_buffer* ccmp_encrypt ( struct net80211_crypto crypto,
struct io_buffer iob 
) [read]

Encapsulate and encrypt a packet using CCMP.

Parameters:
cryptoCCMP cryptosystem
iobI/O buffer containing cleartext packet
Return values:
eiobI/O buffer containing encrypted packet

Definition at line 320 of file wpa_ccmp.c.

References ccmp_head::_rsvd, ccmp_aad::a1, ccmp_nonce::a2, ieee80211_frame::addr1, ieee80211_frame::addr2, alloc_iob(), CCMP_AAD_FC_MASK, CCMP_AAD_SEQ_MASK, ccmp_cbc_mac(), ccmp_ctr_xor(), CCMP_HEAD_LEN, CCMP_MIC_LEN, ctx, io_buffer::data, datalen, DBGC2, ETH_ALEN, ccmp_aad::fc, ieee80211_frame::fc, hdr, IEEE80211_FC_PROTECTED, IEEE80211_TYP_FRAME_HEADER_LEN, iob_len(), iob_put, ccmp_head::kid, memcpy(), mic, NULL, ccmp_nonce::pn, ccmp_head::pn_hi, ccmp_head::pn_lo, PN_LSB, PN_MSB, ccmp_nonce::prio, net80211_crypto::priv, ccmp_aad::seq, ieee80211_frame::seq, ccmp_ctx::tx_seq, and u64_to_pn().

{
        struct ccmp_ctx *ctx = crypto->priv;
        struct ieee80211_frame *hdr = iob->data;
        struct io_buffer *eiob;
        const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN;
        int datalen = iob_len ( iob ) - hdrlen;
        struct ccmp_head head;
        struct ccmp_nonce nonce;
        struct ccmp_aad aad;
        u8 mic[8], tx_pn[6];
        void *edata, *emic;

        ctx->tx_seq++;
        u64_to_pn ( ctx->tx_seq, tx_pn, PN_LSB );

        /* Allocate memory */
        eiob = alloc_iob ( iob_len ( iob ) + CCMP_HEAD_LEN + CCMP_MIC_LEN );
        if ( ! eiob )
                return NULL;

        /* Copy frame header */
        memcpy ( iob_put ( eiob, hdrlen ), iob->data, hdrlen );
        hdr = eiob->data;
        hdr->fc |= IEEE80211_FC_PROTECTED;

        /* Fill in packet number and extended IV */
        memcpy ( head.pn_lo, tx_pn, 2 );
        memcpy ( head.pn_hi, tx_pn + 2, 4 );
        head.kid = 0x20;        /* have Extended IV, key ID 0 */
        head._rsvd = 0;
        memcpy ( iob_put ( eiob, sizeof ( head ) ), &head, sizeof ( head ) );

        /* Form nonce */
        nonce.prio = 0;
        memcpy ( nonce.a2, hdr->addr2, ETH_ALEN );
        u64_to_pn ( ctx->tx_seq, nonce.pn, PN_MSB );

        /* Form additional authentication data */
        aad.fc = hdr->fc & CCMP_AAD_FC_MASK;
        memcpy ( aad.a1, hdr->addr1, 3 * ETH_ALEN ); /* all 3 at once */
        aad.seq = hdr->seq & CCMP_AAD_SEQ_MASK;

        /* Calculate MIC over the data */
        ccmp_cbc_mac ( ctx, &nonce, iob->data + hdrlen, datalen, &aad, mic );

        /* Copy and encrypt data and MIC */
        edata = iob_put ( eiob, datalen );
        emic = iob_put ( eiob, CCMP_MIC_LEN );
        ccmp_ctr_xor ( ctx, &nonce,
                       iob->data + hdrlen, edata, datalen,
                       mic, emic );

        /* Done! */
        DBGC2 ( ctx, "WPA-CCMP %p: encrypted packet %p -> %p\n", ctx,
                iob, eiob );

        return eiob;
}
static struct io_buffer* ccmp_decrypt ( struct net80211_crypto crypto,
struct io_buffer eiob 
) [static, read]

Decrypt a packet using CCMP.

Parameters:
cryptoCCMP cryptosystem
eiobI/O buffer containing encrypted packet
Return values:
iobI/O buffer containing cleartext packet

Definition at line 388 of file wpa_ccmp.c.

References ccmp_aad::a1, ccmp_nonce::a2, ieee80211_frame::addr1, ieee80211_frame::addr2, alloc_iob(), CCMP_AAD_FC_MASK, CCMP_AAD_SEQ_MASK, ccmp_cbc_mac(), ccmp_ctr_xor(), CCMP_HEAD_LEN, CCMP_MIC_LEN, ctx, io_buffer::data, datalen, DBGC, DBGC2, ETH_ALEN, ccmp_aad::fc, ieee80211_frame::fc, free_iob(), hdr, head, IEEE80211_FC_PROTECTED, IEEE80211_TYP_FRAME_HEADER_LEN, iob_len(), iob_put, memcmp(), memcpy(), NULL, ccmp_nonce::pn, ccmp_head::pn_hi, ccmp_head::pn_lo, PN_MSB, pn_to_u64(), ccmp_nonce::prio, net80211_crypto::priv, ccmp_ctx::rx_seq, ccmp_aad::seq, ieee80211_frame::seq, io_buffer::tail, and u64_to_pn().

{
        struct ccmp_ctx *ctx = crypto->priv;
        struct ieee80211_frame *hdr;
        struct io_buffer *iob;
        const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN;
        int datalen = iob_len ( eiob ) - hdrlen - CCMP_HEAD_LEN - CCMP_MIC_LEN;
        struct ccmp_head *head;
        struct ccmp_nonce nonce;
        struct ccmp_aad aad;
        u8 rx_pn[6], their_mic[8], our_mic[8];

        iob = alloc_iob ( hdrlen + datalen );
        if ( ! iob )
                return NULL;

        /* Copy frame header */
        memcpy ( iob_put ( iob, hdrlen ), eiob->data, hdrlen );
        hdr = iob->data;
        hdr->fc &= ~IEEE80211_FC_PROTECTED;

        /* Check and update RX packet number */
        head = eiob->data + hdrlen;
        memcpy ( rx_pn, head->pn_lo, 2 );
        memcpy ( rx_pn + 2, head->pn_hi, 4 );

        if ( pn_to_u64 ( rx_pn ) <= ctx->rx_seq ) {
                DBGC ( ctx, "WPA-CCMP %p: packet received out of order "
                       "(%012llx <= %012llx)\n", ctx, pn_to_u64 ( rx_pn ),
                       ctx->rx_seq );
                free_iob ( iob );
                return NULL;
        }

        ctx->rx_seq = pn_to_u64 ( rx_pn );
        DBGC2 ( ctx, "WPA-CCMP %p: RX packet number %012llx\n", ctx, ctx->rx_seq );

        /* Form nonce */
        nonce.prio = 0;
        memcpy ( nonce.a2, hdr->addr2, ETH_ALEN );
        u64_to_pn ( ctx->rx_seq, nonce.pn, PN_MSB );

        /* Form additional authentication data */
        aad.fc = ( hdr->fc & CCMP_AAD_FC_MASK ) | IEEE80211_FC_PROTECTED;
        memcpy ( aad.a1, hdr->addr1, 3 * ETH_ALEN ); /* all 3 at once */
        aad.seq = hdr->seq & CCMP_AAD_SEQ_MASK;

        /* Copy-decrypt data and MIC */
        ccmp_ctr_xor ( ctx, &nonce, eiob->data + hdrlen + sizeof ( *head ),
                       iob_put ( iob, datalen ), datalen,
                       eiob->tail - CCMP_MIC_LEN, their_mic );

        /* Check MIC */
        ccmp_cbc_mac ( ctx, &nonce, iob->data + hdrlen, datalen, &aad,
                       our_mic );

        if ( memcmp ( their_mic, our_mic, CCMP_MIC_LEN ) != 0 ) {
                DBGC2 ( ctx, "WPA-CCMP %p: MIC failure\n", ctx );
                free_iob ( iob );
                return NULL;
        }

        DBGC2 ( ctx, "WPA-CCMP %p: decrypted packet %p -> %p\n", ctx,
                eiob, iob );

        return iob;
}
static void ccmp_kie_mic ( const void *  kck,
const void *  msg,
size_t  len,
void *  mic 
) [static]

Calculate HMAC-SHA1 MIC for EAPOL-Key frame.

Parameters:
kckKey Confirmation Key, 16 bytes
msgMessage to calculate MIC over
lenNumber of bytes to calculate MIC over
Return values:
micCalculated MIC, 16 bytes long

Definition at line 478 of file wpa_ccmp.c.

References hash, hmac_final(), hmac_init(), hmac_update(), memcpy(), sha1_algorithm, SHA1_CTX_SIZE, and SHA1_DIGEST_SIZE.

{
        u8 sha1_ctx[SHA1_CTX_SIZE];
        u8 kckb[16];
        u8 hash[SHA1_DIGEST_SIZE];
        size_t kck_len = 16;

        memcpy ( kckb, kck, kck_len );

        hmac_init ( &sha1_algorithm, sha1_ctx, kckb, &kck_len );
        hmac_update ( &sha1_algorithm, sha1_ctx, msg, len );
        hmac_final ( &sha1_algorithm, sha1_ctx, kckb, &kck_len, hash );

        memcpy ( mic, hash, 16 );
}
static int ccmp_kie_decrypt ( const void *  kek,
const void *iv  __unused,
void *  msg,
u16 len 
) [static]

Decrypt key data in EAPOL-Key frame.

Parameters:
kekKey Encryption Key, 16 bytes
ivInitialisation vector, 16 bytes (unused)
msgMessage to decrypt
lenLength of message
Return values:
msgDecrypted message in place of original
lenAdjusted downward for 8 bytes of overhead
rcReturn status code

The returned message may still contain padding of 0xDD followed by zero or more 0x00 octets. It is impossible to remove the padding without parsing the IEs in the packet (another design decision that tends to make one question the 802.11i committee's intelligence...)

Definition at line 511 of file wpa_ccmp.c.

References aes_unwrap(), and EINVAL.

{
        if ( *len % 8 != 0 )
                return -EINVAL;

        if ( aes_unwrap ( kek, msg, msg, *len / 8 - 1 ) != 0 )
                return -EINVAL;

        *len -= 8;

        return 0;
}

Variable Documentation

struct net80211_crypto ccmp_crypto __net80211_crypto
Initial value:
 {
        .algorithm = NET80211_CRYPT_CCMP,
        .init = ccmp_init,
        .encrypt = ccmp_encrypt,
        .decrypt = ccmp_decrypt,
        .priv_len = sizeof ( struct ccmp_ctx ),
}

CCMP cryptosystem.

Definition at line 459 of file wpa_ccmp.c.

struct wpa_kie ccmp_kie __wpa_kie
Initial value:
 {
        .version = EAPOL_KEY_VERSION_WPA2,
        .mic = ccmp_kie_mic,
        .decrypt = ccmp_kie_decrypt,
}

CCMP-style key integrity and encryption handler.

Definition at line 526 of file wpa_ccmp.c.