aboutsummaryrefslogtreecommitdiff
path: root/src/hkdf.c
blob: cff7d60ed43f0691e0ade94c99df9e90344e917c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/*
* Copyright (c) 2024 Vaughn Nugent
*
* Package: noscrypt
* File: hkdf.c
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or  (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with noscrypt. If not, see http://www.gnu.org/licenses/.
*/


#include "hkdf.h"

#define HKDF_MIN(a, b) (a < b ? a : b)

STATIC_ASSERT(HKDF_IN_BUF_SIZE > SHA256_DIGEST_SIZE, "HDK Buffer must be at least the size of the underlying hashing alg output")

static _nc_fn_inline void debugValidateHandler(const struct nc_hkdf_fn_cb_struct* handler)
{
	DEBUG_ASSERT(handler != NULL)
	DEBUG_ASSERT(handler->update != NULL)
	DEBUG_ASSERT(handler->finish != NULL)
}

/*
* The following functions implements the HKDF expand function using an existing
* HMAC function.
*
* This follows the guidence from RFC 5869: https://tools.ietf.org/html/rfc5869
*/

cstatus_t hkdfExpandProcess(
	const struct nc_hkdf_fn_cb_struct* handler,
	void* ctx,
	const cspan_t* info,
	span_t* okm
)
{
	cstatus_t result;

	uint8_t counter;
	uint32_t tLen, okmOffset;
	uint8_t t[HKDF_IN_BUF_SIZE];
	cspan_t tSpan, counterSpan;

	debugValidateHandler(handler);

	ncCryptoSecureZero(t, sizeof(t));

	tLen = 0;				/* T(0) is an empty string(zero length) */
	okmOffset = 0;
	counter = 1;			/* counter is offset by 1 for init */
	result = CSTATUS_FAIL;	/* Start in fail state */

	/* counter as a span */
	ncSpanInitC(&counterSpan, &counter, sizeof(counter));

	/* Compute T(N) = HMAC(prk, T(n-1) | info | n) */
	while (okmOffset < okm->size)
	{
		ncSpanInitC(&tSpan, t, tLen);

		if (handler->update(ctx, &tSpan) != CSTATUS_OK)
		{
			goto Exit;
		}

		if (handler->update(ctx, info) != CSTATUS_OK)
		{
			goto Exit;
		}

		if (handler->update(ctx, &counterSpan) != CSTATUS_OK)
		{
			goto Exit;
		}

		/* 
		* Write current hash state to t buffer. It is known
		* that the t buffer must be at least the size of the
		* underlying hash function output.
		*/
		if (handler->finish(ctx, t) != CSTATUS_OK)
		{
			goto Exit;
		}

		/* tlen becomes the hash size or remaining okm size */
		tLen = HKDF_MIN(okm->size - okmOffset, SHA256_DIGEST_SIZE);

		DEBUG_ASSERT(tLen <= sizeof(t));

		/* write the T buffer back to okm */
		ncSpanWrite(*okm, okmOffset, t, tLen);

		/* shift base okm pointer by T */
		okmOffset += tLen;

		/* increment counter */
		counter++;
	}

	result = CSTATUS_OK;	/* HMAC operation completed, so set success */

Exit:

	return result;
}