#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)
{
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));
+}
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
#include "sha-common.h"
#include "buffer.h"
+#include "hex-binary.h"
+
struct test_vector {
const char *prf;
const unsigned char *key;
}
};
+/* 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");
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();
}