ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
hkdf.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4#ifndef PROTOCORE_HKDF_H
5#define PROTOCORE_HKDF_H
6
7#include "protocore_config.h" // the entry point: protocore_types.h for the widths
8
10
11/**
12 * @file hkdf.h
13 * @brief HKDF-SHA256 (RFC 5869) and TLS 1.3 HKDF-Expand-Label (RFC 8446 sec 7.1).
14 *
15 * QUIC packet protection keys are derived with the TLS 1.3 key schedule (RFC 9001 sec 5.2):
16 * an Initial secret is HKDF-Extract'd from a fixed salt and the client's Destination Connection
17 * ID, and every packet-protection value (key / iv / hp) is an HKDF-Expand-Label of a traffic
18 * secret. This is the same HMAC-SHA256 the SSH transport already ships, so these entries are a
19 * thin layer over the @ref HmacSha256Ns entries rather than a second HMAC.
20 *
21 * Pure, zero heap, host-tested against the RFC 9001 Appendix A worked examples (the HkdfLabel
22 * byte strings and the derived client/server secrets).
23 *
24 * @ref HkdfNs::expand caps out_len at 255*PROTOCORE_HKDF_HASH_LEN, the point past which the single-octet
25 * block counter has no encoding: out is zeroed and @ref HkdfNs::ok comes back false.
26 *
27 * @c work is PROTOCORE_HKDF_BORROW secure bytes the CALLER took, at an address it knows. It is not held past the call,
28 * so nothing here aliases it. The caller releases it, and the pool wipes on release; this module neither takes it,
29 * holds it, releases it, nor wipes it. The borrow carries the PRK and the T(i) block, so two derivations in flight are
30 * two borrows and never collide.
31 *
32 * @author Douglas Quigg (dstroy0)
33 * @date 2026
34 */
35
36/** @brief HKDF-SHA256 output block length (== SHA-256 digest length). */
37#define PROTOCORE_HKDF_HASH_LEN 32
38
39/** @brief The RFC 8446 sec 7.1 HKDF-Expand-Label prefix used by TLS 1.3 and QUIC. DTLS 1.3 overrides
40 * it with "dtls13" (RFC 9147 sec 5.9); callers that need it pass it explicitly. */
41#define PROTOCORE_HKDF_LABEL_PREFIX "tls13 "
42
43/** @brief Dispatch table. Addressed by offset, so the layout is asserted below. */
44typedef struct
45{
46 proto_bool (*extract)(uint8_t *, const uint8_t *, size_t, const uint8_t *, size_t, uint8_t *);
47 proto_bool (*expand)(uint8_t *, const uint8_t *, const uint8_t *, size_t, uint8_t *, size_t);
48 proto_bool (*expand_label)(uint8_t *, const uint8_t *, const char *, uint8_t *, size_t, const char *);
49 proto_bool (*expand_label_ctx)(uint8_t *, const uint8_t *, const char *, const uint8_t *, size_t, uint8_t *, size_t,
50 const char *);
51} HkdfNs;
52PROTOCORE_NS_LAYOUT(HkdfNs, extract, expand, expand_label, expand_label_ctx);
53
54/**
55 * @brief PRK = HMAC-SHA256(salt, ikm) (RFC 5869 sec 2.2).
56 * @param work PROTOCORE_HKDF_BORROW bytes the caller took. Not held past the call.
57 * @param salt salt bytes; NULL only when salt_len is 0
58 * @param salt_len salt length
59 * @param ikm input keying material
60 * @param ikm_len its length
61 * @param prk PROTOCORE_HKDF_HASH_LEN bytes
62 * @return PROTO_TRUE on success.
63 */
64proto_bool protocore_hkdf_extract(uint8_t *work, const uint8_t *salt, size_t salt_len, const uint8_t *ikm,
65 size_t ikm_len, uint8_t *prk);
66/**
67 * @brief OKM = T(1) | T(2) | ..., info taken verbatim (RFC 5869 sec 2.3).
68 * @param work PROTOCORE_HKDF_BORROW bytes the caller took. Not held past the call.
69 * @param prk PROTOCORE_HKDF_HASH_LEN bytes from extract
70 * @param info context taken verbatim; NULL only when info_len is 0
71 * @param info_len its length
72 * @param out output keying material
73 * @param out_len bytes requested; past 255*PROTOCORE_HKDF_HASH_LEN out is zeroed instead
74 * @return PROTO_TRUE on success.
75 */
76proto_bool protocore_hkdf_expand(uint8_t *work, const uint8_t *prk, const uint8_t *info, size_t info_len, uint8_t *out,
77 size_t out_len);
78/**
79 * @brief Expand under an HkdfLabel with an empty context.
80 * @param work PROTOCORE_HKDF_BORROW bytes the caller took. Not held past the call.
81 * @param secret traffic secret (HKDF PRK), PROTOCORE_HKDF_HASH_LEN bytes
82 * @param label ASCII label without the prefix, <= 249 bytes
83 * @param out output keying material
84 * @param out_len bytes requested
85 * @param label_prefix PROTOCORE_HKDF_LABEL_PREFIX, or "dtls13" for DTLS 1.3
86 * @return PROTO_TRUE on success.
87 */
88proto_bool protocore_hkdf_expand_label(uint8_t *work, const uint8_t *secret, const char *label, uint8_t *out,
89 size_t out_len, const char *label_prefix);
90/**
91 * @brief Expand under an HkdfLabel carrying a context, the Derive-Secret form.
92 * @param work PROTOCORE_HKDF_BORROW bytes the caller took. Not held past the call.
93 * @param secret PRK, PROTOCORE_HKDF_HASH_LEN bytes
94 * @param label ASCII label without the prefix, <= 249 bytes
95 * @param context context bytes, <= 255; NULL only when context_len is 0
96 * @param context_len context length
97 * @param out output keying material
98 * @param out_len bytes requested
99 * @param label_prefix PROTOCORE_HKDF_LABEL_PREFIX, or "dtls13" for DTLS 1.3
100 * @return PROTO_TRUE on success.
101 */
102proto_bool protocore_hkdf_expand_label_ctx(uint8_t *work, const uint8_t *secret, const char *label,
103 const uint8_t *context, size_t context_len, uint8_t *out, size_t out_len,
104 const char *label_prefix);
105
106/** @brief Module namespace. */
111
113
114#endif // PROTOCORE_HKDF_H
proto_bool protocore_hkdf_expand_label_ctx(uint8_t *work, const uint8_t *secret, const char *label, const uint8_t *context, size_t context_len, uint8_t *out, size_t out_len, const char *label_prefix)
Expand under an HkdfLabel carrying a context, the Derive-Secret form.
proto_bool protocore_hkdf_extract(uint8_t *work, const uint8_t *salt, size_t salt_len, const uint8_t *ikm, size_t ikm_len, uint8_t *prk)
PRK = HMAC-SHA256(salt, ikm) (RFC 5869 sec 2.2).
PROTOCORE_NS HkdfNs Hkdf PROTOCORE_UNUSED
Module namespace.
Definition hkdf.h:107
proto_bool protocore_hkdf_expand(uint8_t *work, const uint8_t *prk, const uint8_t *info, size_t info_len, uint8_t *out, size_t out_len)
OKM = T(1) | T(2) | ..., info taken verbatim (RFC 5869 sec 2.3).
proto_bool protocore_hkdf_expand_label(uint8_t *work, const uint8_t *secret, const char *label, uint8_t *out, size_t out_len, const char *label_prefix)
Expand under an HkdfLabel with an empty context.
#define PROTOCORE_NS_LAYOUT(T,...)
Pin every dispatch slot of a table that is nothing but function pointers.
#define PROTOCORE_NS
Storage for a dispatch table. The const is load bearing.
Dispatch table. Addressed by offset, so the layout is asserted below.
Definition hkdf.h:45
proto_bool(* extract)(uint8_t *, const uint8_t *, size_t, const uint8_t *, size_t, uint8_t *)
Definition hkdf.h:46
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97