root/sbin/cgdconfig/hkdf_hmac_sha256.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