diff --git a/qdf/inc/qdf_crypto.h b/qdf/inc/qdf_crypto.h index 8d592fdd44..e418ce7796 100644 --- a/qdf/inc/qdf_crypto.h +++ b/qdf/inc/qdf_crypto.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, 2020 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -47,6 +47,7 @@ extern "C" { #define FIXED_PARAM_OFFSET_ASSOC_REQ 4 #define FIXED_PARAM_OFFSET_ASSOC_RSP 6 +#define CMAC_TLEN 8 /* CMAC TLen = 64 bits (8 octets) */ #define AAD_LEN 20 #define IEEE80211_MMIE_GMAC_MICLEN 16 @@ -152,9 +153,21 @@ int qdf_aes_ctr(const uint8_t *key, unsigned int key_len, uint8_t *siv, * * Return: 0 if success else Error number */ -int qdf_crypto_aes_gmac(uint8_t *key, uint16_t key_length, - uint8_t *iv, uint8_t *aad, uint8_t *data, - uint16_t data_len, uint8_t *mic); +int qdf_crypto_aes_gmac(const uint8_t *key, uint16_t key_length, + uint8_t *iv, const uint8_t *aad, + const uint8_t *data, uint16_t data_len, uint8_t *mic); + +/** + * qdf_crypto_aes_128_cmac: This API calculates MIC for AES 128 CMAC + * @key: key used for operation + * @data: Pointer to data + * @len: Length of data + * @mic: Pointer to MIC + * + * Return: 0 if success else Error number + */ +int qdf_crypto_aes_128_cmac(const uint8_t *key, const uint8_t *data, + uint16_t len, uint8_t *mic); #ifdef __cplusplus } diff --git a/qdf/linux/src/qdf_crypto.c b/qdf/linux/src/qdf_crypto.c index 766221a20a..9edabe5123 100644 --- a/qdf/linux/src/qdf_crypto.c +++ b/qdf/linux/src/qdf_crypto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -95,6 +95,136 @@ void qdf_update_dbl(uint8_t *d) d[AES_BLOCK_SIZE - 1] ^= 0x87; } +static inline void xor_128(const uint8_t *a, const uint8_t *b, uint8_t *out) +{ + uint8_t i; + + for (i = 0; i < AES_BLOCK_SIZE; i++) + out[i] = a[i] ^ b[i]; +} + +static inline void leftshift_onebit(const uint8_t *input, uint8_t *output) +{ + int i, overflow = 0; + + for (i = (AES_BLOCK_SIZE - 1); i >= 0; i--) { + output[i] = input[i] << 1; + output[i] |= overflow; + overflow = (input[i] & 0x80) ? 1 : 0; + } +} + +static void generate_subkey(struct crypto_cipher *tfm, uint8_t *k1, uint8_t *k2) +{ + uint8_t l[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE]; + const uint8_t const_rb[AES_BLOCK_SIZE] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87 + }; + const uint8_t const_zero[AES_BLOCK_SIZE] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + crypto_cipher_encrypt_one(tfm, l, const_zero); + + if ((l[0] & 0x80) == 0) { /* If MSB(l) = 0, then k1 = l << 1 */ + leftshift_onebit(l, k1); + } else { /* Else k1 = ( l << 1 ) (+) Rb */ + leftshift_onebit(l, tmp); + xor_128(tmp, const_rb, k1); + } + + if ((k1[0] & 0x80) == 0) { + leftshift_onebit(k1, k2); + } else { + leftshift_onebit(k1, tmp); + xor_128(tmp, const_rb, k2); + } +} + +static inline void padding(const uint8_t *lastb, uint8_t *pad, uint16_t length) +{ + uint8_t j; + + /* original last block */ + for (j = 0; j < AES_BLOCK_SIZE; j++) { + if (j < length) + pad[j] = lastb[j]; + else if (j == length) + pad[j] = 0x80; + else + pad[j] = 0x00; + } +} + +int qdf_crypto_aes_128_cmac(const uint8_t *key, const uint8_t *data, + uint16_t len, uint8_t *mic) +{ + uint8_t x[AES_BLOCK_SIZE], y[AES_BLOCK_SIZE]; + uint8_t m_last[AES_BLOCK_SIZE], padded[AES_BLOCK_SIZE]; + uint8_t k1[AES_KEYSIZE_128], k2[AES_KEYSIZE_128]; + int cmp_blk; + int i, num_block = (len + 15) / AES_BLOCK_SIZE; + struct crypto_cipher *tfm; + int ret; + + /* + * Calculate MIC and then copy + */ + tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + qdf_err("crypto_alloc_cipher failed (%d)", ret); + return ret; + } + + ret = crypto_cipher_setkey(tfm, key, AES_KEYSIZE_128); + if (ret) { + qdf_err("crypto_cipher_setkey failed (%d)", ret); + crypto_free_cipher(tfm); + return ret; + } + + generate_subkey(tfm, k1, k2); + + if (num_block == 0) { + num_block = 1; + cmp_blk = 0; + } else { + cmp_blk = ((len % AES_BLOCK_SIZE) == 0) ? 1 : 0; + } + + if (cmp_blk) { + /* Last block is complete block */ + xor_128(&data[AES_BLOCK_SIZE * (num_block - 1)], k1, m_last); + } else { + /* Last block is not complete block */ + padding(&data[AES_BLOCK_SIZE * (num_block - 1)], padded, + len % AES_BLOCK_SIZE); + xor_128(padded, k2, m_last); + } + + for (i = 0; i < AES_BLOCK_SIZE; i++) + x[i] = 0; + + for (i = 0; i < (num_block - 1); i++) { + /* y = Mi (+) x */ + xor_128(x, &data[AES_BLOCK_SIZE * i], y); + /* x = AES-128(KEY, y) */ + crypto_cipher_encrypt_one(tfm, x, y); + } + + xor_128(x, m_last, y); + crypto_cipher_encrypt_one(tfm, x, y); + + crypto_free_cipher(tfm); + + memcpy(mic, x, CMAC_TLEN); + + return 0; +} + /** * set_desc_flags() - set flags variable in the shash_desc struct * @desc: pointer to shash_desc struct @@ -377,9 +507,9 @@ int qdf_aes_ctr(const uint8_t *key, unsigned int key_len, uint8_t *siv, #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) -int qdf_crypto_aes_gmac(uint8_t *key, uint16_t key_length, - uint8_t *iv, uint8_t *aad, uint8_t *data, - uint16_t data_len, uint8_t *mic) +int qdf_crypto_aes_gmac(const uint8_t *key, uint16_t key_length, + uint8_t *iv, const uint8_t *aad, + const uint8_t *data, uint16_t data_len, uint8_t *mic) { struct crypto_aead *tfm; int ret = 0; diff --git a/umac/cmn_services/crypto/inc/wlan_crypto_global_api.h b/umac/cmn_services/crypto/inc/wlan_crypto_global_api.h index c6672139f1..23ac5c27f8 100644 --- a/umac/cmn_services/crypto/inc/wlan_crypto_global_api.h +++ b/umac/cmn_services/crypto/inc/wlan_crypto_global_api.h @@ -23,6 +23,7 @@ #define _WLAN_CRYPTO_GLOBAL_API_H_ #include "wlan_crypto_global_def.h" +#include /** * wlan_crypto_set_vdev_param - called by ucfg to set crypto param * @vdev: vdev @@ -726,18 +727,22 @@ QDF_STATUS wlan_set_vdev_crypto_prarams_from_ie(struct wlan_objmgr_vdev *vdev, uint16_t ie_len); #ifdef WLAN_CRYPTO_GCM_OS_DERIVATIVE static inline int wlan_crypto_aes_gmac(const uint8_t *key, size_t key_len, - const uint8_t *iv, size_t iv_len, + uint8_t *iv, size_t iv_len, const uint8_t *aad, size_t aad_len, uint8_t *tag) { - return 0; + return qdf_crypto_aes_gmac(key, key_len, iv, aad, + aad + AAD_LEN, + aad_len - AAD_LEN - + IEEE80211_MMIE_GMAC_MICLEN, + tag); } #endif #ifdef WLAN_CRYPTO_OMAC1_OS_DERIVATIVE static inline int omac1_aes_128(const uint8_t *key, const uint8_t *data, size_t data_len, uint8_t *mac) { - return 0; + return qdf_crypto_aes_128_cmac(key, data, data_len, mac); } static inline int omac1_aes_256(const uint8_t *key, const uint8_t *data, diff --git a/umac/cmn_services/crypto/src/wlan_crypto_global_api.c b/umac/cmn_services/crypto/src/wlan_crypto_global_api.c index e8845fd91f..98b04b2ee8 100644 --- a/umac/cmn_services/crypto/src/wlan_crypto_global_api.c +++ b/umac/cmn_services/crypto/src/wlan_crypto_global_api.c @@ -2068,7 +2068,7 @@ bool wlan_crypto_is_mmie_valid(struct wlan_objmgr_vdev *vdev, uint8_t *frm, uint8_t *efrm){ struct wlan_crypto_mmie *mmie = NULL; - uint8_t *ipn, *aad, *buf, mic[16], nounce[12]; + uint8_t *ipn, *aad, *buf, *mic, nounce[12]; struct wlan_crypto_key *key; struct wlan_frame_hdr *hdr; uint16_t mic_len, hdrlen, len; @@ -2160,7 +2160,11 @@ bool wlan_crypto_is_mmie_valid(struct wlan_objmgr_vdev *vdev, */ qdf_mem_copy(buf + 20, frm + hdrlen, len - hdrlen); qdf_mem_zero(buf + (len - hdrlen + 20 - mic_len), mic_len); - qdf_mem_zero(mic, 16); + mic = qdf_mem_malloc(mic_len); + if (!mic) { + qdf_mem_free(buf); + return false; + } if (crypto_priv->igtk_key_type == WLAN_CRYPTO_CIPHER_AES_CMAC) { ret = omac1_aes_128(key->keyval, buf, len - hdrlen + aad_len, mic); @@ -2181,16 +2185,19 @@ bool wlan_crypto_is_mmie_valid(struct wlan_objmgr_vdev *vdev, qdf_mem_free(buf); if (ret < 0) { + qdf_mem_free(mic); crypto_err("genarate mmie failed"); return false; } if (qdf_mem_cmp(mic, mmie->mic, mic_len) != 0) { + qdf_mem_free(mic); crypto_err("mmie mismatch"); /* MMIE MIC mismatch */ return false; } + qdf_mem_free(mic); /* Update the receive sequence number */ qdf_mem_copy(key->keyrsc, ipn, 6); crypto_debug("mmie matched");