1281681Srpaulo/*
2346981Scy * HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869)
3346981Scy * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi>
4281681Srpaulo *
5281681Srpaulo * This software may be distributed under the terms of the BSD license.
6281681Srpaulo * See README for more details.
7281681Srpaulo */
8281681Srpaulo
9281681Srpaulo#include "includes.h"
10281681Srpaulo
11281681Srpaulo#include "common.h"
12281681Srpaulo#include "sha256.h"
13281681Srpaulo
14281681Srpaulo
15281681Srpaulo/**
16281681Srpaulo * hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295)
17281681Srpaulo * @secret: Key for KDF
18281681Srpaulo * @secret_len: Length of the key in bytes
19346981Scy * @label: A unique label for each purpose of the KDF or %NULL to select
20346981Scy *	RFC 5869 HKDF-Expand() with arbitrary seed (= info)
21281681Srpaulo * @seed: Seed value to bind into the key
22281681Srpaulo * @seed_len: Length of the seed
23281681Srpaulo * @out: Buffer for the generated pseudo-random key
24281681Srpaulo * @outlen: Number of bytes of key to generate
25281681Srpaulo * Returns: 0 on success, -1 on failure.
26281681Srpaulo *
27281681Srpaulo * This function is used to derive new, cryptographically separate keys from a
28346981Scy * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
29346981Scy * with label = NULL and seed = info, this matches HKDF-Expand() defined in
30346981Scy * RFC 5869, Chapter 2.3.
31281681Srpaulo */
32281681Srpauloint hmac_sha256_kdf(const u8 *secret, size_t secret_len,
33281681Srpaulo		    const char *label, const u8 *seed, size_t seed_len,
34281681Srpaulo		    u8 *out, size_t outlen)
35281681Srpaulo{
36281681Srpaulo	u8 T[SHA256_MAC_LEN];
37281681Srpaulo	u8 iter = 1;
38281681Srpaulo	const unsigned char *addr[4];
39281681Srpaulo	size_t len[4];
40281681Srpaulo	size_t pos, clen;
41281681Srpaulo
42281681Srpaulo	addr[0] = T;
43281681Srpaulo	len[0] = SHA256_MAC_LEN;
44346981Scy	if (label) {
45346981Scy		addr[1] = (const unsigned char *) label;
46346981Scy		len[1] = os_strlen(label) + 1;
47346981Scy	} else {
48346981Scy		addr[1] = (const u8 *) "";
49346981Scy		len[1] = 0;
50346981Scy	}
51281681Srpaulo	addr[2] = seed;
52281681Srpaulo	len[2] = seed_len;
53281681Srpaulo	addr[3] = &iter;
54281681Srpaulo	len[3] = 1;
55281681Srpaulo
56281681Srpaulo	if (hmac_sha256_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
57281681Srpaulo		return -1;
58281681Srpaulo
59281681Srpaulo	pos = 0;
60281681Srpaulo	for (;;) {
61281681Srpaulo		clen = outlen - pos;
62281681Srpaulo		if (clen > SHA256_MAC_LEN)
63281681Srpaulo			clen = SHA256_MAC_LEN;
64281681Srpaulo		os_memcpy(out + pos, T, clen);
65281681Srpaulo		pos += clen;
66281681Srpaulo
67281681Srpaulo		if (pos == outlen)
68281681Srpaulo			break;
69281681Srpaulo
70281681Srpaulo		if (iter == 255) {
71281681Srpaulo			os_memset(out, 0, outlen);
72351611Scy			forced_memzero(T, SHA256_MAC_LEN);
73281681Srpaulo			return -1;
74281681Srpaulo		}
75281681Srpaulo		iter++;
76281681Srpaulo
77281681Srpaulo		if (hmac_sha256_vector(secret, secret_len, 4, addr, len, T) < 0)
78281681Srpaulo		{
79281681Srpaulo			os_memset(out, 0, outlen);
80351611Scy			forced_memzero(T, SHA256_MAC_LEN);
81281681Srpaulo			return -1;
82281681Srpaulo		}
83281681Srpaulo	}
84281681Srpaulo
85351611Scy	forced_memzero(T, SHA256_MAC_LEN);
86281681Srpaulo	return 0;
87281681Srpaulo}
88