mac80211: port CCMP to cryptoapi's CCM driver
Use the generic CCM aead chaining mode driver rather than a local implementation that sits right on top of the core AES cipher. This allows the use of accelerated implementations of either CCM as a whole or the CTR mode which it encapsulates. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
		
					parent
					
						
							
								fa1fb9cb1c
							
						
					
				
			
			
				commit
				
					
						7ec7c4a9a6
					
				
			
		
					 5 changed files with 80 additions and 142 deletions
				
			
		|  | @ -4,6 +4,7 @@ config MAC80211 | ||||||
| 	select CRYPTO | 	select CRYPTO | ||||||
| 	select CRYPTO_ARC4 | 	select CRYPTO_ARC4 | ||||||
| 	select CRYPTO_AES | 	select CRYPTO_AES | ||||||
|  | 	select CRYPTO_CCM | ||||||
| 	select CRC32 | 	select CRC32 | ||||||
| 	select AVERAGE | 	select AVERAGE | ||||||
| 	---help--- | 	---help--- | ||||||
|  |  | ||||||
|  | @ -2,6 +2,8 @@ | ||||||
|  * Copyright 2003-2004, Instant802 Networks, Inc. |  * Copyright 2003-2004, Instant802 Networks, Inc. | ||||||
|  * Copyright 2005-2006, Devicescape Software, Inc. |  * Copyright 2005-2006, Devicescape Software, Inc. | ||||||
|  * |  * | ||||||
|  |  * Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org> | ||||||
|  |  * | ||||||
|  * This program is free software; you can redistribute it and/or modify |  * This program is free software; you can redistribute it and/or modify | ||||||
|  * it under the terms of the GNU General Public License version 2 as |  * it under the terms of the GNU General Public License version 2 as | ||||||
|  * published by the Free Software Foundation. |  * published by the Free Software Foundation. | ||||||
|  | @ -17,134 +19,75 @@ | ||||||
| #include "key.h" | #include "key.h" | ||||||
| #include "aes_ccm.h" | #include "aes_ccm.h" | ||||||
| 
 | 
 | ||||||
| static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a) | void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||||
|  | 			       u8 *data, size_t data_len, u8 *mic) | ||||||
| { | { | ||||||
| 	int i; | 	struct scatterlist assoc, pt, ct[2]; | ||||||
| 	u8 *b_0, *aad, *b, *s_0; | 	struct { | ||||||
|  | 		struct aead_request	req; | ||||||
|  | 		u8			priv[crypto_aead_reqsize(tfm)]; | ||||||
|  | 	} aead_req; | ||||||
| 
 | 
 | ||||||
| 	b_0 = scratch + 3 * AES_BLOCK_SIZE; | 	memset(&aead_req, 0, sizeof(aead_req)); | ||||||
| 	aad = scratch + 4 * AES_BLOCK_SIZE; |  | ||||||
| 	b = scratch; |  | ||||||
| 	s_0 = scratch + AES_BLOCK_SIZE; |  | ||||||
| 
 | 
 | ||||||
| 	crypto_cipher_encrypt_one(tfm, b, b_0); | 	sg_init_one(&pt, data, data_len); | ||||||
|  | 	sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad)); | ||||||
|  | 	sg_init_table(ct, 2); | ||||||
|  | 	sg_set_buf(&ct[0], data, data_len); | ||||||
|  | 	sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN); | ||||||
| 
 | 
 | ||||||
| 	/* Extra Authenticate-only data (always two AES blocks) */ | 	aead_request_set_tfm(&aead_req.req, tfm); | ||||||
| 	for (i = 0; i < AES_BLOCK_SIZE; i++) | 	aead_request_set_assoc(&aead_req.req, &assoc, assoc.length); | ||||||
| 		aad[i] ^= b[i]; | 	aead_request_set_crypt(&aead_req.req, &pt, ct, data_len, b_0); | ||||||
| 	crypto_cipher_encrypt_one(tfm, b, aad); |  | ||||||
| 
 | 
 | ||||||
| 	aad += AES_BLOCK_SIZE; | 	crypto_aead_encrypt(&aead_req.req); | ||||||
| 
 |  | ||||||
| 	for (i = 0; i < AES_BLOCK_SIZE; i++) |  | ||||||
| 		aad[i] ^= b[i]; |  | ||||||
| 	crypto_cipher_encrypt_one(tfm, a, aad); |  | ||||||
| 
 |  | ||||||
| 	/* Mask out bits from auth-only-b_0 */ |  | ||||||
| 	b_0[0] &= 0x07; |  | ||||||
| 
 |  | ||||||
| 	/* S_0 is used to encrypt T (= MIC) */ |  | ||||||
| 	b_0[14] = 0; |  | ||||||
| 	b_0[15] = 0; |  | ||||||
| 	crypto_cipher_encrypt_one(tfm, s_0, b_0); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||||
| void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, | 			      u8 *data, size_t data_len, u8 *mic) | ||||||
| 			       u8 *data, size_t data_len, |  | ||||||
| 			       u8 *cdata, u8 *mic) |  | ||||||
| { | { | ||||||
| 	int i, j, last_len, num_blocks; | 	struct scatterlist assoc, pt, ct[2]; | ||||||
| 	u8 *pos, *cpos, *b, *s_0, *e, *b_0; | 	struct { | ||||||
|  | 		struct aead_request	req; | ||||||
|  | 		u8			priv[crypto_aead_reqsize(tfm)]; | ||||||
|  | 	} aead_req; | ||||||
| 
 | 
 | ||||||
| 	b = scratch; | 	memset(&aead_req, 0, sizeof(aead_req)); | ||||||
| 	s_0 = scratch + AES_BLOCK_SIZE; |  | ||||||
| 	e = scratch + 2 * AES_BLOCK_SIZE; |  | ||||||
| 	b_0 = scratch + 3 * AES_BLOCK_SIZE; |  | ||||||
| 
 | 
 | ||||||
| 	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); | 	sg_init_one(&pt, data, data_len); | ||||||
| 	last_len = data_len % AES_BLOCK_SIZE; | 	sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad)); | ||||||
| 	aes_ccm_prepare(tfm, scratch, b); | 	sg_init_table(ct, 2); | ||||||
|  | 	sg_set_buf(&ct[0], data, data_len); | ||||||
|  | 	sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN); | ||||||
| 
 | 
 | ||||||
| 	/* Process payload blocks */ | 	aead_request_set_tfm(&aead_req.req, tfm); | ||||||
| 	pos = data; | 	aead_request_set_assoc(&aead_req.req, &assoc, assoc.length); | ||||||
| 	cpos = cdata; | 	aead_request_set_crypt(&aead_req.req, ct, &pt, | ||||||
| 	for (j = 1; j <= num_blocks; j++) { | 			       data_len + IEEE80211_CCMP_MIC_LEN, b_0); | ||||||
| 		int blen = (j == num_blocks && last_len) ? |  | ||||||
| 			last_len : AES_BLOCK_SIZE; |  | ||||||
| 
 | 
 | ||||||
| 		/* Authentication followed by encryption */ | 	return crypto_aead_decrypt(&aead_req.req); | ||||||
| 		for (i = 0; i < blen; i++) |  | ||||||
| 			b[i] ^= pos[i]; |  | ||||||
| 		crypto_cipher_encrypt_one(tfm, b, b); |  | ||||||
| 
 |  | ||||||
| 		b_0[14] = (j >> 8) & 0xff; |  | ||||||
| 		b_0[15] = j & 0xff; |  | ||||||
| 		crypto_cipher_encrypt_one(tfm, e, b_0); |  | ||||||
| 		for (i = 0; i < blen; i++) |  | ||||||
| 			*cpos++ = *pos++ ^ e[i]; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) |  | ||||||
| 		mic[i] = b[i] ^ s_0[i]; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]) | ||||||
| int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, |  | ||||||
| 			      u8 *cdata, size_t data_len, u8 *mic, u8 *data) |  | ||||||
| { | { | ||||||
| 	int i, j, last_len, num_blocks; | 	struct crypto_aead *tfm; | ||||||
| 	u8 *pos, *cpos, *b, *s_0, *a, *b_0; | 	int err; | ||||||
| 
 |  | ||||||
| 	b = scratch; |  | ||||||
| 	s_0 = scratch + AES_BLOCK_SIZE; |  | ||||||
| 	a = scratch + 2 * AES_BLOCK_SIZE; |  | ||||||
| 	b_0 = scratch + 3 * AES_BLOCK_SIZE; |  | ||||||
| 
 |  | ||||||
| 	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); |  | ||||||
| 	last_len = data_len % AES_BLOCK_SIZE; |  | ||||||
| 	aes_ccm_prepare(tfm, scratch, a); |  | ||||||
| 
 |  | ||||||
| 	/* Process payload blocks */ |  | ||||||
| 	cpos = cdata; |  | ||||||
| 	pos = data; |  | ||||||
| 	for (j = 1; j <= num_blocks; j++) { |  | ||||||
| 		int blen = (j == num_blocks && last_len) ? |  | ||||||
| 			last_len : AES_BLOCK_SIZE; |  | ||||||
| 
 |  | ||||||
| 		/* Decryption followed by authentication */ |  | ||||||
| 		b_0[14] = (j >> 8) & 0xff; |  | ||||||
| 		b_0[15] = j & 0xff; |  | ||||||
| 		crypto_cipher_encrypt_one(tfm, b, b_0); |  | ||||||
| 		for (i = 0; i < blen; i++) { |  | ||||||
| 			*pos = *cpos++ ^ b[i]; |  | ||||||
| 			a[i] ^= *pos++; |  | ||||||
| 		} |  | ||||||
| 		crypto_cipher_encrypt_one(tfm, a, a); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) { |  | ||||||
| 		if ((mic[i] ^ s_0[i]) != a[i]) |  | ||||||
| 			return -1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]) |  | ||||||
| { |  | ||||||
| 	struct crypto_cipher *tfm; |  | ||||||
| 
 |  | ||||||
| 	tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); |  | ||||||
| 	if (!IS_ERR(tfm)) |  | ||||||
| 		crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP); |  | ||||||
| 
 | 
 | ||||||
|  | 	tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC); | ||||||
|  | 	if (IS_ERR(tfm)) | ||||||
| 		return tfm; | 		return tfm; | ||||||
|  | 
 | ||||||
|  | 	err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP); | ||||||
|  | 	if (!err) | ||||||
|  | 		err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN); | ||||||
|  | 	if (!err) | ||||||
|  | 		return tfm; | ||||||
|  | 
 | ||||||
|  | 	crypto_free_aead(tfm); | ||||||
|  | 	return ERR_PTR(err); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | void ieee80211_aes_key_free(struct crypto_aead *tfm) | ||||||
| void ieee80211_aes_key_free(struct crypto_cipher *tfm) |  | ||||||
| { | { | ||||||
| 	crypto_free_cipher(tfm); | 	crypto_free_aead(tfm); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,13 +12,11 @@ | ||||||
| 
 | 
 | ||||||
| #include <linux/crypto.h> | #include <linux/crypto.h> | ||||||
| 
 | 
 | ||||||
| struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]); | struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]); | ||||||
| void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, | void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||||
| 			       u8 *data, size_t data_len, | 			       u8 *data, size_t data_len, u8 *mic); | ||||||
| 			       u8 *cdata, u8 *mic); | int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||||
| int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, | 			      u8 *data, size_t data_len, u8 *mic); | ||||||
| 			      u8 *cdata, size_t data_len, | void ieee80211_aes_key_free(struct crypto_aead *tfm); | ||||||
| 			      u8 *mic, u8 *data); |  | ||||||
| void ieee80211_aes_key_free(struct crypto_cipher *tfm); |  | ||||||
| 
 | 
 | ||||||
| #endif /* AES_CCM_H */ | #endif /* AES_CCM_H */ | ||||||
|  |  | ||||||
|  | @ -83,7 +83,7 @@ struct ieee80211_key { | ||||||
| 			 * Management frames. | 			 * Management frames. | ||||||
| 			 */ | 			 */ | ||||||
| 			u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; | 			u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; | ||||||
| 			struct crypto_cipher *tfm; | 			struct crypto_aead *tfm; | ||||||
| 			u32 replays; /* dot11RSNAStatsCCMPReplays */ | 			u32 replays; /* dot11RSNAStatsCCMPReplays */ | ||||||
| 		} ccmp; | 		} ccmp; | ||||||
| 		struct { | 		struct { | ||||||
|  |  | ||||||
|  | @ -301,22 +301,16 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, | static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad, | ||||||
| 				int encrypted) | 				int encrypted) | ||||||
| { | { | ||||||
| 	__le16 mask_fc; | 	__le16 mask_fc; | ||||||
| 	int a4_included, mgmt; | 	int a4_included, mgmt; | ||||||
| 	u8 qos_tid; | 	u8 qos_tid; | ||||||
| 	u8 *b_0, *aad; | 	u16 len_a; | ||||||
| 	u16 data_len, len_a; |  | ||||||
| 	unsigned int hdrlen; | 	unsigned int hdrlen; | ||||||
| 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | ||||||
| 
 | 
 | ||||||
| 	memset(scratch, 0, 6 * AES_BLOCK_SIZE); |  | ||||||
| 
 |  | ||||||
| 	b_0 = scratch + 3 * AES_BLOCK_SIZE; |  | ||||||
| 	aad = scratch + 4 * AES_BLOCK_SIZE; |  | ||||||
| 
 |  | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Mask FC: zero subtype b4 b5 b6 (if not mgmt) | 	 * Mask FC: zero subtype b4 b5 b6 (if not mgmt) | ||||||
| 	 * Retry, PwrMgt, MoreData; set Protected | 	 * Retry, PwrMgt, MoreData; set Protected | ||||||
|  | @ -338,20 +332,21 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, | ||||||
| 	else | 	else | ||||||
| 		qos_tid = 0; | 		qos_tid = 0; | ||||||
| 
 | 
 | ||||||
| 	data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN; | 	/* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
 | ||||||
| 	if (encrypted) | 	 * mode authentication are not allowed to collide, yet both are derived | ||||||
| 		data_len -= IEEE80211_CCMP_MIC_LEN; | 	 * from this vector b_0. We only set L := 1 here to indicate that the | ||||||
|  | 	 * data size can be represented in (L+1) bytes. The CCM layer will take | ||||||
|  | 	 * care of storing the data length in the top (L+1) bytes and setting | ||||||
|  | 	 * and clearing the other bits as is required to derive the two IVs. | ||||||
|  | 	 */ | ||||||
|  | 	b_0[0] = 0x1; | ||||||
| 
 | 
 | ||||||
| 	/* First block, b_0 */ |  | ||||||
| 	b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */ |  | ||||||
| 	/* Nonce: Nonce Flags | A2 | PN
 | 	/* Nonce: Nonce Flags | A2 | PN
 | ||||||
| 	 * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7) | 	 * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7) | ||||||
| 	 */ | 	 */ | ||||||
| 	b_0[1] = qos_tid | (mgmt << 4); | 	b_0[1] = qos_tid | (mgmt << 4); | ||||||
| 	memcpy(&b_0[2], hdr->addr2, ETH_ALEN); | 	memcpy(&b_0[2], hdr->addr2, ETH_ALEN); | ||||||
| 	memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN); | 	memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN); | ||||||
| 	/* l(m) */ |  | ||||||
| 	put_unaligned_be16(data_len, &b_0[14]); |  | ||||||
| 
 | 
 | ||||||
| 	/* AAD (extra authenticate-only data) / masked 802.11 header
 | 	/* AAD (extra authenticate-only data) / masked 802.11 header
 | ||||||
| 	 * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ | 	 * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ | ||||||
|  | @ -407,7 +402,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) | ||||||
| 	u8 *pos; | 	u8 *pos; | ||||||
| 	u8 pn[6]; | 	u8 pn[6]; | ||||||
| 	u64 pn64; | 	u64 pn64; | ||||||
| 	u8 scratch[6 * AES_BLOCK_SIZE]; | 	u8 aad[2 * AES_BLOCK_SIZE]; | ||||||
|  | 	u8 b_0[AES_BLOCK_SIZE]; | ||||||
| 
 | 
 | ||||||
| 	if (info->control.hw_key && | 	if (info->control.hw_key && | ||||||
| 	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && | 	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && | ||||||
|  | @ -460,9 +456,9 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	pos += IEEE80211_CCMP_HDR_LEN; | 	pos += IEEE80211_CCMP_HDR_LEN; | ||||||
| 	ccmp_special_blocks(skb, pn, scratch, 0); | 	ccmp_special_blocks(skb, pn, b_0, aad, 0); | ||||||
| 	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len, | 	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, | ||||||
| 				  pos, skb_put(skb, IEEE80211_CCMP_MIC_LEN)); | 				  skb_put(skb, IEEE80211_CCMP_MIC_LEN)); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  | @ -525,16 +521,16 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (!(status->flag & RX_FLAG_DECRYPTED)) { | 	if (!(status->flag & RX_FLAG_DECRYPTED)) { | ||||||
| 		u8 scratch[6 * AES_BLOCK_SIZE]; | 		u8 aad[2 * AES_BLOCK_SIZE]; | ||||||
|  | 		u8 b_0[AES_BLOCK_SIZE]; | ||||||
| 		/* hardware didn't decrypt/verify MIC */ | 		/* hardware didn't decrypt/verify MIC */ | ||||||
| 		ccmp_special_blocks(skb, pn, scratch, 1); | 		ccmp_special_blocks(skb, pn, b_0, aad, 1); | ||||||
| 
 | 
 | ||||||
| 		if (ieee80211_aes_ccm_decrypt( | 		if (ieee80211_aes_ccm_decrypt( | ||||||
| 			    key->u.ccmp.tfm, scratch, | 			    key->u.ccmp.tfm, b_0, aad, | ||||||
| 			    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN, | 			    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN, | ||||||
| 			    data_len, | 			    data_len, | ||||||
| 			    skb->data + skb->len - IEEE80211_CCMP_MIC_LEN, | 			    skb->data + skb->len - IEEE80211_CCMP_MIC_LEN)) | ||||||
| 			    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN)) |  | ||||||
| 			return RX_DROP_UNUSABLE; | 			return RX_DROP_UNUSABLE; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Ard Biesheuvel
				Ard Biesheuvel