HKDF详细请见文章 https://blog.csdn.net/mrpre/article/details/79879392
本文给了一种python实现和三种C实现。
python程序(摘自wiki):
#!/usr/bin/env python3
import hashlib
import hmac
from math import ceil
hash_len = 32
def hmac_sha256(key, data):
return hmac.new(key, data, hashlib.sha256).digest()
def hkdf(length, ikm, salt=b"", info=b""):
prk = hmac_sha256(salt, ikm)
t = b""
okm = b""
for i in range(ceil(length / hash_len)):
t = hmac_sha256(prk, t + info + bytes([1+i]))
okm += t
return okm[:length]
C程序:
OpenSSL_HKDF1()
直接调用了OPENSSL库函数计算完整的HKDF。
OpenSSL_HKDF2()
先调用extract,然后再调用expand,完成一次完整的HKDF。
my_HKDF()
是我按照RFC的逻辑实现的HKDF。
api说明
EVP_PKEY_CTX_add1_hkdf_info
:设置label,expand时需要用到
EVP_PKEY_CTX_set1_hkdf_salt
:设置salt,extract需要用到,可选。
EVP_PKEY_derive
:计算。其中,当时expand时,入参 outlen
需要设置大小,而extract的时候,outlen
不用指定大小。
#include <openssl/ssl.h>
#include <openssl/kdf.h>
#include <string.h>
#define OUT_LEN 90
#define HASH_SIZE 32
#define MD_FUNC EVP_sha256()
#define INFO "test"
#define INFO_LEN 4
void OpenSSL_HKDF1()
{
unsigned char secret[HASH_SIZE]={0};
const EVP_MD *md = MD_FUNC;
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
unsigned char out[OUT_LEN];
size_t outlen = OUT_LEN, i, ret;
ret = EVP_PKEY_derive_init(pctx) <= 0
|| EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND) <= 0
|| EVP_PKEY_CTX_set_hkdf_md(pctx, md) <= 0
|| EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, HASH_SIZE) <= 0
|| EVP_PKEY_CTX_add1_hkdf_info(pctx, INFO, INFO_LEN) <= 0
|| EVP_PKEY_derive(pctx, out, &outlen) <= 0;
EVP_PKEY_CTX_free(pctx);
if (ret == 0)
{
printf("HKDF result:%d\n",outlen);
for(i=0;i<outlen;i++)
printf("%02X", out[i]);
printf("\n");
}
}
void OpenSSL_HKDF2()
{
unsigned char secret[HASH_SIZE]={0};
const EVP_MD *md = MD_FUNC;
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
unsigned char prk[HASH_SIZE], okm[OUT_LEN];
size_t outlen = HASH_SIZE, i, ret;
/*first do extract*/
ret = EVP_PKEY_derive_init(pctx) <= 0
|| EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) <= 0
|| EVP_PKEY_CTX_set_hkdf_md(pctx, md) <= 0
|| EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, HASH_SIZE) <= 0
//|| EVP_PKEY_CTX_add1_hkdf_info(pctx, INFO, INFO_LEN) <= 0
|| EVP_PKEY_derive(pctx, prk, &outlen) <= 0;
if (ret == 0)
{
printf("HKDF extract prk:%d\n",outlen);
for(i=0;i<outlen;i++)
printf("%02X", prk[i]);
printf("\n");
}
EVP_PKEY_CTX_free(pctx);
/*second do expand, using prk as secret*/
outlen = OUT_LEN;
pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
ret = EVP_PKEY_derive_init(pctx) <= 0
|| EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) <= 0
|| EVP_PKEY_CTX_set_hkdf_md(pctx, md) <= 0
|| EVP_PKEY_CTX_set1_hkdf_key(pctx, prk, HASH_SIZE) <= 0
|| EVP_PKEY_CTX_add1_hkdf_info(pctx, INFO, INFO_LEN) <= 0
|| EVP_PKEY_derive(pctx, okm, &outlen) <= 0;
if (ret == 0)
{
printf("HKDF expand okm:%d\n",outlen);
for(i=0;i<outlen;i++)
printf("%02X", okm[i]);
printf("\n");
}
EVP_PKEY_CTX_free(pctx);
}
void my_HKDF()
{
unsigned char secret[HASH_SIZE]={0};
const EVP_MD *md = MD_FUNC;
unsigned char prk[HASH_SIZE], okm[OUT_LEN], T[HASH_SIZE]={0}, tmp[OUT_LEN];
int outlen = HASH_SIZE, i, ret, tmplen;
unsigned char *p;
/*extract is a simple HMAC...
Note:salt should be treated as hmac key and ikm should be treated as data
*/
if (!HMAC(md, NULL, 0, secret, HASH_SIZE, prk, &outlen))
return ;
printf("HKDF extract prk:%d\n",outlen);
for(i=0;i<outlen;i++)
printf("%02X", prk[i]);
printf("\n");
/*do expand*/
/*calc the round times*/
ret = OUT_LEN/HASH_SIZE + !!(OUT_LEN%HASH_SIZE);
tmplen = OUT_LEN;
for (i = 0; i < ret; i++)
{
p = tmp;
/*T(0) = empty string (zero length)*/
if (i != 0)
{
memcpy(p, T, HASH_SIZE);
p += HASH_SIZE;
}
memcpy(p, INFO, INFO_LEN);
p += INFO_LEN;
*p++ = i + 1;
HMAC(md, prk, HASH_SIZE, tmp, (int)(p - tmp), T, &outlen);
memcpy(okm + i*HASH_SIZE, T, tmplen < HASH_SIZE ? tmplen:HASH_SIZE);
tmplen -= HASH_SIZE;
}
printf("HKDF expand okm:%d\n",OUT_LEN);
for(i=0;i<OUT_LEN;i++)
printf("%02X", okm[i]);
printf("\n");
}
void main()
{
my_HKDF();
}