使用 libTomCrypt 实现 AES、HMAC、HKDF 运算
Qidi 2017.11.06 (Markdown & Haroopad)
1、libTomCrypt 简介
libTomCrypt 是一个使用 C 语言编写的开源加解密算法库,使用 WTF 协议进行授权,支持 AES、HMAC、HKDF、RSA、PRNG、BASE64 等常用加解密算法。
2、基础文件
我们在使用 libTomCrypt 时会发现,调用某一类算法时总是要依赖几个特定头文件和源文件。我称这些头文件/源文件为该类算法的基础文件。
对于 AES 算法来说,这些基础文件是:
tomcrypt.h tomcrypt_argchk.h tomcrypt_cipher.h
aes.c aes_tab.c compare_testvector.c
crypt_argchk.c crypt_cipher_descriptor.c crypt_cipher_is_valid.c
crypt_find_cipher.c crypt_register_cipher.c crypt_unregister_cipher.c
zeromem.c
对于 HMAC 和 HKDF 算法来说,这些基础文件是:
tomcrypt.h tomcrypt_argchk.h tomcrypt_cipher.h
aes.c aes_tab.c compare_testvector.c
crypt_argchk.c crypt_hash_descriptor.c crypt_hash_is_valid.c
crypt_find_hash.c crypt_register_hash.c crypt_unregister_hash.c
zeromem.c
3、AES 算法调用流程
使用 AES 算法时一般可以分为以下几个步骤:
- 注册算法描述符:
register_cipher(&aes_desc);
- 获取算法描述符:
idx = find_cipher("aes");
- 初始化算法结构体:
XXX_start(idx, IV,
key, key_len,
num_rounds,
symmetric_XXX *XXX);
- 加密:
XXX_encrypt(*plain_text, *crypted_text,
len, symmetric_XXX *XXX);
- 解密:
XXX_decrypt(*crypted_text, *plain_text,
len, symmetric_XXX *XXX);
- 结束运算:
XXX_done(symmetric_XXX *XXX);
- 释放资源:
zeromem(key, sizeof(key));
zeromem(XXX, sizeof(XXX));
3.1、实现 AES-128-ECB 加解密
要实现 aes-128-ecb
加解密,除了要包含(#include)和引用(Makefile 中添加)第 2 节中提到的文件外,还要引用几个 ecb
相关的源文件,如下:
ecb_decrypt.c ecb_done.c
ecb_encrypt.c ecb_start.c
工程中截取的代码片段如下:
...
symmetric_ECB ecb;
unsigned char key[MAXBLOCKSIZE] = "1234567890abcdef" \
"1234567890abcdef" \
"1234567890abcdef" \
"1234567890abcdef" \
"1234567890abcdef" \
"1234567890abcdef" \
"1234567890abcdef" \
"1234567890abcde";
unsigned char pt[4096] = "Keep it simple and stupid."; // plain text for encrypting, place your own content here
unsigned char ct[4096];
if (register_cipher(&aes_desc) < 0) {
ALOGE("register_cipher() failed!\n");
goto exit1;
}
int idx = find_cipher("aes");
if (idx == -1) {
ALOGE("find_cipher() failed!\n");
goto exit1;
}
if (CRYPT_OK != ecb_start(idx, key, cipher_descriptor[idx].min_key_length, 0, &ecb)) {
ALOGE("ecb_start() failed!\n");
goto exit1;
}
if (CRYPT_OK != ecb_encrypt(pt, ct, sizeof(pt), &ecb)) {
ALOGE("ecb_encrypt() failed!\n");
goto exit1;
}
#if SHOW_DECRYPT
if (CRYPT_OK != ecb_decrypt(ct, at, sizeof(at), &ecb)) {
LOG("ecb_decrypt() failed!\n");
goto exit1;
}
#endif
if (CRYPT_OK != ecb_done(&ecb)) {
LOG("ecb_done() failed!\n");
goto exit1;
}
...
exit1:
...
3.2、实现 AES-128-CTR 加解密
要实现 aes-128-ctr
加解密,除了要包含(#include)和引用(Makefile 中添加)第 2 节中提到的文件外,还要引用几个 ctr
相关的源文件,如下:
ctr_decrypt.c ctr_done.c
ctr_encrypt.c ctr_start.c
工程中截取的代码片段如下:
...
if (register_cipher(&aes_desc) < 0) {
ALOGE("Qidi - aes-128-ctr register_cipher() failed!\n");
goto error;
}
idx = find_cipher("aes");
if (idx == -1) {
ALOGE("Qidi - aes-128-ctr find_cipher() failed!\n");
goto error;
}
err = ctr_start(idx, session_iv, session_key, 16, 0, CTR_COUNTER_BIG_ENDIAN, &ctr);
if (err != CRYPT_OK) {
ALOGE("Qidi - ctr_start() failed!\n");
goto error;
}
err = ctr_encrypt((unsigned char*)input_raw, buf, 16, &ctr);
if (err != CRYPT_OK) {
ALOGE("Qidi - ctr_encrypt() failed!\n");
goto error;
}
// ctr 解密前需要再 start 初始化一次
err = ctr_start(idx, session_iv, session_key, 16, 0, CTR_COUNTER_BIG_ENDIAN, &ctr);
if (err != CRYPT_OK) {
ALOGE("Qidi - ctr_start() failed!\n");
goto error;
}
memset(input_raw, 0, 16);
err = ctr_decrypt((unsigned char *)output_raw, (unsigned char *)input_raw, 16, &ctr);
if (err != CRYPT_OK) {
ALOGE("Qidi - ERROR: ctr_decrypt() failed!\n");
goto error;
}
ctr_done(&ctr);
...
error:
...
4、HASH 算法一般调用流程
使用 HASH 算法时,一般可以分为以下几个步骤:
- 注册算法描述符:
register_hash(&aes_desc);
- 获取算法描述符:
idx = find_hash("aes");
- 初始化算法结构体:
XXX_start(idx, IV,
key, key_len,
num_rounds,
symmetric_XXX *XXX);
- 加密:
XXX_encrypt(*plain_text, *crypted_text,
len, symmetric_XXX *XXX);
- 解密:
XXX_decrypt(*crypted_text, *plain_text,
len, symmetric_XXX *XXX);
- 结束运算:
XXX_done(symmetric_XXX *XXX);
- 释放资源:
zeromem(key, sizeof(key));
zeromem(XXX, sizeof(XXX));
4.1、实现 HMAC 运算
要实现 hmac
加解密,除了要包含(#include)和引用(Makefile 中添加)第 2 节中提到的文件外,还要引用几个 hmac
相关的源文件,如下:
hmac_init.c hmac_process.c
hmac_memory.c hash_memory.c
hmac_done.c sha256.c
工程中截取的代码片段如下:
...
if (register_hash(&sha256_desc) < 0) {
ALOGE("Qidi - ERROR: verify fw_resp register_hash(&sha256_desc) failed!\n");
goto error;
}
int idx = find_hash("sha256");
if (idx < 0) {
ALOGE("Qidi - ERROR: verify fw_resp find_hash(sha256) failed!\n");
goto error;
}
if (CRYPT_OK != hmac_memory(idx, (unsigned char*)pre_key, 32,
(unsigned char*)in_data, 36,
(unsigned char*)fw_resp, &fw_resp_len)) {
ALOGE("ERROR: verify fw_resp hmac_memory() failed!\n");
goto error;
}
...
error:
...
其中 hmac_memory()
函数实际上是对 hmac_init()
、hmac_process
、hmac_done()
的封装。
4.2、实现 HKDF 运算
要实现 hkdf
加解密,除了要包含(#include)和引用(Makefile 中添加)第 2 节中提到的文件外,还要引用几个 hkdf
相关的源文件,如下:
hkdf.c sha256.c
工程中截取的代码片段如下:
...
register_hash(&sha256_desc);
int idx = find_hash("sha256");
ret = hkdf(idx, (unsigned char*)salt, 20,
(unsigned char*)info, 20, (unsigned char*)in_data, 18, session_key_iv, 32);
...
其中 hkdf()
函数实际上是对 hkdf_extract()
和 hkdf_expand()
的封装。