274 lines
7.3 KiB
C
274 lines
7.3 KiB
C
|
/* $NetBSD: hkdf_hmac_sha256.c,v 1.1 2022/08/12 10:49:17 riastradh Exp $ */
|
||
|
|
||
|
/*-
|
||
|
* Copyright (c) 2022 The NetBSD Foundation, Inc.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions
|
||
|
* are met:
|
||
|
* 1. Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer in the
|
||
|
* documentation and/or other materials provided with the distribution.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
#include <sys/cdefs.h>
|
||
|
__RCSID("$NetBSD: hkdf_hmac_sha256.c,v 1.1 2022/08/12 10:49:17 riastradh Exp $");
|
||
|
|
||
|
#include <sys/sha2.h>
|
||
|
|
||
|
#include <stdint.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "hkdf_hmac_sha256.h"
|
||
|
|
||
|
/* RFC 2104: HMAC */
|
||
|
|
||
|
struct hmacsha256 {
|
||
|
SHA256_CTX sha256;
|
||
|
uint8_t key[SHA256_BLOCK_LENGTH];
|
||
|
};
|
||
|
|
||
|
static void
|
||
|
hmacsha256_init(struct hmacsha256 *H, const void *key, size_t keylen)
|
||
|
{
|
||
|
uint8_t hkey[SHA256_DIGEST_LENGTH];
|
||
|
uint8_t ipad[SHA256_BLOCK_LENGTH];
|
||
|
unsigned i;
|
||
|
|
||
|
if (keylen > SHA256_BLOCK_LENGTH) { /* XXX should not happen here */
|
||
|
SHA256_Init(&H->sha256);
|
||
|
SHA256_Update(&H->sha256, key, keylen);
|
||
|
SHA256_Final(hkey, &H->sha256);
|
||
|
key = hkey;
|
||
|
keylen = sizeof(hkey);
|
||
|
}
|
||
|
|
||
|
memset(H->key, 0, sizeof(H->key));
|
||
|
memcpy(H->key, key, keylen);
|
||
|
|
||
|
for (i = 0; i < SHA256_BLOCK_LENGTH; i++)
|
||
|
ipad[i] = 0x36 ^ H->key[i];
|
||
|
|
||
|
SHA256_Init(&H->sha256);
|
||
|
SHA256_Update(&H->sha256, ipad, SHA256_BLOCK_LENGTH);
|
||
|
|
||
|
explicit_memset(hkey, 0, sizeof(hkey));
|
||
|
explicit_memset(ipad, 0, sizeof(ipad));
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
hmacsha256_update(struct hmacsha256 *H, const void *buf, size_t buflen)
|
||
|
{
|
||
|
|
||
|
SHA256_Update(&H->sha256, buf, buflen);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
hmacsha256_final(uint8_t h[static SHA256_DIGEST_LENGTH],
|
||
|
struct hmacsha256 *H)
|
||
|
{
|
||
|
uint8_t opad[SHA256_BLOCK_LENGTH];
|
||
|
unsigned i;
|
||
|
|
||
|
for (i = 0; i < SHA256_BLOCK_LENGTH; i++)
|
||
|
opad[i] = 0x5c ^ H->key[i];
|
||
|
|
||
|
SHA256_Final(h, &H->sha256);
|
||
|
SHA256_Init(&H->sha256);
|
||
|
SHA256_Update(&H->sha256, opad, SHA256_BLOCK_LENGTH);
|
||
|
SHA256_Update(&H->sha256, h, SHA256_DIGEST_LENGTH);
|
||
|
SHA256_Final(h, &H->sha256);
|
||
|
|
||
|
explicit_memset(opad, 0, sizeof(opad));
|
||
|
explicit_memset(H, 0, sizeof(*H));
|
||
|
}
|
||
|
|
||
|
/* RFC 5869 HKDF, Sec. 2.3 HKDF-Expand */
|
||
|
|
||
|
int
|
||
|
hkdf_hmac_sha256(void *okm, size_t L,
|
||
|
const void *prk, size_t prklen,
|
||
|
const void *info, size_t infolen)
|
||
|
{
|
||
|
struct hmacsha256 hmacsha256;
|
||
|
size_t n, tlen;
|
||
|
uint8_t T[SHA256_DIGEST_LENGTH], *p = okm;
|
||
|
uint8_t i;
|
||
|
|
||
|
if (L > 255*SHA256_DIGEST_LENGTH)
|
||
|
return -1;
|
||
|
if (L == 0)
|
||
|
return 0;
|
||
|
|
||
|
for (tlen = 0, i = 1; L > 0; i++, tlen = SHA256_DIGEST_LENGTH) {
|
||
|
hmacsha256_init(&hmacsha256, prk, prklen);
|
||
|
hmacsha256_update(&hmacsha256, T, tlen);
|
||
|
hmacsha256_update(&hmacsha256, info, infolen);
|
||
|
hmacsha256_update(&hmacsha256, &i, 1);
|
||
|
hmacsha256_final(T, &hmacsha256);
|
||
|
n = (L < SHA256_DIGEST_LENGTH ? L : SHA256_DIGEST_LENGTH);
|
||
|
memcpy(p, T, n);
|
||
|
p += n;
|
||
|
L -= n;
|
||
|
}
|
||
|
|
||
|
explicit_memset(T, 0, sizeof(T));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
#include <stdarg.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
static void
|
||
|
hexdump(const void *buf, size_t len, const char *fmt, ...)
|
||
|
{
|
||
|
va_list va;
|
||
|
const uint8_t *p = buf;
|
||
|
size_t i;
|
||
|
|
||
|
printf("### ");
|
||
|
va_start(va, fmt);
|
||
|
vprintf(fmt, va);
|
||
|
va_end(va);
|
||
|
printf(" (%zu bytes):\n", len);
|
||
|
for (i = 0; i < len; i++) {
|
||
|
if (i % 8 == 0)
|
||
|
printf(" ");
|
||
|
printf(" %02x", p[i]);
|
||
|
if (i % 16 == 15)
|
||
|
printf("\n");
|
||
|
}
|
||
|
if (i % 16)
|
||
|
printf("\n");
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int
|
||
|
hkdf_hmac_sha256_selftest(void)
|
||
|
{
|
||
|
const struct {
|
||
|
size_t L;
|
||
|
const uint8_t *okm;
|
||
|
size_t prklen;
|
||
|
const uint8_t *prk;
|
||
|
size_t infolen;
|
||
|
const uint8_t *info;
|
||
|
} C[] = {
|
||
|
[0] = { /* A.1 Test Case 1 with SHA-256 */
|
||
|
.L = 42,
|
||
|
.okm = (const uint8_t[]) {
|
||
|
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,
|
||
|
},
|
||
|
.prklen = 32,
|
||
|
.prk = (const uint8_t[]) {
|
||
|
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,
|
||
|
},
|
||
|
.infolen = 10,
|
||
|
.info = (const uint8_t[]) {
|
||
|
0xf0,0xf1,0xf2,0xf3, 0xf4,0xf5,0xf6,0xf7,
|
||
|
0xf8,0xf9,
|
||
|
},
|
||
|
},
|
||
|
[1] = { /* A.2 Test Case 2 with SHA-256, longer I/O */
|
||
|
.L = 82,
|
||
|
.okm = (const uint8_t[]) {
|
||
|
0xb1,0x1e,0x39,0x8d, 0xc8,0x03,0x27,0xa1,
|
||
|
0xc8,0xe7,0xf7,0x8c, 0x59,0x6a,0x49,0x34,
|
||
|
0x4f,0x01,0x2e,0xda, 0x2d,0x4e,0xfa,0xd8,
|
||
|
0xa0,0x50,0xcc,0x4c, 0x19,0xaf,0xa9,0x7c,
|
||
|
0x59,0x04,0x5a,0x99, 0xca,0xc7,0x82,0x72,
|
||
|
0x71,0xcb,0x41,0xc6, 0x5e,0x59,0x0e,0x09,
|
||
|
0xda,0x32,0x75,0x60, 0x0c,0x2f,0x09,0xb8,
|
||
|
0x36,0x77,0x93,0xa9, 0xac,0xa3,0xdb,0x71,
|
||
|
0xcc,0x30,0xc5,0x81, 0x79,0xec,0x3e,0x87,
|
||
|
0xc1,0x4c,0x01,0xd5, 0xc1,0xf3,0x43,0x4f,
|
||
|
0x1d,0x87,
|
||
|
},
|
||
|
.prklen = 32,
|
||
|
.prk = (const uint8_t[]) {
|
||
|
0x06,0xa6,0xb8,0x8c, 0x58,0x53,0x36,0x1a,
|
||
|
0x06,0x10,0x4c,0x9c, 0xeb,0x35,0xb4,0x5c,
|
||
|
0xef,0x76,0x00,0x14, 0x90,0x46,0x71,0x01,
|
||
|
0x4a,0x19,0x3f,0x40, 0xc1,0x5f,0xc2,0x44,
|
||
|
},
|
||
|
.infolen = 80,
|
||
|
.info = (const uint8_t[]) {
|
||
|
0xb0,0xb1,0xb2,0xb3, 0xb4,0xb5,0xb6,0xb7,
|
||
|
0xb8,0xb9,0xba,0xbb, 0xbc,0xbd,0xbe,0xbf,
|
||
|
0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,
|
||
|
0xc8,0xc9,0xca,0xcb, 0xcc,0xcd,0xce,0xcf,
|
||
|
0xd0,0xd1,0xd2,0xd3, 0xd4,0xd5,0xd6,0xd7,
|
||
|
0xd8,0xd9,0xda,0xdb, 0xdc,0xdd,0xde,0xdf,
|
||
|
0xe0,0xe1,0xe2,0xe3, 0xe4,0xe5,0xe6,0xe7,
|
||
|
0xe8,0xe9,0xea,0xeb, 0xec,0xed,0xee,0xef,
|
||
|
0xf0,0xf1,0xf2,0xf3, 0xf4,0xf5,0xf6,0xf7,
|
||
|
0xf8,0xf9,0xfa,0xfb, 0xfc,0xfd,0xfe,0xff,
|
||
|
},
|
||
|
},
|
||
|
[2] = { /* A.3 Test Case 3 with SHA-256, empty info */
|
||
|
.L = 42,
|
||
|
.okm = (const uint8_t[]) {
|
||
|
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,
|
||
|
},
|
||
|
.prklen = 32,
|
||
|
.prk = (const uint8_t[]) {
|
||
|
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,
|
||
|
},
|
||
|
.infolen = 0,
|
||
|
.info = NULL,
|
||
|
},
|
||
|
};
|
||
|
uint8_t okm[128];
|
||
|
unsigned i;
|
||
|
|
||
|
for (i = 0; i < __arraycount(C); i++) {
|
||
|
if (hkdf_hmac_sha256(okm, C[i].L, C[i].prk, C[i].prklen,
|
||
|
C[i].info, C[i].infolen))
|
||
|
return -1;
|
||
|
if (memcmp(okm, C[i].okm, C[i].L))
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
int
|
||
|
main(void)
|
||
|
{
|
||
|
return hkdf_hmac_sha256_selftest();
|
||
|
}
|
||
|
#endif
|