INT AirPDcapCcmpDecrypt(
	UINT8 *m,
        gint mac_header_len,
	INT len,
	UCHAR TK1[16])
{
	PAIRPDCAP_MAC_FRAME wh;
	UINT8 aad[2 * AES_BLOCK_LEN];
	UINT8 b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN];
	UINT8 mic[AES_BLOCK_LEN];
	size_t data_len;
	UINT i;
	UINT8 *pos;
	UINT space;
	INT z = mac_header_len;
	rijndael_ctx key;
	UINT64 PN;
	UINT8 *ivp=m+z;

	PN = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);

	/* freebsd	*/
	rijndael_set_key(&key, TK1, 128);
	wh = (PAIRPDCAP_MAC_FRAME )m;
	data_len = len - (z + AIRPDCAP_CCMP_HEADER+AIRPDCAP_CCMP_TRAILER);
	if (data_len < 1)
	    return 0;
	ccmp_init_blocks(&key, wh, PN, data_len, b0, aad, a, b);
	memcpy(mic, m+len-AIRPDCAP_CCMP_TRAILER, AIRPDCAP_CCMP_TRAILER);
	XOR_BLOCK(mic, b, AIRPDCAP_CCMP_TRAILER);

	i = 1;
	pos = (UINT8 *)m + z + AIRPDCAP_CCMP_HEADER;
	space = len - (z + AIRPDCAP_CCMP_HEADER);

	if (space > data_len)
		space = (UINT)data_len;
	while (space >= AES_BLOCK_LEN) {
		CCMP_DECRYPT(i, b, b0, pos, a, AES_BLOCK_LEN);
		pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN;
		data_len -= AES_BLOCK_LEN;
		i++;
	}

	if (space != 0)         /* short last block */
		CCMP_DECRYPT(i, b, b0, pos, a, space);

	/*	MIC Key ?= MIC																				*/
	if (memcmp(mic, a, AIRPDCAP_CCMP_TRAILER) == 0) {
		return 0;
	}

	/* TODO replay check	(IEEE 802.11i-2004, pg. 62)			*/
	/* TODO PN must be incremental (IEEE 802.11i-2004, pg. 62)		*/

	return 1;
}
static void ccmp_init_blocks(
	rijndael_ctx *ctx,
        PAIRPDCAP_MAC_FRAME wh,
	UINT64 pn,
	size_t dlen,
	UINT8 b0[AES_BLOCK_LEN],
	UINT8 aad[2 * AES_BLOCK_LEN],
	UINT8 a[AES_BLOCK_LEN],
	UINT8 b[AES_BLOCK_LEN])
{
#define IS_4ADDRESS(wh) \
	((wh->fc[1] & AIRPDCAP_FC1_DIR_MASK) == AIRPDCAP_FC1_DIR_DSTODS)
#define IS_QOS_DATA(wh) AIRPDCAP_QOS_HAS_SEQ(wh)

	memset(aad, 0, 2*AES_BLOCK_LEN);

	/* CCM Initial Block:
	* Flag (Include authentication header, M=3 (8-octet MIC),
	*       L=1 (2-octet Dlen))
	* Nonce: 0x00 | A2 | PN
	* Dlen */
	b0[0] = 0x59;
	/* NB: b0[1] set below */
	AIRPDCAP_ADDR_COPY(b0 + 2, wh->addr2);
	b0[8] = (UINT8)(pn >> 40);
	b0[9] = (UINT8)(pn >> 32);
	b0[10] = (UINT8)(pn >> 24);
	b0[11] = (UINT8)(pn >> 16);
	b0[12] = (UINT8)(pn >> 8);
	b0[13] = (UINT8)(pn >> 0);
	b0[14] = (UINT8)((UINT8)(dlen >> 8) & 0xff);
	b0[15] = (UINT8)(dlen & 0xff);

	/* AAD:
	* FC with bits 4..6 and 11..13 masked to zero; 14 is always one
	* A1 | A2 | A3
	* SC with bits 4..15 (seq#) masked to zero
	* A4 (if present)
	* QC (if present)
	*/
	aad[0] = 0;     /* AAD length >> 8 */
	/* NB: aad[1] set below */
	aad[2] = (UINT8)(wh->fc[0] & 0x8f);    /* XXX magic #s */
	aad[3] = (UINT8)(wh->fc[1] & 0xc7);    /* XXX magic #s */
	/* NB: we know 3 addresses are contiguous */
	memcpy(aad + 4, &wh->addr1[0], 3 * AIRPDCAP_MAC_LEN);
	aad[22] = (UINT8)(wh->seq[0] & AIRPDCAP_SEQ_FRAG_MASK);
	aad[23] = 0; /* all bits masked */
	/*
	* Construct variable-length portion of AAD based
	* on whether this is a 4-address frame/QOS frame.
	* We always zero-pad to 32 bytes before running it
	* through the cipher.
	*
	* We also fill in the priority bits of the CCM
	* initial block as we know whether or not we have
	* a QOS frame.
	*/
	if (IS_4ADDRESS(wh)) {
		AIRPDCAP_ADDR_COPY(aad + 24,
			((PAIRPDCAP_MAC_FRAME_ADDR4)wh)->addr4);
		if (IS_QOS_DATA(wh)) {
			PAIRPDCAP_MAC_FRAME_ADDR4_QOS qwh4 =
				(PAIRPDCAP_MAC_FRAME_ADDR4_QOS) wh;
			aad[30] = (UINT8)(qwh4->qos[0] & 0x0f);/* just priority bits */
			aad[31] = 0;
			b0[1] = aad[30];
			aad[1] = 22 + AIRPDCAP_MAC_LEN + 2;
		} else {
			memset(&aad[30], 0, 2);
			b0[1] = 0;
			aad[1] = 22 + AIRPDCAP_MAC_LEN;
		}
	} else {
		if (IS_QOS_DATA(wh)) {
			PAIRPDCAP_MAC_FRAME_QOS qwh =
				(PAIRPDCAP_MAC_FRAME_QOS) wh;
			aad[24] = (UINT8)(qwh->qos[0] & 0x0f); /* just priority bits */
			aad[25] = 0;
			b0[1] = aad[24];
			aad[1] = 22 + 2;
		} else {
			memset(&aad[24], 0, 2);
			b0[1] = 0;
			aad[1] = 22;
		}
		memset(&aad[26], 0, 4);
	}

	/* Start with the first block and AAD */
	rijndael_encrypt(ctx, b0, a);
	XOR_BLOCK(a, aad, AES_BLOCK_LEN);
	rijndael_encrypt(ctx, a, a);
	XOR_BLOCK(a, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
	rijndael_encrypt(ctx, a, a);
	b0[0] &= 0x07;
	b0[14] = b0[15] = 0;
	rijndael_encrypt(ctx, b0, b);

	/** //XOR( m + len - 8, b, 8 ); **/
#undef  IS_QOS_DATA
#undef  IS_4ADDRESS
}
Beispiel #3
0
/*
 * See addemdum to NIST SP 800-38A
 * Decrypt, Expect CS-1: input. See the comment on the encrypt side
 * to understand what CS-2 and CS-3 mean.
 *
 * To convert the input buffer to CS-1 from ...
 *   CS-2 (Schneier): do
 *       unsigned char tmp[MAX_BLOCK_SIZE];
 *       pad = inlen % blocksize;
 *       if (pad) {
 *          memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize);
 *          memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad);
 *      memcpy(inbuf+inlen-blocksize, tmp, blocksize);
 *       }
 *   CS-3 (Kerberos): do
 *       unsigned char tmp[MAX_BLOCK_SIZE];
 *       pad = inlen % blocksize;
 *       if (pad == 0) {
 *           pad = blocksize;
 *       }
 *       memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize);
 *       memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad);
 *   memcpy(inbuf+inlen-blocksize, tmp, blocksize);
 */
SECStatus
CTS_DecryptUpdate(CTSContext *cts, unsigned char *outbuf,
                  unsigned int *outlen, unsigned int maxout,
                  const unsigned char *inbuf, unsigned int inlen,
                  unsigned int blocksize)
{
    unsigned char *Pn;
    unsigned char Cn_2[MAX_BLOCK_SIZE]; /* block Cn-2 */
    unsigned char Cn_1[MAX_BLOCK_SIZE]; /* block Cn-1 */
    unsigned char Cn[MAX_BLOCK_SIZE];   /* block Cn   */
    unsigned char lastBlock[MAX_BLOCK_SIZE];
    const unsigned char *tmp;
    unsigned char *saveout = outbuf;
    unsigned int tmpLen;
    unsigned int fullblocks, pad;
    unsigned int i;
    SECStatus rv;

    if (inlen < blocksize) {
        PORT_SetError(SEC_ERROR_INPUT_LEN);
        return SECFailure;
    }

    if (maxout < inlen) {
        *outlen = inlen;
        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
        return SECFailure;
    }

    fullblocks = (inlen / blocksize) * blocksize;

    /* even though we expect the input to be CS-1, CS-2 is easier to parse,
     * so convert to CS-2 immediately. NOTE: this is the same code as in
     * the comment for encrypt. NOTE2: since we can't modify inbuf unless
     * inbuf and outbuf overlap, just copy inbuf to outbuf and modify it there
     */
    pad = inlen - fullblocks;
    if (pad != 0) {
        if (inbuf != outbuf) {
            memcpy(outbuf, inbuf, inlen);
            /* keep the names so we logically know how we are using the
         * buffers */
            inbuf = outbuf;
        }
        memcpy(lastBlock, inbuf + inlen - blocksize, blocksize);
        /* we know inbuf == outbuf now, inbuf is declared const and can't
     * be the target, so use outbuf for the target here */
        memcpy(outbuf + inlen - pad, inbuf + inlen - blocksize - pad, pad);
        memcpy(outbuf + inlen - blocksize - pad, lastBlock, blocksize);
    }
    /* save the previous to last block so we can undo the misordered
     * chaining */
    tmp = (fullblocks < blocksize * 2) ? cts->iv : inbuf + fullblocks - blocksize * 2;
    PORT_Memcpy(Cn_2, tmp, blocksize);
    PORT_Memcpy(Cn, inbuf + fullblocks - blocksize, blocksize);
    rv = (*cts->cipher)(cts->context, outbuf, outlen, maxout, inbuf,
                        fullblocks, blocksize);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    *outlen = fullblocks; /* AES low level doesn't set outlen */
    inbuf += fullblocks;
    inlen -= fullblocks;
    if (inlen == 0) {
        return SECSuccess;
    }
    outbuf += fullblocks;

    /* recover the stolen text */
    PORT_Memset(lastBlock, 0, blocksize);
    PORT_Memcpy(lastBlock, inbuf, inlen);
    PORT_Memcpy(Cn_1, inbuf, inlen);
    Pn = outbuf - blocksize;
    /* inbuf points to Cn-1* in the input buffer */
    /* NOTE: below there are 2 sections marked "make up for the out of order
     * cbc decryption". You may ask, what is going on here.
     *   Short answer: CBC automatically xors the plain text with the previous
     * encrypted block. We are decrypting the last 2 blocks out of order, so
     * we have to 'back out' the decrypt xor and 'add back' the encrypt xor.
     *   Long answer: When we encrypted, we encrypted as follows:
     *       Pn-2, Pn-1, (Pn || 0), but on decryption we can't
     *  decrypt Cn-1 until we decrypt Cn because part of Cn-1 is stored in
     *  Cn (see below).  So above we decrypted all the full blocks:
     *       Cn-2, Cn,
     *  to get:
     *       Pn-2, Pn, Except that Pn is not yet corect. On encrypt, we
     *  xor'd Pn || 0  with Cn-1, but on decrypt we xor'd it with Cn-2
     *  To recover Pn, we xor the block with Cn-1* || 0 (in last block) and
     *  Cn-2 to get Pn || Cn-1**. Pn can then be written to the output buffer
     *  and we can now reunite Cn-1. With the full Cn-1 we can decrypt it,
     *  but now decrypt is going to xor the decrypted data with Cn instead of
     *  Cn-2. xoring Cn and Cn-2 restores the original Pn-1 and we can now
     *  write that oout to the buffer */

    /* make up for the out of order CBC decryption */
    XOR_BLOCK(lastBlock, Cn_2, blocksize);
    XOR_BLOCK(lastBlock, Pn, blocksize);
    /* last buf now has Pn || Cn-1**, copy out Pn */
    PORT_Memcpy(outbuf, lastBlock, inlen);
    *outlen += inlen;
    /* copy Cn-1* into last buf to recover Cn-1 */
    PORT_Memcpy(lastBlock, Cn_1, inlen);
    /* note: because Cn and Cn-1 were out of order, our pointer to Pn also
     * points to where Pn-1 needs to reside. From here on out read Pn in
     * the code as really Pn-1. */
    rv = (*cts->cipher)(cts->context, Pn, &tmpLen, blocksize, lastBlock,
                        blocksize, blocksize);
    if (rv != SECSuccess) {
        PORT_Memset(lastBlock, 0, blocksize);
        PORT_Memset(saveout, 0, *outlen);
        return SECFailure;
    }
    /* make up for the out of order CBC decryption */
    XOR_BLOCK(Pn, Cn_2, blocksize);
    XOR_BLOCK(Pn, Cn, blocksize);
    /* reset iv to Cn  */
    PORT_Memcpy(cts->iv, Cn, blocksize);
    /* This makes Cn the last block for the next decrypt operation, which
     * matches the encrypt. We don't care about the contexts of last block,
     * only the side effect of setting the internal IV */
    (void)(*cts->cipher)(cts->context, lastBlock, &tmpLen, blocksize, Cn,
                         blocksize, blocksize);
    /* clear last block. At this point last block contains Pn xor Cn_1 xor
     * Cn_2, both of with an attacker would know, so we need to clear this
     * buffer out */
    PORT_Memset(lastBlock, 0, blocksize);
    /* Cn, Cn_1, and Cn_2 have encrypted data, so no need to clear them */
    return SECSuccess;
}