From 830881389a1369a0e8915aa20a0c0853949a85f3 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Thu, 30 Mar 2017 17:37:50 +0530 Subject: [PATCH] qcacmn: Add changes to support crypto API Add support for hash, hmac-hash calculation and aead encryption and decryption. Change-Id: Idddcfaa2ec4c5689c997559551230bc8f59df032 CRs-Fixed: 2029962 --- qdf/inc/qdf_crypto.h | 139 ++++++++++++++++ qdf/linux/src/qdf_crypto.c | 321 +++++++++++++++++++++++++++++++++++++ 2 files changed, 460 insertions(+) create mode 100644 qdf/inc/qdf_crypto.h create mode 100644 qdf/linux/src/qdf_crypto.c diff --git a/qdf/inc/qdf_crypto.h b/qdf/inc/qdf_crypto.h new file mode 100644 index 0000000000..1cd3394245 --- /dev/null +++ b/qdf/inc/qdf_crypto.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2017 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 + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: qdf_crypto.h + * This file provides OS abstraction for crypto APIs. + */ + +#if !defined(__QDF_CRYPTO_H) +#define __QDF_CRYPTO_H + +/* Include Files */ +#include "qdf_status.h" +#include +#include + +/* Preprocessor definitions and constants */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define AES_BLOCK_SIZE 16 +#define HMAC_SHA256_CRYPTO_TYPE "hmac(sha256)" +#define HMAC_SHA386_CRYPTO_TYPE "hmac(sha384)" + +#define SHA256_CRYPTO_TYPE "sha256" +#define SHA386_CRYPTO_TYPE "sha384" + +#define SHA256_DIGEST_SIZE 32 +#define SHA384_DIGEST_SIZE 48 + +#define FIXED_PARAM_OFFSET_ASSOC_REQ 4 +#define FIXED_PARAM_OFFSET_ASSOC_RSP 6 + +/* Function declarations and documenation */ + +/** + * qdf_get_hash: API to get hash using specific crypto and scatterlist + * @type: crypto type + * @element_cnt: scatterlist element count + * @addr: scatterlist element array + * @addr_len: element length array + * @hash: new hash + * + * Return: 0 if success else error code + */ +int qdf_get_hash(uint8_t *type, uint8_t element_cnt, + uint8_t *addr[], uint32_t *addr_len, + int8_t *hash); + +/** + * qdf_get_hmac_hash: API to get hmac hash using specific crypto and + * scatterlist elements. + * @type: crypto type + * @key: key needs to be used for hmac api + * @keylen: length of key + * @element_cnt: scatterlist element count + * @addr: scatterlist element array + * @addr_len: element length array + * @hash: new hash + * + * Return: 0 if success else error code + */ +int qdf_get_hmac_hash(uint8_t *type, uint8_t *key, + uint32_t keylen, uint8_t element_cnt, + uint8_t *addr[], uint32_t *addr_len, int8_t *hash); + +/** + * qdf_get_keyed_hash: API to get hash using specific crypto and + * scatterlist elements. + * @type: crypto type + * @key: key needs to be used for hmac api + * @keylen: length of key + * @element_cnt: scatterlist element count + * @addr: scatterlist element array + * @addr_len: element length array + * @hash: new hash + * + * Return: 0 if success else error code + */ +int qdf_get_keyed_hash(const char *alg, const uint8_t *key, + unsigned int key_len, const uint8_t *src[], + size_t *src_len, size_t num_elements, uint8_t *out); +/** + * qdf_update_dbl: This API does the doubling operation as defined in RFC5297 + * @d: input for doubling + * + * Return: None + */ +void qdf_update_dbl(uint8_t *d); + +/** + * qdf_aes_s2v: This API gets vector from AES string as defined in RFC5297 + * output length will be AES_BLOCK_SIZE. + * @key: key used for operation + * @key_len: key len + * @s: addresses of elements to be used + * @s_len: array of element length + * @num_s: number of elements + * @out: pointer to output vector + * + * Return: 0 if success else Error number + */ +int qdf_aes_s2v(const uint8_t *key, unsigned int key_len, const uint8_t *s[], + size_t s_len[], size_t num_s, uint8_t *out); + +/** + * qdf_aes_ctr: This API defines AES Counter Mode + * @key: key used for operation + * @key_len: key len + * @siv: Initialization vector + * @src: input + * @src_len: input len + * @dest: output + * @enc: if encryption needs to be done or decryption + * + * Return: 0 if success else Error number + */ +int qdf_aes_ctr(const uint8_t *key, unsigned int key_len, uint8_t *siv, + const uint8_t *src, size_t src_len, uint8_t *dest, bool enc); +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* __QDF_CRYPTO_H */ diff --git a/qdf/linux/src/qdf_crypto.c b/qdf/linux/src/qdf_crypto.c new file mode 100644 index 0000000000..c79bd201db --- /dev/null +++ b/qdf/linux/src/qdf_crypto.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2017 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 + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: qdf_crypto.c + * + * This source file contains linux specific definitions for QDF crypto APIs + */ + +/* Include Files */ +#include "qdf_crypto.h" +#include +#include +#include +#include + +/* Function Definitions and Documentation */ + +/* + * xor: API to calculate xor + * @a: first variable + * @b: second variable + * @len: length of variables + */ +static void xor(uint8_t *a, const uint8_t *b, size_t len) +{ + unsigned int i; + + for (i = 0; i < len; i++) + a[i] ^= b[i]; +} + +int qdf_get_hash(uint8_t *type, + uint8_t element_cnt, uint8_t *addr[], uint32_t *addr_len, + int8_t *hash) +{ + int i, ret; + struct hash_desc desc; + struct scatterlist sg; + + /* allocate crypto hash type */ + desc.tfm = crypto_alloc_hash(type, 0, CRYPTO_ALG_ASYNC); + + if (IS_ERR(desc.tfm)) { + ret = PTR_ERR(desc.tfm); + return -EINVAL; + } + desc.flags = 0; + ret = crypto_hash_init(&desc); + + if (ret) + return ret; + + for (i = 0; i < element_cnt ; i++) { + sg_init_one(&sg, addr[i], addr_len[i]); + crypto_hash_update(&desc, &sg, addr_len[i]); + } + + crypto_hash_final(&desc, hash); + crypto_free_hash(desc.tfm); + return 0; +} + +int qdf_get_hmac_hash(uint8_t *type, uint8_t *key, + uint32_t keylen, + uint8_t element_cnt, uint8_t *addr[], uint32_t *addr_len, + int8_t *hash) +{ + int i, ret; + struct hash_desc desc; + struct scatterlist sg; + + /* allocate crypto hash type */ + desc.tfm = crypto_alloc_hash(type, 0, CRYPTO_ALG_ASYNC); + + if (IS_ERR(desc.tfm)) { + ret = PTR_ERR(desc.tfm); + return -EINVAL; + } + desc.flags = 0; + ret = crypto_hash_setkey(desc.tfm, key, keylen); + + crypto_hash_init(&desc); + + if (ret) + return ret; + + for (i = 0; i < element_cnt ; i++) { + sg_init_one(&sg, addr[i], addr_len[i]); + crypto_hash_update(&desc, &sg, addr_len[i]); + } + + crypto_hash_final(&desc, hash); + crypto_free_hash(desc.tfm); + return 0; +} + +/* qdf_update_dbl from RFC 5297. Length of d is AES_BLOCK_SIZE (128 bits) */ +void qdf_update_dbl(uint8_t *d) +{ + int i; + uint8_t msb, msb_prev = 0; + + /* left shift by 1 */ + for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) { + msb = d[i] & 0x80; + d[i] = d[i] << 1; + d[i] += msb_prev ? 1 : 0; + msb_prev = msb; + } + + if (msb) + d[AES_BLOCK_SIZE - 1] ^= 0x87; +} + +int qdf_get_keyed_hash(const char *alg, const uint8_t *key, + unsigned int key_len, const uint8_t *src[], + size_t *src_len, size_t num_elements, uint8_t *out) +{ + struct crypto_shash *tfm; + int ret; + size_t i; + + tfm = crypto_alloc_shash(alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + FL("Failed to allocate transformation for %s: %ld"), + alg, PTR_ERR(tfm)); + return -EINVAL; + } + + if (key && key_len) { + ret = crypto_shash_setkey(tfm, key, key_len); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + FL("Set key failed for %s, ret:%d"), + alg, -ret); + goto error; + } + } + + do { + SHASH_DESC_ON_STACK(desc, tfm); + desc->tfm = tfm; + desc->flags = crypto_shash_get_flags(tfm); + + ret = crypto_shash_init(desc); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + FL("Failed to init hash for %s, ret:%d"), + alg, -ret); + goto error; + } + + for (i = 0; i < num_elements; i++) { + ret = crypto_shash_update(desc, src[i], src_len[i]); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_QDF, + QDF_TRACE_LEVEL_ERROR, + FL("Failed to update hash for %s, ret:%d"), + alg, -ret); + goto error; + } + } + + ret = crypto_shash_final(desc, out); + if (ret) + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + FL("Failed to get digest for %s, ret:%d"), + alg, -ret); + } while (0); + +error: + crypto_free_shash(tfm); + return ret; +} + +/* AES String to Vector from RFC 5297, 'out' should be of length AES_BLOCK_SIZE + */ +int qdf_aes_s2v(const uint8_t *key, unsigned int key_len, const uint8_t *s[], + size_t s_len[], size_t num_s, uint8_t *out) +{ + const char *alg = "cmac(aes)"; + uint8_t d[AES_BLOCK_SIZE]; + uint8_t buf[AES_BLOCK_SIZE] = { 0 }; + size_t buf_len = AES_BLOCK_SIZE; + const uint8_t *a[1]; + unsigned int i; + uint8_t *t = NULL; + size_t t_len; + int ret; + + if (num_s == 0) { + /* V = AES-CMAC(K, ) */ + buf[0] = 0x01; + a[0] = buf; + ret = qdf_get_keyed_hash(alg, key, key_len, a, &buf_len, 1, + out); + return ret; + } + + /* D = AES-CMAC(K, ) */ + a[0] = buf; + ret = qdf_get_keyed_hash(alg, key, key_len, a, &buf_len, 1, d); + if (ret) + goto error; + + for (i = 0; i < num_s - 1; i++) { + /* D = qdf_update_dbl(D) xor AES-CMAC(K, Si) */ + qdf_update_dbl(d); + ret = qdf_get_keyed_hash(alg, key, key_len, &s[i], &s_len[i], 1, + buf); + if (ret) + goto error; + xor(d, buf, AES_BLOCK_SIZE); + } + + if (s_len[i] >= AES_BLOCK_SIZE) { + /* len(Sn) >= 128 */ + /* T = Sn xorend D */ + t = qdf_mem_malloc(s_len[i]); + if (!t) + return -EINVAL; + qdf_mem_copy(t, s[i], s_len[i]); + xor(t + s_len[i] - AES_BLOCK_SIZE, d, AES_BLOCK_SIZE); + t_len = s_len[i]; + } else { + /* len(Sn) < 128 */ + /* T = qdf_update_dbl(D) xor pad(Sn) */ + qdf_update_dbl(d); + qdf_mem_set(buf, 0, AES_BLOCK_SIZE); + qdf_mem_copy(buf, s[i], s_len[i]); + buf[s_len[i]] = 0x80; + xor(d, s[i], AES_BLOCK_SIZE); + t = d; + t_len = AES_BLOCK_SIZE; + } + + /* V = AES-CMAC(K, T) */ + a[0] = t; + ret = qdf_get_keyed_hash(alg, key, key_len, a, &t_len, 1, out); + +error: + if (t != NULL && t != d) + qdf_mem_free(t); + return ret; +} + +int qdf_aes_ctr(const uint8_t *key, unsigned int key_len, uint8_t *siv, + const uint8_t *src, size_t src_len, uint8_t *dest, bool enc) +{ + struct crypto_skcipher *tfm; + struct skcipher_request *req = NULL; + struct scatterlist sg_in, sg_out; + int ret; + + if (key_len != 16 && key_len != 24 && key_len != 32) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + FL("Invalid key length: %u"), key_len); + return -EINVAL; + } + + tfm = crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + FL("Failed to alloc transformation for ctr(aes):%ld"), + PTR_ERR(tfm)); + return -EAGAIN; + } + + req = skcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + FL("Failed to allocate request for ctr(aes)")); + crypto_free_skcipher(tfm); + return -EAGAIN; + } + + ret = crypto_skcipher_setkey(tfm, key, key_len); + if (ret) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + FL("Set key failed for ctr(aes), ret:%d"), -ret); + skcipher_request_free(req); + crypto_free_skcipher(tfm); + return ret; + } + + sg_init_one(&sg_in, src, src_len); + sg_init_one(&sg_out, dest, src_len); + skcipher_request_set_crypt(req, &sg_in, &sg_out, src_len, siv); + + if (enc) + ret = crypto_skcipher_encrypt(req); + else + ret = crypto_skcipher_decrypt(req); + + if (ret) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + FL("%s failed for ctr(aes), ret:%d"), + enc ? "Encryption" : "Decryption", -ret); + } + + skcipher_request_free(req); + crypto_free_skcipher(tfm); + return ret; +}