blob: a329e8d52bd418566eba7079ee8286ca2d22d149 [file] [log] [blame]
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <crypto/hmac256.h>
/*
HKDF-Expand(PRK, info, L)
Description in RFC 5869
HKDF-Expand(PRK, info, L) -> OKM
Options:
Hash a hash function; HashLen denotes the length of the
hash function output in octets
Inputs:
PRK a pseudorandom key of at least HashLen octets
(usually, the output from the extract step)
info optional context and application specific information
(can be a zero-length string)
L length of output keying material in octets
(<= 255*HashLen)
Output:
OKM output keying material (of L octets)
The output OKM is calculated as follows:
N = ceil(L/HashLen)
T = T(1) | T(2) | T(3) | ... | T(N)
OKM = first L octets of T
where:
T(0) = empty string (zero length)
T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
...
(where the constant concatenated to the end of each T(n) is a
single octet.)
A.1. Test Case 1
Basic test case with SHA-256
Hash = SHA-256
IKM = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b (22 octets)
salt = 0x000102030405060708090a0b0c (13 octets)
info = 0xf0f1f2f3f4f5f6f7f8f9 (10 octets)
L = 42
PRK = 0x077709362c2e32df0ddc3f0dc47bba63
90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets)
OKM = 0x3cb25f25faacd57a90434f64d0362f2a
2d2d0a90cf1a5a4c5db02d56ecc4c5bf
34007208d5b887185865 (42 octets)
A.2. Test Case 2
Test with SHA-256 and longer inputs/outputs
Hash = SHA-256
IKM = 0x000102030405060708090a0b0c0d0e0f
101112131415161718191a1b1c1d1e1f
202122232425262728292a2b2c2d2e2f
303132333435363738393a3b3c3d3e3f
404142434445464748494a4b4c4d4e4f (80 octets)
salt = 0x606162636465666768696a6b6c6d6e6f
707172737475767778797a7b7c7d7e7f
808182838485868788898a8b8c8d8e8f
909192939495969798999a9b9c9d9e9f
a0a1a2a3a4a5a6a7a8a9aaabacadaeaf (80 octets)
info = 0xb0b1b2b3b4b5b6b7b8b9babbbcbdbebf
c0c1c2c3c4c5c6c7c8c9cacbcccdcecf
d0d1d2d3d4d5d6d7d8d9dadbdcdddedf
e0e1e2e3e4e5e6e7e8e9eaebecedeeef
f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff (80 octets)
L = 82
PRK = 0x06a6b88c5853361a06104c9ceb35b45c
ef760014904671014a193f40c15fc244 (32 octets)
OKM = 0xb11e398dc80327a1c8e7f78c596a4934
4f012eda2d4efad8a050cc4c19afa97c
59045a99cac7827271cb41c65e590e09
da3275600c2f09b8367793a9aca3db71
cc30c58179ec3e87c14c01d5c1f3434f
1d87 (82 octets)
A.3. Test Case 3
Test with SHA-256 and zero-length salt/info
Hash = SHA-256
IKM = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b (22 octets)
salt = (0 octets)
info = (0 octets)
L = 42
PRK = 0x19ef24a32c717b167f33a91d6f648bdf
96596776afdb6377ac434c1c293ccb04 (32 octets)
OKM = 0x8da4e775a563c18f715f802a063c5a31
b8a11f5c5ee1879ec3454e5f3c738d2d
9d201395faa4b61a96c8 (42 octets)
*/
static void hexdump(const char* title, const unsigned char *s, int l)
{
int n=0;
if (s == NULL) return;
fprintf(stderr, "%s",title);
for( ; n < l ; ++n) {
if((n%16) == 0)
fprintf(stderr, "\n%04x",n);
fprintf(stderr, " %02x",s[n]);
}
fprintf(stderr, "\n");
}
static uint8_t info_A1[] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9};
static int32_t L_A1 = 42;
static uint8_t PRK_A1[] = {
0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5};
static uint8_t OKM_A1[] = {
0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a,
0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf,
0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65}; // (42 octets)
static int32_t L_A3 = 42;
static uint8_t PRK_A3[] = {
0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf,
0x96, 0x59, 0x67, 0x76, 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, 0xcb, 0x04}; // (32 octets)
static uint8_t OKM_A3[] = {
0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c, 0x5a, 0x31,
0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d,
0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, 0x96, 0xc8}; // (42 octets)
void* createSha256HmacContext(uint8_t* key, int32_t keyLength);
void freeSha256HmacContext(void* ctx);
void hmacSha256Ctx(void* ctx, const uint8_t* data[], uint32_t dataLength[], uint8_t* mac, int32_t* macLength );
static int expand(uint8_t* prk, uint32_t prkLen, uint8_t* info, int32_t infoLen, int32_t L, uint32_t hashLen, uint8_t* outbuffer)
{
int32_t n;
uint8_t *T;
void* hmacCtx;
const uint8_t* data[4]; // Data pointers for HMAC data, max. 3 plus terminating NULL
uint32_t dataLen[4];
int32_t dataIdx = 0;
uint8_t counter;
int32_t macLength;
if (prkLen < hashLen)
return -1;
n = (L + (hashLen-1)) / hashLen;
// T points to buffer that holds concatenated T(1) || T(2) || ... T(N))
T = reinterpret_cast<uint8_t*>(malloc(n * hashLen));
// Prepare the HMAC
hmacCtx = createSha256HmacContext(prk, prkLen);
// Prepare first HMAC. T(0) has zero length, thus we ignore it in first run.
// After first run use its output (T(1)) as first data in next HMAC run.
for (int i = 1; i <= n; i++) {
if (infoLen > 0 && info != NULL) {
data[dataIdx] = info;
dataLen[dataIdx++] = infoLen;
}
counter = i & 0xff;
data[dataIdx] = &counter;
dataLen[dataIdx++] = 1;
data[dataIdx] = NULL;
dataLen[dataIdx++] = 0;
hmacSha256Ctx(hmacCtx, data, dataLen, T + ((i-1) * hashLen), &macLength);
dataIdx = 0;
data[dataIdx] = T + ((i-1) * hashLen);
dataLen[dataIdx++] = hashLen;
}
freeSha256HmacContext(hmacCtx);
memcpy(outbuffer, T, L);
free(T);
return 0;
}
int main(int argc, char *argv[])
{
uint8_t buffer[500];
expand(PRK_A1, sizeof(PRK_A1), info_A1, sizeof(info_A1), L_A1, SHA256_DIGEST_LENGTH, buffer);
if (memcmp(buffer, OKM_A1, L_A1) != 0) {
fprintf(stderr, "ERROR: Test result A1 mismatch");
hexdump("Computed result of expand A1", buffer, L_A1);
hexdump("Expected result of expand A1", OKM_A1, L_A1);
return 1;
}
expand(PRK_A3, sizeof(PRK_A3), NULL, 0, L_A3, SHA256_DIGEST_LENGTH, buffer);
if (memcmp(buffer, OKM_A3, L_A3) != 0) {
fprintf(stderr, "ERROR: Test result A3 mismatch");
hexdump("Computed result of expand A3", buffer, L_A3);
hexdump("Expected result of expand A3", OKM_A3, L_A3);
return 1;
}
printf("Done\n");
return 0;
}