Branch data Line data Source code
1 : : /* SPDX-FileCopyrightText: Copyright 2026 Cozens Software Solutions Limited
2 : : * SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 OR LicenseRef-PolyForm-Internal-Use-1.0.0 OR LicenseRef-COSOSO-Commercial
3 : : */
4 : :
5 : : #include "SolidSyslogWolfSslSettings.h"
6 : :
7 : : #include "SolidSyslogWolfSslHmacSha256Policy.h"
8 : :
9 : : #include <stdbool.h>
10 : : #include <stddef.h>
11 : : #include <stdint.h>
12 : : #include <wolfssl/wolfcrypt/hmac.h>
13 : : #include <wolfssl/wolfcrypt/memory.h>
14 : : #include <wolfssl/wolfcrypt/types.h>
15 : :
16 : : #include "SolidSyslogError.h"
17 : : #include "SolidSyslogErrorCategory.h"
18 : : #include "SolidSyslogPrival.h"
19 : : #include "SolidSyslogSecurityPolicyCategories.h"
20 : : #include "SolidSyslogSecurityPolicyDefinition.h"
21 : : #include "SolidSyslogTunables.h"
22 : : #include "SolidSyslogWolfSslHmacSha256PolicyErrors.h"
23 : : #include "SolidSyslogWolfSslHmacSha256PolicyPrivate.h"
24 : :
25 : : const struct SolidSyslogErrorSource SolidSyslogWolfSslHmacSha256PolicyErrorSource = {
26 : : "WolfSslHmacSha256Policy",
27 : : SolidSyslogHmacSha256PolicyErrors_Name
28 : : };
29 : :
30 : : enum
31 : : {
32 : : HMAC_SHA256_TAG_SIZE = 32,
33 : : /* RFC 2104 / NIST SP 800-107: an HMAC key should be at least the hash
34 : : * output length. A shorter key (or an empty one) yields a well-defined but
35 : : * cryptographically worthless MAC, so we reject it and fail closed. */
36 : : HMAC_SHA256_MIN_KEY_SIZE = 32
37 : : };
38 : :
39 : : static inline struct SolidSyslogWolfSslHmacSha256Policy* WolfSslHmacSha256Policy_SelfFromBase(
40 : : struct SolidSyslogSecurityPolicy* base
41 : : );
42 : : static bool WolfSslHmacSha256Policy_SealRecord(
43 : : struct SolidSyslogSecurityPolicy* base,
44 : : const struct SolidSyslogSecurityRecord* record
45 : : );
46 : : static enum SolidSyslogSecurityPolicyOpenResult WolfSslHmacSha256Policy_OpenRecord(
47 : : struct SolidSyslogSecurityPolicy* base,
48 : : const struct SolidSyslogSecurityRecord* record
49 : : );
50 : : static bool WolfSslHmacSha256Policy_ComputeTag(
51 : : struct SolidSyslogWolfSslHmacSha256Policy* self,
52 : : const uint8_t* data,
53 : : uint16_t length,
54 : : uint8_t* tagOut,
55 : : uint16_t failureCategory
56 : : );
57 : : static bool WolfSslHmacSha256Policy_FetchKey(
58 : : struct SolidSyslogWolfSslHmacSha256Policy* self,
59 : : uint8_t* keyOut,
60 : : size_t* keyLengthOut
61 : : );
62 : : static bool WolfSslHmacSha256Policy_Hmac(
63 : : const uint8_t* key,
64 : : size_t keyLength,
65 : : const uint8_t* data,
66 : : uint16_t length,
67 : : uint8_t* tagOut
68 : : );
69 : : static void WolfSslHmacSha256Policy_ReportFault(
70 : : struct SolidSyslogWolfSslHmacSha256Policy* self,
71 : : uint16_t category,
72 : : enum SolidSyslogHmacSha256PolicyErrors code
73 : : );
74 : : static inline bool WolfSslHmacSha256Policy_KeyLengthIsValid(size_t keyLength);
75 : : static inline bool WolfSslHmacSha256Policy_ConstantTimeEquals(const uint8_t* a, const uint8_t* b, size_t length);
76 : :
77 : 50 : void SolidSyslogWolfSslHmacSha256Policy_Initialise(
78 : : struct SolidSyslogSecurityPolicy* base,
79 : : const struct SolidSyslogWolfSslHmacSha256PolicyConfig* config
80 : : )
81 : : {
82 : 50 : struct SolidSyslogWolfSslHmacSha256Policy* self = WolfSslHmacSha256Policy_SelfFromBase(base);
83 : 50 : self->Base.TrailerSize = HMAC_SHA256_TAG_SIZE;
84 : 50 : self->Base.SealRecord = WolfSslHmacSha256Policy_SealRecord;
85 : 50 : self->Base.OpenRecord = WolfSslHmacSha256Policy_OpenRecord;
86 : 50 : self->Config = *config;
87 : 50 : self->FaultReported = false;
88 : 50 : }
89 : :
90 : 50 : void SolidSyslogWolfSslHmacSha256Policy_Cleanup(struct SolidSyslogSecurityPolicy* base)
91 : : {
92 : : /* No owned resources to release - the key is fetched on demand via the
93 : : * GetKey callback and never stored on the instance. */
94 : : (void) base;
95 : 50 : }
96 : :
97 : 123 : static inline struct SolidSyslogWolfSslHmacSha256Policy* WolfSslHmacSha256Policy_SelfFromBase(
98 : : struct SolidSyslogSecurityPolicy* base
99 : : )
100 : : {
101 : : /* Base is the first member of the instance struct - see Private.h. */
102 : 123 : return (struct SolidSyslogWolfSslHmacSha256Policy*) base;
103 : : }
104 : :
105 : : /* HMAC authenticates the whole content as one buffer - the header/body split
106 : : * only matters to AEAD policies, so HeaderLength is ignored here. */
107 : 42 : static bool WolfSslHmacSha256Policy_SealRecord(
108 : : struct SolidSyslogSecurityPolicy* base,
109 : : const struct SolidSyslogSecurityRecord* record
110 : : )
111 : : {
112 : 42 : uint8_t* tag = record->Trailer;
113 : 42 : return WolfSslHmacSha256Policy_ComputeTag(
114 : : WolfSslHmacSha256Policy_SelfFromBase(base),
115 : 42 : record->Content,
116 : 42 : record->ContentLength,
117 : : tag,
118 : : SOLIDSYSLOG_CAT_SECURITY_POLICY_SEAL_FAILED
119 : : );
120 : : }
121 : :
122 : : /* Fetches the key on demand into a transient buffer, computes HMAC-SHA256 over
123 : : * `data` into `tagOut`, then wipes the key buffer - the key never lingers
124 : : * beyond a single computation. Returns false and reports the reason if the key
125 : : * is unavailable or the HMAC computation fails: no tag, so a seal fails and an
126 : : * open could not run. Shared by seal (writes the record tag) and verify
127 : : * (recomputes for comparison). */
128 : 73 : static bool WolfSslHmacSha256Policy_ComputeTag(
129 : : struct SolidSyslogWolfSslHmacSha256Policy* self,
130 : : const uint8_t* data,
131 : : uint16_t length,
132 : : uint8_t* tagOut,
133 : : uint16_t failureCategory
134 : : )
135 : : {
136 : : uint8_t key[SOLIDSYSLOG_MAX_HMAC_KEY_SIZE];
137 : 73 : size_t keyLength = 0U;
138 : 73 : bool computed = false;
139 [ + + ]: 73 : if (WolfSslHmacSha256Policy_FetchKey(self, key, &keyLength))
140 : : {
141 [ + + ]: 54 : if (WolfSslHmacSha256Policy_Hmac(key, keyLength, data, length, tagOut))
142 : : {
143 : : /* A tag to seal with or verify against: the run of faults is over. */
144 : 45 : computed = true;
145 : 45 : self->FaultReported = false;
146 : : }
147 : : else
148 : : {
149 : 9 : WolfSslHmacSha256Policy_ReportFault(
150 : : self,
151 : : failureCategory,
152 : : SOLIDSYSLOG_HMAC_SHA256_POLICY_ERROR_HMAC_FAILED
153 : : );
154 : : }
155 : : }
156 : : /* Wipe the whole key buffer - the full region GetKey was handed, not just
157 : : * the bytes written - so no key material lingers on the stack. */
158 : 73 : wc_ForceZero(key, sizeof key);
159 : 73 : return computed;
160 : : }
161 : :
162 : : /* Fetches the key on demand and validates its length. Fails closed (and
163 : : * reports) if the key is unavailable, or if its length falls outside
164 : : * [HMAC_SHA256_MIN_KEY_SIZE, sizeof buffer]. */
165 : 73 : static bool WolfSslHmacSha256Policy_FetchKey(
166 : : struct SolidSyslogWolfSslHmacSha256Policy* self,
167 : : uint8_t* keyOut,
168 : : size_t* keyLengthOut
169 : : )
170 : : {
171 : 73 : size_t keyLength = 0U;
172 : 73 : bool fetched = false;
173 [ + + ]: 73 : if (self->Config.GetKey(self->Config.KeyContext, keyOut, SOLIDSYSLOG_MAX_HMAC_KEY_SIZE, &keyLength))
174 : : {
175 [ + + ]: 58 : if (WolfSslHmacSha256Policy_KeyLengthIsValid(keyLength))
176 : : {
177 : 54 : *keyLengthOut = keyLength;
178 : 54 : fetched = true;
179 : : }
180 : : else
181 : : {
182 : 4 : WolfSslHmacSha256Policy_ReportFault(
183 : : self,
184 : : SOLIDSYSLOG_CAT_SECURITY_POLICY_KEY_UNAVAILABLE,
185 : : SOLIDSYSLOG_HMAC_SHA256_POLICY_ERROR_KEY_TOO_SHORT
186 : : );
187 : : }
188 : : }
189 : : else
190 : : {
191 : 15 : WolfSslHmacSha256Policy_ReportFault(
192 : : self,
193 : : SOLIDSYSLOG_CAT_SECURITY_POLICY_KEY_UNAVAILABLE,
194 : : SOLIDSYSLOG_HMAC_SHA256_POLICY_ERROR_KEY_UNAVAILABLE
195 : : );
196 : : }
197 : 73 : return fetched;
198 : : }
199 : :
200 : : /* One HMAC-SHA256 over `data` with a transient wolfCrypt context. Any step
201 : : * failing is one fault to the caller; the context is freed either way. */
202 : 54 : static bool WolfSslHmacSha256Policy_Hmac(
203 : : const uint8_t* key,
204 : : size_t keyLength,
205 : : const uint8_t* data,
206 : : uint16_t length,
207 : : uint8_t* tagOut
208 : : )
209 : : {
210 : : Hmac hmac;
211 : 54 : bool ok = false;
212 [ + + ]: 54 : if (wc_HmacInit(&hmac, NULL, INVALID_DEVID) == 0)
213 : : {
214 [ + + ]: 102 : ok = (wc_HmacSetKey(&hmac, WC_SHA256, key, (word32) keyLength) == 0) &&
215 [ + + + + ]: 102 : (wc_HmacUpdate(&hmac, data, length) == 0) && (wc_HmacFinal(&hmac, tagOut) == 0);
216 : 52 : wc_HmacFree(&hmac);
217 : : }
218 : 54 : return ok;
219 : : }
220 : :
221 : : /* The first fault in a run is the one reported. */
222 : 28 : static void WolfSslHmacSha256Policy_ReportFault(
223 : : struct SolidSyslogWolfSslHmacSha256Policy* self,
224 : : uint16_t category,
225 : : enum SolidSyslogHmacSha256PolicyErrors code
226 : : )
227 : : {
228 [ + + ]: 28 : if (!self->FaultReported)
229 : : {
230 : 25 : WolfSslHmacSha256Policy_Report(SOLIDSYSLOG_SEVERITY_ERROR, category, code);
231 : 25 : self->FaultReported = true;
232 : : }
233 : 28 : }
234 : :
235 : 58 : static inline bool WolfSslHmacSha256Policy_KeyLengthIsValid(size_t keyLength)
236 : : {
237 [ + + + + ]: 58 : return (keyLength >= (size_t) HMAC_SHA256_MIN_KEY_SIZE) && (keyLength <= (size_t) SOLIDSYSLOG_MAX_HMAC_KEY_SIZE);
238 : : }
239 : :
240 : 31 : static enum SolidSyslogSecurityPolicyOpenResult WolfSslHmacSha256Policy_OpenRecord(
241 : : struct SolidSyslogSecurityPolicy* base,
242 : : const struct SolidSyslogSecurityRecord* record
243 : : )
244 : : {
245 : : uint8_t expected[HMAC_SHA256_TAG_SIZE];
246 : 31 : enum SolidSyslogSecurityPolicyOpenResult result = SOLIDSYSLOG_SECURITY_POLICY_OPEN_COULD_NOT_RUN;
247 [ + + ]: 31 : if (WolfSslHmacSha256Policy_ComputeTag(
248 : : WolfSslHmacSha256Policy_SelfFromBase(base),
249 : 31 : record->Content,
250 : 31 : record->ContentLength,
251 : : expected,
252 : : SOLIDSYSLOG_CAT_SECURITY_POLICY_OPEN_FAILED
253 : : ))
254 : : {
255 : 16 : result = WolfSslHmacSha256Policy_ConstantTimeEquals(expected, record->Trailer, HMAC_SHA256_TAG_SIZE)
256 : : ? SOLIDSYSLOG_SECURITY_POLICY_OPEN_OPENED
257 : 16 : : SOLIDSYSLOG_SECURITY_POLICY_OPEN_REJECTED;
258 : : }
259 : 31 : return result;
260 : : }
261 : :
262 : 16 : static inline bool WolfSslHmacSha256Policy_ConstantTimeEquals(const uint8_t* a, const uint8_t* b, size_t length)
263 : : {
264 : : /* Accumulate every byte difference so the loop runs the full length
265 : : * regardless of where a mismatch occurs - no early exit, no timing oracle
266 : : * on the tag comparison. */
267 : 16 : uint8_t difference = 0U;
268 [ + + ]: 528 : for (size_t index = 0U; index < length; index++)
269 : : {
270 : 512 : difference |= (uint8_t) (a[index] ^ b[index]);
271 : : }
272 : 16 : return difference == 0U;
273 : : }
|