hks_mbedtls_aes.c代码分析

一、背景知识

自己对代码的一些见解
这里主要是使用mbedtls库中aes对应的一些函数,该库中将功能基本封装好。鸿蒙这里进行了进一步的适配和封装。主要实现一些密钥的生成,加解密的实现。这里的加解密相比其他文件有一个特殊点:就是加解密都封装在一个函数内,在函数形参中传入逻辑变量来实现。相当于一个开关变量,开的时候实现加密,关闭的时候实现解密。真正的封装好各部分功能。

aes算法介绍链接:

  1. 队友的博客

二、代码分析

1.密钥的生成
实现最基础的密钥生成(基于随机数的生成实现密钥生成,填写随机数据进key中data)

//密钥的生成
int32_t HksMbedtlsAesGenerateKey(const struct HksKeySpec *spec, struct HksBlob *key)
{
    const uint32_t keyByteLen = spec->keyLen / HKS_BITS_PER_BYTE;

    uint8_t *outKey = (uint8_t *)HksMalloc(keyByteLen);
    //先申请空间用来存放随机数据(密钥数据)
    if (outKey == NULL) {
        return HKS_ERROR_MALLOC_FAIL;
    }

    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context ctrDrbg;
    int32_t ret = HksCtrDrbgSeed(&ctrDrbg, &entropy);
    //随机数种子(熵源)
    if (ret != HKS_SUCCESS) {
        HKS_FREE_PTR(outKey);
        return ret;
    }

    do {
        ret = mbedtls_ctr_drbg_random(&ctrDrbg, outKey, keyByteLen);
        //在对应空间outkey生成随机数
        if (ret != HKS_MBEDTLS_SUCCESS) {
            HKS_LOG_E("Mbedtls ctr drbg random failed! mbedtls ret = 0x%X", ret);
            (void)memset_s(outKey, keyByteLen, 0, keyByteLen);
            HKS_FREE_PTR(outKey);
            break;
        }
        //将最后结果写进key的数据域和size
        key->data = outKey;
        key->size = keyByteLen;
    } while (0);

    mbedtls_ctr_drbg_free(&ctrDrbg);
    mbedtls_entropy_free(&entropy);
    //释放随机数空间和熵源
    return ret;
}

2.aes/cbc在nopadding模式下的加密
第一种加密模式,其中参数最重要的个人认为是这个bool变量 encrypt。他象个开关控制是加密还是解密。
参数介绍:

  • key:加解密的密钥
  • cipherParam:密文参数信息
  • message:加密的结果会生成一个消息
  • encrypt:逻辑变量控制是加密还是解密
  • ciphetext:密文信息
static int32_t AesCbcNoPaddingCrypt(const struct HksBlob *key, const struct HksCipherParam *cipherParam,
    const struct HksBlob *message, const bool encrypt, struct HksBlob *cipherText)
{
    mbedtls_aes_context ctx;
    mbedtls_aes_init(&ctx);

    int32_t ret;
    do {
        if (encrypt) {
            ret = mbedtls_aes_setkey_enc(&ctx, key->data, key->size * HKS_BITS_PER_BYTE);
        //根据传入的参数,如果为逻辑真就设置一套加密密钥
        } else {
            ret = mbedtls_aes_setkey_dec(&ctx, key->data, key->size * HKS_BITS_PER_BYTE);
        }
        //如果为逻辑假就设置一套解密密钥
        if (ret != HKS_MBEDTLS_SUCCESS) {
            HKS_LOG_E("Mbedtls aes set key failed! mbedtls ret = 0x%X", ret);
            break;
        }

        /* mbedtls_aes_crypt_cbc will refresh iv, so need a temp iv */
        uint8_t tmpIv[HKS_AES_CBC_NOPADDING_IV_SIZE];
        //定义一组临时向量iv
        if (memcpy_s(tmpIv, HKS_AES_CBC_NOPADDING_IV_SIZE, cipherParam->iv.data, cipherParam->iv.size) != EOK) {
            HKS_LOG_E("Memcpy temp iv failed!");
            break;
        }
        //初始化tmpiv的空间为cipherParam->iv.data
        ret = mbedtls_aes_crypt_cbc(&ctx, (encrypt ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT),
            message->size, tmpIv, message->data, cipherText->data);
        //加解密的实现,根据逻辑值判断为加密还是解密
        if (ret != HKS_MBEDTLS_SUCCESS) {
            HKS_LOG_E("Mbedtks aes cbc crypt failed! mbedtls ret = 0x%X", ret);
            (void)memset_s(cipherText->data, cipherText->size, 0, cipherText->size);
            break;
        }
        cipherText->size = message->size;
        //将最后加密后的message内容写进密文
    } while (0);

    mbedtls_aes_free(&ctx);
    //释放相关空间ctx
    return ret;
}

3.aes/cbc在pkcs7下的加密
该函数也是一种加解密的方式,上一种是nopadding,着一种是okcs7的方法。参数功能基本相似。

参数介绍:

  • key:加解密的密钥
  • cipherParam:密文参数信息
  • message:加密的结果会生成一个消息
  • encrypt:逻辑变量控制是加密还是解密
  • ciphetext:密文信息
//pkcs7的加解密
static int32_t AesCbcPkcs7Crypt(const struct HksBlob *key, const struct HksCipherParam *cipherParam,
    const struct HksBlob *message, const bool encrypt, struct HksBlob *cipherText)
{
    const uint32_t keyBitLen = key->size * HKS_BITS_PER_BYTE; 
    //密钥的位长度
    const mbedtls_cipher_info_t *info =
        mbedtls_cipher_info_from_values(MBEDTLS_CIPHER_ID_AES, keyBitLen, MBEDTLS_MODE_CBC);
    //村数组中获取密文信息
    mbedtls_cipher_context_t ctx;
    mbedtls_cipher_init(&ctx);
    //cipher变量初始化
    int32_t ret;
    do {
        ret = mbedtls_cipher_setup(&ctx, info);
        //以指定信息设置ctx
        if (ret != HKS_SUCCESS) {
            HKS_LOG_E("Mbedtls cbc pkcs7 setup ctx failed! mbedtls ret = 0x%X", ret);
            break;
        }

        ret = mbedtls_cipher_setkey(&ctx, key->data, keyBitLen, (encrypt ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT));
        //根据encrypt传入的逻辑值来判断是要设置加密密文还是解密密文
        if (ret != HKS_SUCCESS) {
            HKS_LOG_E("Mbedtls cbc pkcs7 set key failed! mbedtls ret = 0x%X", ret);
            break;
        }

        ret = mbedtls_cipher_crypt(&ctx, cipherParam->iv.data, cipherParam->iv.size,
            message->data, message->size, cipherText->data, (size_t *)&(cipherText->size));
        //加解密的实现。上一步密钥密文信息的设置结果,在ctx中的结果实现加解密。
        if (ret != HKS_MBEDTLS_SUCCESS) {
            HKS_LOG_E("Mbedtls cbc pkcs7 crypt failed! mbedtls ret = 0x%X", ret);
            (void)memset_s(cipherText->data, cipherText->size, 0, cipherText->size);
        }
    } while (0);

    mbedtls_cipher_free(&ctx);
    return ret;
}

4.aes/cbc的普通加密
该函数主要也是调用之前的加密方法,将算法写进usageSpec进行判断选用不同的模式进行加解密。

//封装函数
#if defined(HKS_SUPPORT_AES_CBC_NOPADDING) || defined(HKS_SUPPORT_AES_CBC_PKCS7)
static int32_t AesCbcCrypt(const struct HksBlob *key, const struct HksUsageSpec *usageSpec,
    const struct HksBlob *message, const bool encrypt, struct HksBlob *cipherText)
{
    const struct HksCipherParam *cipherParam = (struct HksCipherParam *)(usageSpec->algParam);

    switch (usageSpec->padding) {
        //根据usageSpec中内容进行分支,里面是判断选用nopadding还是pkcs7算法
#ifdef HKS_SUPPORT_AES_CBC_NOPADDING
        case HKS_PADDING_NONE:
            return AesCbcNoPaddingCrypt(key, cipherParam, message, encrypt, cipherText);
#endif
#ifdef HKS_SUPPORT_AES_CBC_PKCS7
        case HKS_PADDING_PKCS7:
            return AesCbcPkcs7Crypt(key, cipherParam, message, encrypt, cipherText);
#endif
        default:
            HKS_LOG_E("Unsupport padding! mode = 0x%X", usageSpec->padding);
            return HKS_ERROR_INVALID_PADDING;
    }
}

5.aes和gcm的加密
aes和gcm的加密模式。

static int32_t AesEncryptGcm(const struct HksBlob *key, const struct HksUsageSpec *usageSpec,
    const struct HksBlob *message, struct HksBlob *cipherText, struct HksBlob *tagAead)
{
    mbedtls_gcm_context ctx;
    mbedtls_gcm_init(&ctx);
    //先定义及初始化一个gcm的密文信息ctx
    int32_t ret;
    do {
        ret = mbedtls_gcm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key->data, key->size * HKS_BITS_PER_BYTE);
        //设置一个密钥,写进key中
        if (ret != HKS_MBEDTLS_SUCCESS) {
            HKS_LOG_E("Mbedtls aes gcm set key failed! mbedtls ret = 0x%X", ret);
            break;
        }

        const struct HksAeadParam *aeadParam = (struct HksAeadParam *)(usageSpec->algParam);
        ret = mbedtls_gcm_crypt_and_tag(&ctx, MBEDTLS_GCM_ENCRYPT, message->size,
            aeadParam->nonce.data, aeadParam->nonce.size, aeadParam->aad.data, aeadParam->aad.size,
            message->data, cipherText->data, tagAead->size, tagAead->data);
        //调用mbedtls_gcm_crypt_and_tag实现加密和标记
        if (ret != HKS_MBEDTLS_SUCCESS) {
            HKS_LOG_E("Mbedtls aes gcm encryot failed! mbedtls ret = 0x%X", ret);
            (void)memset_s(cipherText->data, cipherText->size, 0, cipherText->size);
            (void)memset_s(tagAead->data, tagAead->size, 0, tagAead->size);
            break;
        //加密失败的处理
        }
        cipherText->size = message->size;
        //将最后结果写进密文
    } while (0);

    mbedtls_gcm_free(&ctx);
    return ret;
}

6.aes和gcm的解密
对应的解密函数,一般加密函数会对应一个唯一的解密函数。或者是将两个函数写在一起,用逻辑变量区分。

//aes/gcm的解密
static int32_t AesDecryptGcm(const struct HksBlob *key, const struct HksUsageSpec *usageSpec,
    const struct HksBlob *message, struct HksBlob *cipherText)
{
    mbedtls_gcm_context ctx;
    mbedtls_gcm_init(&ctx);

    int32_t ret;
    do {
        ret = mbedtls_gcm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key->data, key->size * HKS_BITS_PER_BYTE);
        if (ret != HKS_MBEDTLS_SUCCESS) {
            HKS_LOG_E("Mbedtls aes gcm set key failed! mbedtls ret = 0x%X", ret);
            break;
        }

        const struct HksAeadParam *aeadParam = (struct HksAeadParam *)(usageSpec->algParam);
        ret = mbedtls_gcm_auth_decrypt(&ctx, message->size, aeadParam->nonce.data, aeadParam->nonce.size,
            aeadParam->aad.data, aeadParam->aad.size, aeadParam->tagDec.data, aeadParam->tagDec.size,
            message->data, cipherText->data);
        if (ret != HKS_MBEDTLS_SUCCESS) {
            HKS_LOG_E("Mbedtls aes gcm decrypt failed! mbedtls ret = 0x%X", ret);
            (void)memset_s(cipherText->data, cipherText->size, 0, cipherText->size);
            break;
        }
        cipherText->size = message->size;
    } while (0);

    mbedtls_gcm_free(&ctx);
    return ret;
}

7.密钥的检查
检查密钥的位长度size是否位128、192、256等。

//密钥的检查
static int32_t CheckKeySize(const struct HksBlob *key)
{
    if ((key->size != HKS_KEY_BYTES(HKS_AES_KEY_SIZE_128)) && (key->size != HKS_KEY_BYTES(HKS_AES_KEY_SIZE_192)) &&
        (key->size != HKS_KEY_BYTES(HKS_AES_KEY_SIZE_256))) {
    //检查密钥字节长度是否为128或者是192或者是256
        return HKS_ERROR_INVALID_KEY_SIZE;
    }

    return HKS_SUCCESS;
}

8.加密和解密功能的封装
总的函数功能封装,将会情况写进usagespec中,然后采用switch语句将不同函数封装进去,cbc将nopadding和pkcs7封装在了一起,整体叫做cbc加密,而相对应的另一种加解密方式就是gcm和aes的加解密模式,这里封装cbc和gcm的情况。同时实现了解密的封装。

//函数功能的封装,根据不同情况判断使用何种加解密方式
int32_t HksMbedtlsAesEncrypt(const struct HksBlob *key, const struct HksUsageSpec *usageSpec,
    const struct HksBlob *message, struct HksBlob *cipherText, struct HksBlob *tagAead)
{
    if (CheckKeySize(key) != HKS_SUCCESS) {
        HKS_LOG_E("Invalid aes keySiz = 0x%X", key->size);
        return HKS_ERROR_INVALID_KEY_SIZE;
    }

    switch (usageSpec->mode) {
        //基于usageSpec->mode进行分支选择加解密算法
#if defined(HKS_SUPPORT_AES_CBC_NOPADDING) || defined(HKS_SUPPORT_AES_CBC_PKCS7)
        case HKS_MODE_CBC:
            return AesCbcCrypt(key, usageSpec, message, true, cipherText);
#endif
#ifdef HKS_SUPPORT_AES_GCM
        case HKS_MODE_GCM:
            return AesEncryptGcm(key, usageSpec, message, cipherText, tagAead);
#endif
#ifdef HKS_SUPPORT_AES_CCM
        case HKS_MODE_CCM:
            return AesEncryptCcm(key, usageSpec, message, cipherText, tagAead);
#endif
        default:
            HKS_LOG_E("Unsupport key alg! mode = 0x%X", usageSpec->mode);
            return HKS_ERROR_INVALID_ARGUMENT;
    }
}

解密的封装

//解密的封装
int32_t HksMbedtlsAesDecrypt(const struct HksBlob *key, const struct HksUsageSpec *usageSpec,
    const struct HksBlob *message, struct HksBlob *cipherText)
{
    if (CheckKeySize(key) != HKS_SUCCESS) {
        HKS_LOG_E("Invalid aes keySize = 0x%X", key->size);
        return HKS_ERROR_INVALID_KEY_SIZE;
    }

    switch (usageSpec->mode) {
#if defined(HKS_SUPPORT_AES_CBC_NOPADDING) || defined(HKS_SUPPORT_AES_CBC_PKCS7)
        case HKS_MODE_CBC:
            return AesCbcCrypt(key, usageSpec, message, false, cipherText);
#endif
#ifdef HKS_SUPPORT_AES_GCM
        case HKS_MODE_GCM:
            return AesDecryptGcm(key, usageSpec, message, cipherText);
#endif
#ifdef HKS_SUPPORT_AES_CCM
        case HKS_MODE_CCM:
            return AesDecryptCcm(key, usageSpec, message, cipherText);
#endif
        default:
            HKS_LOG_E("Unsupport key alg! mode = 0x%X", usageSpec->mode);
            return HKS_ERROR_INVALID_ARGUMENT;
    }
}