int mbedtls_pkcs5_pbkdf2_hmac(
    mbedtls_md_context_t* context,
    const unsigned char* password,
    size_t               passwordLength,
    const unsigned char* salt,
    size_t               saltLength,
    unsigned int   iterationCount,
    uint32_t    outputLength,
    unsigned char* output
)
1.0、参考
1.1、此函数的作用

PBKDF2算法

1.2、参数说明

mbedtls_md_context_t的定义:

typedef enum {
    MBEDTLS_MD_NONE=0,    /**< None. */
    MBEDTLS_MD_MD2,       /**< The MD2 message digest. */
    MBEDTLS_MD_MD4,       /**< The MD4 message digest. */
    MBEDTLS_MD_MD5,       /**< The MD5 message digest. */
    MBEDTLS_MD_SHA1,      /**< The SHA-1 message digest. */
    MBEDTLS_MD_SHA224,    /**< The SHA-224 message digest. */
    MBEDTLS_MD_SHA256,    /**< The SHA-256 message digest. */
    MBEDTLS_MD_SHA384,    /**< The SHA-384 message digest. */
    MBEDTLS_MD_SHA512,    /**< The SHA-512 message digest. */
    MBEDTLS_MD_RIPEMD160, /**< The RIPEMD-160 message digest. */
} mbedtls_md_type_t;

typedef struct {
    const char*       name; /** Name of the message digest */
    mbedtls_md_type_t type; /** Digest identifier */
    unsigned char size;      /** Output length of the digest function in bytes */
    unsigned char block_size; /** Block length of the digest function in bytes */
} mbedtls_md_info_t;

typedef struct mbedtls_md_context_t {
    /** Information about the associated message digest. */
    const mbedtls_md_info_t *md_info;
    void *md_ctx;   /** The digest-specific context. */
    void *hmac_ctx; /** The HMAC part of the context. */
} mbedtls_md_context_t;

const unsigned char *password密码字节数据C语言中没有其他语言中的byte类型,C语言中表达byte类型就是用unsigned charunsigned char的意思就是告诉编译器, 不要把我的最高位当成符号位,这样这8bit就都表示真实的数据了。

size_t的定义:

typedef long unsigned int __darwin_size_t;
typedef __darwin_size_t size_t;

size_t passwordLengthconst unsigned char* password的大小,单位是字节

const unsigned char* salt字节数据

size_t saltLengthconst unsigned char* salt的大小,单位是字节

unsigned int iterationCount是迭代次数。迭代次数越多,产生输出的速度越慢,破解的难度也越大。

unsigned char* output输出字节数据。 其长度是uint32_t outputLength,它的最大值为0xFFFFFFFF

1.3、返回值说明

0表示成功。

非0表示失败。错误码定义如下:

#define MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE -0x5080  /** The selected feature is not available. */
#define MBEDTLS_ERR_MD_BAD_INPUT_DATA      -0x5100  /** Bad input parameters to function. */
#define MBEDTLS_ERR_MD_ALLOC_FAILED        -0x5180  /** Failed to allocate memory. */
#define MBEDTLS_ERR_MD_FILE_IO_ERROR       -0x5200  /** Opening or reading of file failed. */

#define MBEDTLS_ERR_PKCS5_BAD_INPUT_DATA   -0x2f80  /** Bad input parameters to function. */
#define MBEDTLS_ERR_PKCS5_INVALID_FORMAT   -0x2f00  /** Unexpected ASN.1 data. */
#define MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE -0x2e80  /** Requested encryption or digest alg not available. */
#define MBEDTLS_ERR_PKCS5_PASSWORD_MISMATCH -0x2e00  /** Given private key password does not allow for correct decryption. */
1.4、使用示例

step1、创建一个项目目录和存放源码的目录PBKDF2/mbedtls,并进入该目录

mkdir -p PBKDF2/mbedtls && cd PBKDF2/mbedtls

step2、使用curl命令下载代码

BRANCH=development
BASE_URL_H=https://raw.githubusercontent.com/ARMmbed/mbedtls/$BRANCH/include/mbedtls
BASE_URL_C=https://raw.githubusercontent.com/ARMmbed/mbedtls/$BRANCH/library

for item in pkcs5.h sha256.h md.h md_internal.h platform_util.h platform.h threading.h error.h
do
    curl -LO "$BASE_URL_H/$item"
done

for item in pkcs5.c sha256.c md.c platform_util.c platform.c threading.c error.c
do
    curl -LO "$BASE_URL_C/$item"
done

step3、编写一个config.h文件,其内容如下

#define MBEDTLS_PKCS5_C
#define MBEDTLS_MD_C
#define MBEDTLS_SHA256_C

注意:此例中,我们只与SHA-256联合使用。要与其他哈希函数联合使用的话,定义对应的,并下载对应的代码。

#define MBEDTLS_MD5_C
md5.h
#define MBEDTLS_RIPEMD160_C
ripemd160.h
#define MBEDTLS_SHA1_C
sha1.h
#define MBEDTLS_SHA256_C
sha256.h
#define MBEDTLS_SHA512_C
sha512.h

step4、将代码中的mbedtls/字符串去掉

sed -i    's@mbedtls/@@g' * 2> /dev/null ||
sed -i "" 's@mbedtls/@@g' *

step5、进入目录PBKDF2

cd ..

step6、编写一个C语言源程序hamcTest.c,其内容如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "mbedtls/pkcs5.h"

#define OUTPUT_LENGTH_IN_BYTES         32
#define BASE_16_OUTPUT_LENGTH_IN_BYTES 32 * 2 + 1
#define ITERATION_COUNT 1000

void showHelp() {
    printf("usage: pbkdf2 <option> <PASSWORD> <SALT>\n");
    printf("option: -x lower case, default true\n");
    printf("        -X upper case\n");
    printf("example:pbkdf2 -X 123456 abcdefg\n");
    exit(1);
}

void base16(unsigned char input[OUTPUT_LENGTH_IN_BYTES], char* output, bool isToUpper) {
    const char* format = isToUpper ? "%02X" : "%02x";
    char tmp[3] = {0};
    for(int i = 0; i < OUTPUT_LENGTH_IN_BYTES; i++) {
        sprintf(tmp, format, input[i]);
        strcat(output, tmp);
    }
}

void performPBKDF2_HMAC_SHA256(char* password, char* salt, bool isToUpper) {
    unsigned char output[OUTPUT_LENGTH_IN_BYTES] = {0};

    mbedtls_md_context_t md_context;
    mbedtls_md_init(&md_context);
    
    extern const mbedtls_md_info_t mbedtls_sha256_info;
    int resultCode = mbedtls_md_setup(&md_context, &mbedtls_sha256_info, 1);
    if (0 == resultCode) {
        resultCode = mbedtls_pkcs5_pbkdf2_hmac(&md_context, (unsigned char*)password, strlen(password), (unsigned char*)salt, strlen(salt), ITERATION_COUNT, OUTPUT_LENGTH_IN_BYTES, output);
        if (0 == resultCode) {
            mbedtls_md_free(&md_context);
            //为了便于比较,通常会把字节数据用base16编码
            char base16Encoded[BASE_16_OUTPUT_LENGTH_IN_BYTES] = {0};
            base16(output, base16Encoded, isToUpper);
            printf("pbkdf2_hmac_sha256(password=%s,salt=%s)=%s\n", password, salt, base16Encoded);
            return;
        }
    }
    printf("occrued error, code is %d\n", resultCode);
}

int main(int argc, char* argv[]) {
    if (4 == argc) {
        if (strcmp("-x", argv[1]) == 0) {
            performPBKDF2_HMAC_SHA256(argv[2], argv[3], false);
        } else if (strcmp("-X", argv[1]) == 0) {
            performPBKDF2_HMAC_SHA256(argv[2], argv[3], true);
        } else {
            showHelp();
        }
    }
    return 0;
}

step7、使用cc命令进行编译

cc -o pbkdf2Test pbkdf2Test.c mbedtls/*.c

step8、运行pbkdf2Test

./pbkdf2Test -x 123456 abc
pbkdf2_hmac_sha256(password=123456,salt=abc)=d4c3fa431418d704a43b48f699c6ee0eecf55754891455d95500a69aa1228efa

./pbkdf2Test -X 123456 abc
pbkdf2_hmac_sha256(password=123456,salt=abc)=D4C3FA431418D704A43B48F699C6EE0EECF55754891455D95500A69AA1228EFA

step9、在浏览器中运行下面的HTML代码,看看结果是否一样:

<script src="https://cdn.bootcdn.net/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
<script>
        var password = '123456';
        var salt = 'abc';
        var iterations = 1000;
        var dklength = 256;
        var key = CryptoJS.PBKDF2(password, salt, {hasher:CryptoJS.algo.SHA256, iterations: iterations, keySize: dklength/32}).toString(CryptoJS.enc.Hex);
        document.write("pbkdf2_hmac_sha256(password=123456,salt=abc)=" + key);
</script>
source code on GitHub