]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: hmac - Add hkdf for key derivation
authorAki Tuomi <aki.tuomi@open-xchange.com>
Thu, 22 Aug 2019 15:43:11 +0000 (18:43 +0300)
committerVille Savolainen <ville.savolainen@dovecot.fi>
Mon, 23 Sep 2019 05:47:48 +0000 (08:47 +0300)
src/lib/hmac.c
src/lib/hmac.h
src/lib/test-hmac.c

index 8424e9a3e35e343c2dabec62714f3d3500cf46d9..6d743c961dccb99d81920123862f5df49a7a5355 100644 (file)
@@ -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));
+}
index 8b0a2806d1577b1f7bb6ee0e36c87242b5530299..cf1b2f57070b3664e6ba9a35a669f97e75d538fb 100644 (file)
@@ -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
index 4e2d8298b103ff8270de0c3eed963ed435e78f07..3c52faa0494221d6e183f771affa1aac8d615093 100644 (file)
@@ -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();
 }