Commit a11e2f85 authored by Ard Biesheuvel's avatar Ard Biesheuvel Committed by Johannes Berg

lib80211: use crypto API ccm(aes) transform for CCMP processing

Instead of open coding the CCM aead mode in the driver, and invoking
the AES block cipher block by block, use a ccm(aes) aead transform
which already encapsulates this functionality. This is a cleaner use
of the crypto API, and permits optimized implementations to be used,
which are typically much faster and deal more efficiently with the
SIMD register file, which usually needs to be preserved/restored in
order to use special AES instructions.
Signed-off-by: default avatarArd Biesheuvel <ard.biesheuvel@linaro.org>
Link: https://lore.kernel.org/r/20190617091901.7063-1-ard.biesheuvel@linaro.orgSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 612fcfd9
...@@ -217,6 +217,8 @@ config LIB80211_CRYPT_WEP ...@@ -217,6 +217,8 @@ config LIB80211_CRYPT_WEP
config LIB80211_CRYPT_CCMP config LIB80211_CRYPT_CCMP
tristate tristate
select CRYPTO_AES
select CRYPTO_CCM
config LIB80211_CRYPT_TKIP config LIB80211_CRYPT_TKIP
tristate tristate
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/ieee80211.h> #include <linux/ieee80211.h>
#include <linux/crypto.h> #include <linux/crypto.h>
#include <crypto/aead.h>
#include <net/lib80211.h> #include <net/lib80211.h>
...@@ -48,20 +49,13 @@ struct lib80211_ccmp_data { ...@@ -48,20 +49,13 @@ struct lib80211_ccmp_data {
int key_idx; int key_idx;
struct crypto_cipher *tfm; struct crypto_aead *tfm;
/* scratch buffers for virt_to_page() (crypto API) */ /* scratch buffers for virt_to_page() (crypto API) */
u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN], u8 tx_aad[2 * AES_BLOCK_LEN];
tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN]; u8 rx_aad[2 * AES_BLOCK_LEN];
u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN];
}; };
static inline void lib80211_ccmp_aes_encrypt(struct crypto_cipher *tfm,
const u8 pt[16], u8 ct[16])
{
crypto_cipher_encrypt_one(tfm, ct, pt);
}
static void *lib80211_ccmp_init(int key_idx) static void *lib80211_ccmp_init(int key_idx)
{ {
struct lib80211_ccmp_data *priv; struct lib80211_ccmp_data *priv;
...@@ -71,7 +65,7 @@ static void *lib80211_ccmp_init(int key_idx) ...@@ -71,7 +65,7 @@ static void *lib80211_ccmp_init(int key_idx)
goto fail; goto fail;
priv->key_idx = key_idx; priv->key_idx = key_idx;
priv->tfm = crypto_alloc_cipher("aes", 0, 0); priv->tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(priv->tfm)) { if (IS_ERR(priv->tfm)) {
priv->tfm = NULL; priv->tfm = NULL;
goto fail; goto fail;
...@@ -82,7 +76,7 @@ static void *lib80211_ccmp_init(int key_idx) ...@@ -82,7 +76,7 @@ static void *lib80211_ccmp_init(int key_idx)
fail: fail:
if (priv) { if (priv) {
if (priv->tfm) if (priv->tfm)
crypto_free_cipher(priv->tfm); crypto_free_aead(priv->tfm);
kfree(priv); kfree(priv);
} }
...@@ -93,25 +87,16 @@ static void lib80211_ccmp_deinit(void *priv) ...@@ -93,25 +87,16 @@ static void lib80211_ccmp_deinit(void *priv)
{ {
struct lib80211_ccmp_data *_priv = priv; struct lib80211_ccmp_data *_priv = priv;
if (_priv && _priv->tfm) if (_priv && _priv->tfm)
crypto_free_cipher(_priv->tfm); crypto_free_aead(_priv->tfm);
kfree(priv); kfree(priv);
} }
static inline void xor_block(u8 * b, u8 * a, size_t len) static int ccmp_init_iv_and_aad(const struct ieee80211_hdr *hdr,
{ const u8 *pn, u8 *iv, u8 *aad)
int i;
for (i = 0; i < len; i++)
b[i] ^= a[i];
}
static void ccmp_init_blocks(struct crypto_cipher *tfm,
struct ieee80211_hdr *hdr,
u8 * pn, size_t dlen, u8 * b0, u8 * auth, u8 * s0)
{ {
u8 *pos, qc = 0; u8 *pos, qc = 0;
size_t aad_len; size_t aad_len;
int a4_included, qc_included; int a4_included, qc_included;
u8 aad[2 * AES_BLOCK_LEN];
a4_included = ieee80211_has_a4(hdr->frame_control); a4_included = ieee80211_has_a4(hdr->frame_control);
qc_included = ieee80211_is_data_qos(hdr->frame_control); qc_included = ieee80211_is_data_qos(hdr->frame_control);
...@@ -127,17 +112,19 @@ static void ccmp_init_blocks(struct crypto_cipher *tfm, ...@@ -127,17 +112,19 @@ static void ccmp_init_blocks(struct crypto_cipher *tfm,
aad_len += 2; aad_len += 2;
} }
/* CCM Initial Block: /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
* Flag (Include authentication header, M=3 (8-octet MIC), * mode authentication are not allowed to collide, yet both are derived
* L=1 (2-octet Dlen)) * from the same vector. We only set L := 1 here to indicate that the
* Nonce: 0x00 | A2 | PN * data size can be represented in (L+1) bytes. The CCM layer will take
* Dlen */ * care of storing the data length in the top (L+1) bytes and setting
b0[0] = 0x59; * and clearing the other bits as is required to derive the two IVs.
b0[1] = qc; */
memcpy(b0 + 2, hdr->addr2, ETH_ALEN); iv[0] = 0x1;
memcpy(b0 + 8, pn, CCMP_PN_LEN);
b0[14] = (dlen >> 8) & 0xff; /* Nonce: QC | A2 | PN */
b0[15] = dlen & 0xff; iv[1] = qc;
memcpy(iv + 2, hdr->addr2, ETH_ALEN);
memcpy(iv + 8, pn, CCMP_PN_LEN);
/* AAD: /* AAD:
* FC with bits 4..6 and 11..13 masked to zero; 14 is always one * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
...@@ -147,31 +134,20 @@ static void ccmp_init_blocks(struct crypto_cipher *tfm, ...@@ -147,31 +134,20 @@ static void ccmp_init_blocks(struct crypto_cipher *tfm,
* QC (if present) * QC (if present)
*/ */
pos = (u8 *) hdr; pos = (u8 *) hdr;
aad[0] = 0; /* aad_len >> 8 */ aad[0] = pos[0] & 0x8f;
aad[1] = aad_len & 0xff; aad[1] = pos[1] & 0xc7;
aad[2] = pos[0] & 0x8f; memcpy(aad + 2, hdr->addr1, 3 * ETH_ALEN);
aad[3] = pos[1] & 0xc7;
memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN);
pos = (u8 *) & hdr->seq_ctrl; pos = (u8 *) & hdr->seq_ctrl;
aad[22] = pos[0] & 0x0f; aad[20] = pos[0] & 0x0f;
aad[23] = 0; /* all bits masked */ aad[21] = 0; /* all bits masked */
memset(aad + 24, 0, 8); memset(aad + 22, 0, 8);
if (a4_included) if (a4_included)
memcpy(aad + 24, hdr->addr4, ETH_ALEN); memcpy(aad + 22, hdr->addr4, ETH_ALEN);
if (qc_included) { if (qc_included) {
aad[a4_included ? 30 : 24] = qc; aad[a4_included ? 28 : 22] = qc;
/* rest of QC masked */ /* rest of QC masked */
} }
return aad_len;
/* Start with the first block and AAD */
lib80211_ccmp_aes_encrypt(tfm, b0, auth);
xor_block(auth, aad, AES_BLOCK_LEN);
lib80211_ccmp_aes_encrypt(tfm, auth, auth);
xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
lib80211_ccmp_aes_encrypt(tfm, auth, auth);
b0[0] &= 0x07;
b0[14] = b0[15] = 0;
lib80211_ccmp_aes_encrypt(tfm, b0, s0);
} }
static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len, static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len,
...@@ -214,13 +190,13 @@ static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len, ...@@ -214,13 +190,13 @@ static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len,
static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
{ {
struct lib80211_ccmp_data *key = priv; struct lib80211_ccmp_data *key = priv;
int data_len, i, blocks, last, len;
u8 *pos, *mic;
struct ieee80211_hdr *hdr; struct ieee80211_hdr *hdr;
u8 *b0 = key->tx_b0; struct aead_request *req;
u8 *b = key->tx_b; struct scatterlist sg[2];
u8 *e = key->tx_e; u8 *aad = key->tx_aad;
u8 *s0 = key->tx_s0; u8 iv[AES_BLOCK_LEN];
int len, data_len, aad_len;
int ret;
if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len) if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len)
return -1; return -1;
...@@ -230,31 +206,28 @@ static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) ...@@ -230,31 +206,28 @@ static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
if (len < 0) if (len < 0)
return -1; return -1;
pos = skb->data + hdr_len + CCMP_HDR_LEN; req = aead_request_alloc(key->tfm, GFP_ATOMIC);
if (!req)
return -ENOMEM;
hdr = (struct ieee80211_hdr *)skb->data; hdr = (struct ieee80211_hdr *)skb->data;
ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); aad_len = ccmp_init_iv_and_aad(hdr, key->tx_pn, iv, aad);
blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
last = data_len % AES_BLOCK_LEN;
for (i = 1; i <= blocks; i++) {
len = (i == blocks && last) ? last : AES_BLOCK_LEN;
/* Authentication */
xor_block(b, pos, len);
lib80211_ccmp_aes_encrypt(key->tfm, b, b);
/* Encryption, with counter */
b0[14] = (i >> 8) & 0xff;
b0[15] = i & 0xff;
lib80211_ccmp_aes_encrypt(key->tfm, b0, e);
xor_block(pos, e, len);
pos += len;
}
mic = skb_put(skb, CCMP_MIC_LEN); skb_put(skb, CCMP_MIC_LEN);
for (i = 0; i < CCMP_MIC_LEN; i++)
mic[i] = b[i] ^ s0[i];
return 0; sg_init_table(sg, 2);
sg_set_buf(&sg[0], aad, aad_len);
sg_set_buf(&sg[1], skb->data + hdr_len + CCMP_HDR_LEN,
data_len + CCMP_MIC_LEN);
aead_request_set_callback(req, 0, NULL, NULL);
aead_request_set_ad(req, aad_len);
aead_request_set_crypt(req, sg, sg, data_len, iv);
ret = crypto_aead_encrypt(req);
aead_request_free(req);
return ret;
} }
/* /*
...@@ -283,13 +256,13 @@ static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) ...@@ -283,13 +256,13 @@ static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
struct lib80211_ccmp_data *key = priv; struct lib80211_ccmp_data *key = priv;
u8 keyidx, *pos; u8 keyidx, *pos;
struct ieee80211_hdr *hdr; struct ieee80211_hdr *hdr;
u8 *b0 = key->rx_b0; struct aead_request *req;
u8 *b = key->rx_b; struct scatterlist sg[2];
u8 *a = key->rx_a; u8 *aad = key->rx_aad;
u8 iv[AES_BLOCK_LEN];
u8 pn[6]; u8 pn[6];
int i, blocks, last, len; int aad_len, ret;
size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN; size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN;
u8 *mic = skb->data + skb->len - CCMP_MIC_LEN;
if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) { if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) {
key->dot11RSNAStatsCCMPFormatErrors++; key->dot11RSNAStatsCCMPFormatErrors++;
...@@ -337,28 +310,26 @@ static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) ...@@ -337,28 +310,26 @@ static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
return -4; return -4;
} }
ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b); req = aead_request_alloc(key->tfm, GFP_ATOMIC);
xor_block(mic, b, CCMP_MIC_LEN); if (!req)
return -ENOMEM;
blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
last = data_len % AES_BLOCK_LEN;
for (i = 1; i <= blocks; i++) {
len = (i == blocks && last) ? last : AES_BLOCK_LEN;
/* Decrypt, with counter */
b0[14] = (i >> 8) & 0xff;
b0[15] = i & 0xff;
lib80211_ccmp_aes_encrypt(key->tfm, b0, b);
xor_block(pos, b, len);
/* Authentication */
xor_block(a, pos, len);
lib80211_ccmp_aes_encrypt(key->tfm, a, a);
pos += len;
}
if (memcmp(mic, a, CCMP_MIC_LEN) != 0) { aad_len = ccmp_init_iv_and_aad(hdr, pn, iv, aad);
net_dbg_ratelimited("CCMP: decrypt failed: STA=%pM\n",
hdr->addr2); sg_init_table(sg, 2);
sg_set_buf(&sg[0], aad, aad_len);
sg_set_buf(&sg[1], pos, data_len);
aead_request_set_callback(req, 0, NULL, NULL);
aead_request_set_ad(req, aad_len);
aead_request_set_crypt(req, sg, sg, data_len, iv);
ret = crypto_aead_decrypt(req);
aead_request_free(req);
if (ret) {
net_dbg_ratelimited("CCMP: decrypt failed: STA=%pM (%d)\n",
hdr->addr2, ret);
key->dot11RSNAStatsCCMPDecryptErrors++; key->dot11RSNAStatsCCMPDecryptErrors++;
return -5; return -5;
} }
...@@ -377,7 +348,7 @@ static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv) ...@@ -377,7 +348,7 @@ static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv)
{ {
struct lib80211_ccmp_data *data = priv; struct lib80211_ccmp_data *data = priv;
int keyidx; int keyidx;
struct crypto_cipher *tfm = data->tfm; struct crypto_aead *tfm = data->tfm;
keyidx = data->key_idx; keyidx = data->key_idx;
memset(data, 0, sizeof(*data)); memset(data, 0, sizeof(*data));
...@@ -394,7 +365,9 @@ static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv) ...@@ -394,7 +365,9 @@ static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv)
data->rx_pn[4] = seq[1]; data->rx_pn[4] = seq[1];
data->rx_pn[5] = seq[0]; data->rx_pn[5] = seq[0];
} }
crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN); if (crypto_aead_setauthsize(data->tfm, CCMP_MIC_LEN) ||
crypto_aead_setkey(data->tfm, data->key, CCMP_TK_LEN))
return -1;
} else if (len == 0) } else if (len == 0)
data->key_set = 0; data->key_set = 0;
else else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment