ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
hkdf_sha384.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/**
5 * @file hkdf_sha384.h
6 * @brief HKDF-SHA384 (RFC 5869) and TLS 1.3 HKDF-Expand-Label (RFC 8446 sec 7.1).
7 *
8 * The SHA-384 instantiation of the same two constructions crypto/kdf/hkdf.h runs on SHA-256. RFC 8446
9 * sec 7.1 keys the whole schedule off the cipher suite's hash, so the SHA-384 suites
10 * (TLS_AES_256_GCM_SHA384) need every Extract, Expand and Expand-Label at a 48-octet block. A thin
11 * layer over the @ref HmacSha384Ns entries rather than a second HMAC.
12 *
13 * Pure, zero heap, host-tested against the RFC 5869 construction re-run at SHA-384's width.
14 *
15 * @author Douglas Quigg (dstroy0)
16 * @date 2026
17 */
18
19#ifndef PROTOCORE_HKDF_SHA384_H
20#define PROTOCORE_HKDF_SHA384_H
21
22#include "protocore_config.h" // the entry point: protocore_types.h for the widths
23
24#if PROTOCORE_ENABLE_HKDF_SHA384
25
27
28/** @brief HKDF-SHA384 output block length (== SHA-384 digest length). */
29#define PROTOCORE_HKDF_SHA384_HASH_LEN 48
30
31/** @brief The RFC 8446 sec 7.1 HKDF-Expand-Label prefix used by TLS 1.3 and QUIC. DTLS 1.3 overrides
32 * it with "dtls13" (RFC 9147 sec 5.9); callers that need it pass it explicitly. */
33#define PROTOCORE_HKDF_SHA384_LABEL_PREFIX "tls13 "
34
35// PROTOCORE_HKDF_SHA384_BORROW - the bytes a derivation runs out of - is stated in protocore_config.h, which
36// sums it into the secure arena. A caller takes them once and passes the pointer to every call.
37
38/** @brief The salt and input keying material HKDF-Extract folds into a PRK. */
39typedef struct
40{
41 const uint8_t *salt; ///< salt bytes; NULL only when salt_len is 0
42 size_t salt_len; ///< salt length
43 const uint8_t *ikm; ///< input keying material
44 size_t ikm_len; ///< its length
45 uint8_t *prk; ///< PROTOCORE_HKDF_SHA384_HASH_LEN bytes
46} HkdfSha384ExtractArgs;
47
48/** @brief The PRK, context and output span of a bare HKDF-Expand. */
49typedef struct
50{
51 const uint8_t *prk; ///< PROTOCORE_HKDF_SHA384_HASH_LEN bytes from extract
52 const uint8_t *info; ///< context taken verbatim; NULL only when info_len is 0
53 size_t info_len; ///< its length
54 uint8_t *out; ///< output keying material
55 size_t out_len; ///< bytes requested; past 255*PROTOCORE_HKDF_SHA384_HASH_LEN out is zeroed instead
56} HkdfSha384ExpandArgs;
57
58/** @brief The secret and label an HKDF-Expand-Label derives from, with an empty HkdfLabel context. */
59typedef struct
60{
61 const uint8_t *secret; ///< traffic secret (HKDF PRK), PROTOCORE_HKDF_SHA384_HASH_LEN bytes
62 const char *label; ///< ASCII label without the prefix, <= 249 bytes
63 uint8_t *out; ///< output keying material
64 size_t out_len; ///< bytes requested
65 const char *label_prefix; ///< PROTOCORE_HKDF_SHA384_LABEL_PREFIX, or "dtls13" for DTLS 1.3
66} HkdfSha384ExpandLabelArgs;
67
68/** @brief The same with an explicit HkdfLabel context. */
69typedef struct
70{
71 const uint8_t *secret; ///< PRK, PROTOCORE_HKDF_SHA384_HASH_LEN bytes
72 const char *label; ///< ASCII label without the prefix, <= 249 bytes
73 const uint8_t *context; ///< context bytes, <= 255; NULL only when context_len is 0
74 size_t context_len; ///< context length
75 uint8_t *out; ///< output keying material
76 size_t out_len; ///< bytes requested
77 const char *label_prefix; ///< PROTOCORE_HKDF_SHA384_LABEL_PREFIX, or "dtls13" for DTLS 1.3
78} HkdfSha384ExpandLabelCtxArgs;
79
80/**
81 * @brief HKDF-SHA384 (RFC 5869) and HKDF-Expand-Label (RFC 8446 sec 7.1).
82 *
83 * A caller sets the members a call takes, invokes it through ::HkdfSha384 with the bytes it runs out of, and
84 * reads the outcome off the same handle. How those bytes are carved is this module's and is never named
85 * here.
86 *
87 * HkdfSha384.extract_args.salt = derived;
88 * HkdfSha384.extract_args.salt_len = PROTOCORE_HKDF_SHA384_HASH_LEN;
89 * HkdfSha384.extract_args.ikm = ecdhe;
90 * HkdfSha384.extract_args.ikm_len = ecdhe_len;
91 * HkdfSha384.extract_args.prk = handshake_secret;
92 * HkdfSha384.extract(work);
93 * HkdfSha384.expand_label_args.secret = handshake_secret;
94 * HkdfSha384.expand_label_args.label = "finished";
95 * HkdfSha384.expand_label_args.out = finished_key;
96 * HkdfSha384.expand_label_args.out_len = PROTOCORE_HKDF_SHA384_HASH_LEN;
97 * HkdfSha384.expand_label_args.label_prefix = PROTOCORE_HKDF_SHA384_LABEL_PREFIX;
98 * HkdfSha384.expand_label(work);
99 *
100 * @var HkdfSha384Ns::extract_args the salt and IKM HKDF-Extract folds into a PRK
101 * @var HkdfSha384Ns::expand_args the PRK, context and output span of a bare HKDF-Expand
102 * @var HkdfSha384Ns::expand_label_args the secret and label of an empty-context HKDF-Expand-Label
103 * @var HkdfSha384Ns::expand_label_ctx_args the same with an explicit HkdfLabel context
104 * @var HkdfSha384Ns::ok a call's true/false outcome
105 * @var HkdfSha384Ns::extract PRK = HMAC-SHA384(salt, ikm) (RFC 5869 sec 2.2)
106 * @var HkdfSha384Ns::expand OKM = T(1) | T(2) | ..., info taken verbatim (RFC 5869 sec 2.3)
107 * @var HkdfSha384Ns::expand_label expand under an HkdfLabel with an empty context
108 * @var HkdfSha384Ns::expand_label_ctx expand under an HkdfLabel carrying a context, the Derive-Secret form
109 *
110 * @ref HkdfSha384Ns::expand caps out_len at 255*PROTOCORE_HKDF_SHA384_HASH_LEN, the point past which the single-octet
111 * block counter has no encoding: out is zeroed and @ref HkdfSha384Ns::ok comes back false.
112 *
113 * @c work is PROTOCORE_HKDF_SHA384_BORROW secure bytes the CALLER took, at an address it knows. It is not held past the
114 * call, so nothing here aliases it. The caller releases it, and the pool wipes on release; this module neither takes
115 * it, holds it, releases it, nor wipes it. The borrow carries the PRK and the T(i) block, so two derivations in flight
116 * are two borrows and never collide.
117 *
118 * No storage member and no context: a caller sets operands and reads @ref HkdfSha384Ns::ok, and that is all
119 * the surface there is.
120 */
121typedef struct
122{
123 HkdfSha384ExtractArgs extract_args;
124 HkdfSha384ExpandArgs expand_args;
125 HkdfSha384ExpandLabelArgs expand_label_args;
126 HkdfSha384ExpandLabelCtxArgs expand_label_ctx_args;
127 proto_bool ok;
128} HkdfSha384Vars;
129
130/** @brief The operands and the outcome. */
131extern HkdfSha384Vars HkdfSha384V;
132
133/** @brief The entries. */
134typedef struct
135{
136 void (*const extract)(uint8_t *work);
137 void (*const expand)(uint8_t *work);
138 void (*const expand_label)(uint8_t *work);
139 void (*const expand_label_ctx)(uint8_t *work);
140} HkdfSha384Ns;
141
142// What the table binds, defined once in the .c and taking one parameter each: everything
143// else an entry needs is an operand in HkdfSha384V or a region of the borrow at a fixed offset.
144void protocore_hkdf_sha384_extract(uint8_t *work);
145void protocore_hkdf_sha384_expand(uint8_t *work);
146void protocore_hkdf_sha384_expand_label(uint8_t *work);
147void protocore_hkdf_sha384_expand_label_ctx(uint8_t *work);
148
149// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
150// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
151// `HkdfSha384.extract(work)` resolves to a named function and becomes a DIRECT call. An extern table
152// leaves the call indirect and the symbol live at every level, -O2 -flto included.
153static const HkdfSha384Ns HkdfSha384 __attribute__((unused)) = {
154 .extract = protocore_hkdf_sha384_extract,
155 .expand = protocore_hkdf_sha384_expand,
156 .expand_label = protocore_hkdf_sha384_expand_label,
157 .expand_label_ctx = protocore_hkdf_sha384_expand_label_ctx,
158};
159
161
162#endif // PROTOCORE_ENABLE_HKDF_SHA384
163
164#endif // PROTOCORE_HKDF_SHA384_H
#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