/* $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 __RCSID("$NetBSD: hkdf_hmac_sha256.c,v 1.1 2022/08/12 10:49:17 riastradh Exp $"); #include #include #include #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 #include 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