From 9993f252773afbc3b1ebd6c57f4deddcef6bc7fc Mon Sep 17 00:00:00 2001 From: Aki Tuomi Date: Thu, 22 Aug 2019 18:43:11 +0300 Subject: [PATCH] lib: hmac - Add hkdf for key derivation --- src/lib/hmac.c | 57 +++++++++++++++++++++++++++++++++++ src/lib/hmac.h | 19 ++++++++++++ src/lib/test-hmac.c | 73 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+) diff --git a/src/lib/hmac.c b/src/lib/hmac.c index 8424e9a3e3..6d743c961d 100644 --- a/src/lib/hmac.c +++ b/src/lib/hmac.c @@ -12,6 +12,8 @@ #include "safe-memset.h" #include "buffer.h" +#include "hex-binary.h" + void hmac_init(struct hmac_context *_ctx, const unsigned char *key, size_t key_len, const struct hash_method *meth) { @@ -93,3 +95,58 @@ buffer_t *t_hmac_str(const struct hash_method *meth, return t_hmac_data(meth, key, key_len, data, strlen(data)); } +void hmac_hkdf(const struct hash_method *method, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + const unsigned char *info, size_t info_len, + buffer_t *okm_r, size_t okm_len) +{ + i_assert(method != NULL); + i_assert(okm_len < 255*method->digest_size); + struct hmac_context key_mac; + struct hmac_context info_mac; + size_t remain = okm_len; + unsigned char prk[method->digest_size]; + unsigned char okm[method->digest_size]; + /* N = ceil(L/HashLen) */ + unsigned int rounds = (okm_len + method->digest_size - 1)/method->digest_size; + + /* salt and info can be NULL */ + i_assert(salt != NULL || salt_len == 0); + i_assert(info != NULL || info_len == 0); + + i_assert(ikm != NULL && ikm_len > 0); + i_assert(okm_r != NULL && okm_len > 0); + + /* but they still need valid pointer, reduces + complains from static analysers */ + if (salt == NULL) + salt = &uchar_nul; + if (info == NULL) + info = &uchar_nul; + + /* extract */ + hmac_init(&key_mac, salt, salt_len, method); + hmac_update(&key_mac, ikm, ikm_len); + hmac_final(&key_mac, prk); + + /* expand */ + for (unsigned int i = 0; remain > 0 && i < rounds; i++) { + unsigned char round = (i+1); + size_t amt = remain; + if (amt > method->digest_size) + amt = method->digest_size; + hmac_init(&info_mac, prk, method->digest_size, method); + if (i > 0) + hmac_update(&info_mac, okm, method->digest_size); + hmac_update(&info_mac, info, info_len); + hmac_update(&info_mac, &round, 1); + memset(okm, 0, method->digest_size); + hmac_final(&info_mac, okm); + buffer_append(okm_r, okm, amt); + remain -= amt; + } + + safe_memset(prk, 0, sizeof(prk)); + safe_memset(okm, 0, sizeof(okm)); +} diff --git a/src/lib/hmac.h b/src/lib/hmac.h index 8b0a2806d1..cf1b2f5707 100644 --- a/src/lib/hmac.h +++ b/src/lib/hmac.h @@ -42,4 +42,23 @@ buffer_t *t_hmac_str(const struct hash_method *meth, const unsigned char *key, size_t key_len, const char *data); +void hmac_hkdf(const struct hash_method *method, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + const unsigned char *info, size_t info_len, + buffer_t *okm_r, size_t okm_len); + +static inline buffer_t * +t_hmac_hkdf(const struct hash_method *method, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + const unsigned char *info, size_t info_len, + size_t okm_len) +{ + buffer_t *okm_buffer = t_buffer_create(okm_len); + hmac_hkdf(method, salt, salt_len, ikm, ikm_len, info, info_len, + okm_buffer, okm_len); + return okm_buffer; +} + #endif diff --git a/src/lib/test-hmac.c b/src/lib/test-hmac.c index 4e2d8298b1..3c52faa049 100644 --- a/src/lib/test-hmac.c +++ b/src/lib/test-hmac.c @@ -6,6 +6,8 @@ #include "sha-common.h" #include "buffer.h" +#include "hex-binary.h" + struct test_vector { const char *prf; const unsigned char *key; @@ -65,6 +67,46 @@ static const struct test_vector test_vectors[] = { } }; +/* RFC 5869 test vectors */ + +static const struct test_vector_5869 { + const char *prf; + const unsigned char *ikm; + size_t ikm_len; + const unsigned char *salt; + size_t salt_len; + const unsigned char *info; + size_t info_len; + const unsigned char *okm; + size_t okm_len; +} test_vectors_5869[] = { + { "sha256", + TEST_BUF("\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"), + TEST_BUF("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c"), + TEST_BUF("\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9"), + TEST_BUF("\x3c\xb2\x5f\x25\xfa\xac\xd5\x7a\x90\x43\x4f\x64\xd0\x36\x2f\x2a\x2d\x2d\x0a\x90\xcf\x1a\x5a\x4c\x5d\xb0\x2d\x56\xec\xc4\xc5\xbf\x34\x00\x72\x08\xd5\xb8\x87\x18\x58\x65") + }, + { "sha256", + TEST_BUF("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"), + TEST_BUF("\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"), + TEST_BUF("\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"), + TEST_BUF("\xb1\x1e\x39\x8d\xc8\x03\x27\xa1\xc8\xe7\xf7\x8c\x59\x6a\x49\x34\x4f\x01\x2e\xda\x2d\x4e\xfa\xd8\xa0\x50\xcc\x4c\x19\xaf\xa9\x7c\x59\x04\x5a\x99\xca\xc7\x82\x72\x71\xcb\x41\xc6\x5e\x59\x0e\x09\xda\x32\x75\x60\x0c\x2f\x09\xb8\x36\x77\x93\xa9\xac\xa3\xdb\x71\xcc\x30\xc5\x81\x79\xec\x3e\x87\xc1\x4c\x01\xd5\xc1\xf3\x43\x4f\x1d\x87") + }, + { "sha256", + TEST_BUF("\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"), + TEST_BUF(""), + TEST_BUF(""), + TEST_BUF("\x8d\xa4\xe7\x75\xa5\x63\xc1\x8f\x71\x5f\x80\x2a\x06\x3c\x5a\x31\xb8\xa1\x1f\x5c\x5e\xe1\x87\x9e\xc3\x45\x4e\x5f\x3c\x73\x8d\x2d\x9d\x20\x13\x95\xfa\xa4\xb6\x1a\x96\xc8") + }, + /* should be equal to above */ + { "sha256", + TEST_BUF("\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"), + NULL, 0, + NULL, 0, + TEST_BUF("\x8d\xa4\xe7\x75\xa5\x63\xc1\x8f\x71\x5f\x80\x2a\x06\x3c\x5a\x31\xb8\xa1\x1f\x5c\x5e\xe1\x87\x9e\xc3\x45\x4e\x5f\x3c\x73\x8d\x2d\x9d\x20\x13\x95\xfa\xa4\xb6\x1a\x96\xc8") + }, +}; + static void test_hmac_rfc(void) { test_begin("hmac sha256 rfc4231 vectors"); @@ -96,8 +138,39 @@ static void test_hmac_buffer(void) test_end(); } +static void test_hkdf_rfc(void) +{ + test_begin("hkdf sha256 rfc5869 vectors"); + buffer_t *res = t_buffer_create(82); + for(size_t i = 0; i < N_ELEMENTS(test_vectors_5869); i++) { + buffer_set_used_size(res, 0); + const struct test_vector_5869 *vec = &(test_vectors_5869[i]); + const struct hash_method *m = hash_method_lookup(vec->prf); + hmac_hkdf(m, vec->salt, vec->salt_len, vec->ikm, vec->ikm_len, + vec->info, vec->info_len, res, vec->okm_len); + test_assert_idx(memcmp(res->data, vec->okm, vec->okm_len) == 0, i); + } + + test_end(); +} + +static void test_hkdf_buffer(void) +{ + test_begin("hkdf temporary buffer"); + const struct test_vector_5869 *vec = &(test_vectors_5869[0]); + const struct hash_method *m = hash_method_lookup(vec->prf); + buffer_t *tmp = t_hmac_hkdf(m, vec->salt, vec->salt_len, vec->ikm, + vec->ikm_len, vec->info, vec->info_len, + vec->okm_len); + test_assert(tmp->used == vec->okm_len && + memcmp(tmp->data, vec->okm, vec->okm_len) == 0); + test_end(); +} + void test_hmac(void) { test_hmac_rfc(); test_hmac_buffer(); + test_hkdf_rfc(); + test_hkdf_buffer(); } -- 2.47.3