/src/samba/lib/crypto/gkdi.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | Group Key Distribution Protocol functions |
4 | | |
5 | | Copyright (C) Catalyst.Net Ltd 2023 |
6 | | |
7 | | This program is free software: you can redistribute it and/or modify |
8 | | it under the terms of the GNU General Public License as published by |
9 | | the Free Software Foundation, either version 3 of the License, or |
10 | | (at your option) any later version. |
11 | | |
12 | | This program is distributed in the hope that it will be useful, |
13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | GNU General Public License for more details. |
16 | | |
17 | | You should have received a copy of the GNU General Public License |
18 | | along with this program. If not, see <https://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | #include "includes.h" |
22 | | #include <gnutls/gnutls.h> |
23 | | #include <gnutls/crypto.h> |
24 | | |
25 | | #include "lib/crypto/gnutls_helpers.h" |
26 | | |
27 | | #include "lib/util/bytearray.h" |
28 | | |
29 | | #include "librpc/ndr/libndr.h" |
30 | | #include "librpc/gen_ndr/ndr_security.h" |
31 | | #include "librpc/gen_ndr/gkdi.h" |
32 | | #include "librpc/gen_ndr/ndr_gkdi.h" |
33 | | |
34 | | #include "lib/crypto/gkdi.h" |
35 | | #include "lib/util/data_blob.h" |
36 | | |
37 | | static const uint8_t kds_service[] = { |
38 | | /* “KDS service” as a NULL‐terminated UTF‐16LE string. */ |
39 | | 'K', 0, 'D', 0, 'S', 0, ' ', 0, 's', 0, 'e', 0, |
40 | | 'r', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, 0, 0, |
41 | | }; |
42 | | |
43 | | static struct Gkid gkid_from_u32_indices(const uint32_t l0_idx, |
44 | | const uint32_t l1_idx, |
45 | | const uint32_t l2_idx) |
46 | 0 | { |
47 | | /* Catch out‐of‐range indices. */ |
48 | 0 | if (l0_idx > INT32_MAX || l1_idx > INT8_MAX || l2_idx > INT8_MAX) { |
49 | 0 | return invalid_gkid; |
50 | 0 | } |
51 | | |
52 | 0 | return Gkid(l0_idx, l1_idx, l2_idx); |
53 | 0 | } |
54 | | |
55 | | NTSTATUS gkdi_pull_KeyEnvelope(TALLOC_CTX *mem_ctx, |
56 | | const DATA_BLOB *key_env_blob, |
57 | | struct KeyEnvelope *key_env_out) |
58 | 0 | { |
59 | 0 | NTSTATUS status = NT_STATUS_OK; |
60 | 0 | enum ndr_err_code err; |
61 | |
|
62 | 0 | if (key_env_blob == NULL) { |
63 | 0 | return NT_STATUS_INVALID_PARAMETER; |
64 | 0 | } |
65 | | |
66 | 0 | if (key_env_out == NULL) { |
67 | 0 | return NT_STATUS_INVALID_PARAMETER; |
68 | 0 | } |
69 | | |
70 | 0 | err = ndr_pull_struct_blob(key_env_blob, |
71 | 0 | mem_ctx, |
72 | 0 | key_env_out, |
73 | 0 | (ndr_pull_flags_fn_t)ndr_pull_KeyEnvelope); |
74 | 0 | status = ndr_map_error2ntstatus(err); |
75 | 0 | if (!NT_STATUS_IS_OK(status)) { |
76 | 0 | return status; |
77 | 0 | } |
78 | | |
79 | | /* If we felt so inclined, we could check the version field here. */ |
80 | | |
81 | 0 | return status; |
82 | 0 | } |
83 | | |
84 | | /* |
85 | | * Retrieve the GKID and root key ID from a KeyEnvelope blob. The returned |
86 | | * structure is guaranteed to have a valid GKID. |
87 | | */ |
88 | | const struct KeyEnvelopeId *gkdi_pull_KeyEnvelopeId( |
89 | | const DATA_BLOB key_env_blob, |
90 | | struct KeyEnvelopeId *key_env_out) |
91 | 0 | { |
92 | 0 | TALLOC_CTX *tmp_ctx = NULL; |
93 | 0 | struct KeyEnvelope key_env; |
94 | 0 | const struct KeyEnvelopeId *key_env_ret = NULL; |
95 | 0 | NTSTATUS status; |
96 | |
|
97 | 0 | if (key_env_out == NULL) { |
98 | 0 | goto out; |
99 | 0 | } |
100 | | |
101 | 0 | tmp_ctx = talloc_new(NULL); |
102 | 0 | if (tmp_ctx == NULL) { |
103 | 0 | goto out; |
104 | 0 | } |
105 | | |
106 | 0 | status = gkdi_pull_KeyEnvelope(tmp_ctx, &key_env_blob, &key_env); |
107 | 0 | if (!NT_STATUS_IS_OK(status)) { |
108 | 0 | goto out; |
109 | 0 | } |
110 | | |
111 | 0 | { |
112 | 0 | const struct Gkid gkid = gkid_from_u32_indices( |
113 | 0 | key_env.l0_index, key_env.l1_index, key_env.l2_index); |
114 | 0 | if (!gkid_is_valid(gkid)) { |
115 | | /* The KeyId is not valid: we can’t use it. */ |
116 | 0 | goto out; |
117 | 0 | } |
118 | | |
119 | 0 | *key_env_out = (struct KeyEnvelopeId){ |
120 | 0 | .root_key_id = key_env.root_key_id, .gkid = gkid}; |
121 | 0 | } |
122 | | |
123 | | /* Return a pointer to the buffer passed in by the caller. */ |
124 | 0 | key_env_ret = key_env_out; |
125 | |
|
126 | 0 | out: |
127 | 0 | TALLOC_FREE(tmp_ctx); |
128 | 0 | return key_env_ret; |
129 | 0 | } |
130 | | |
131 | | NTSTATUS ProvRootKey(TALLOC_CTX *mem_ctx, |
132 | | const struct GUID root_key_id, |
133 | | const int32_t version, |
134 | | const DATA_BLOB root_key_data, |
135 | | const NTTIME create_time, |
136 | | const NTTIME use_start_time, |
137 | | const char *const domain_id, |
138 | | const struct KdfAlgorithm kdf_algorithm, |
139 | | const struct ProvRootKey **const root_key_out) |
140 | 0 | { |
141 | 0 | NTSTATUS status = NT_STATUS_OK; |
142 | 0 | struct ProvRootKey *root_key = NULL; |
143 | |
|
144 | 0 | if (root_key_out == NULL) { |
145 | 0 | return NT_STATUS_INVALID_PARAMETER; |
146 | 0 | } |
147 | 0 | *root_key_out = NULL; |
148 | |
|
149 | 0 | root_key = talloc(mem_ctx, struct ProvRootKey); |
150 | 0 | if (root_key == NULL) { |
151 | 0 | return NT_STATUS_NO_MEMORY; |
152 | 0 | } |
153 | | |
154 | 0 | *root_key = (struct ProvRootKey){ |
155 | 0 | .id = root_key_id, |
156 | 0 | .data = {.data = talloc_steal(root_key, root_key_data.data), |
157 | 0 | .length = root_key_data.length}, |
158 | 0 | .create_time = create_time, |
159 | 0 | .use_start_time = use_start_time, |
160 | 0 | .domain_id = talloc_steal(root_key, domain_id), |
161 | 0 | .kdf_algorithm = kdf_algorithm, |
162 | 0 | .version = version, |
163 | 0 | }; |
164 | |
|
165 | 0 | *root_key_out = root_key; |
166 | 0 | return status; |
167 | 0 | } |
168 | | |
169 | | struct Gkid gkdi_get_interval_id(const NTTIME time) |
170 | 0 | { |
171 | 0 | return Gkid(time / (gkdi_l1_key_iteration * gkdi_l2_key_iteration * |
172 | 0 | gkdi_key_cycle_duration), |
173 | 0 | time / (gkdi_l2_key_iteration * gkdi_key_cycle_duration) % |
174 | 0 | gkdi_l1_key_iteration, |
175 | 0 | time / gkdi_key_cycle_duration % gkdi_l2_key_iteration); |
176 | 0 | } |
177 | | |
178 | | bool gkdi_get_key_start_time(const struct Gkid gkid, NTTIME *start_time_out) |
179 | 0 | { |
180 | 0 | if (!gkid_is_valid(gkid)) { |
181 | 0 | return false; |
182 | 0 | } |
183 | | |
184 | 0 | { |
185 | 0 | enum GkidType key_type = gkid_key_type(gkid); |
186 | 0 | if (key_type != GKID_L2_SEED_KEY) { |
187 | 0 | return false; |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | 0 | { |
192 | | /* |
193 | | * Make sure that the GKID is not so large its start time can’t |
194 | | * be represented in NTTIME. |
195 | | */ |
196 | 0 | const struct Gkid max_gkid = { |
197 | 0 | UINT64_MAX / |
198 | 0 | (gkdi_l1_key_iteration * gkdi_l2_key_iteration * |
199 | 0 | gkdi_key_cycle_duration), |
200 | 0 | UINT64_MAX / |
201 | 0 | (gkdi_l2_key_iteration * |
202 | 0 | gkdi_key_cycle_duration) % |
203 | 0 | gkdi_l1_key_iteration, |
204 | 0 | UINT64_MAX / gkdi_key_cycle_duration % |
205 | 0 | gkdi_l2_key_iteration}; |
206 | 0 | if (!gkid_less_than_or_equal_to(gkid, max_gkid)) { |
207 | 0 | return false; |
208 | 0 | } |
209 | 0 | } |
210 | | |
211 | 0 | *start_time_out = ((uint64_t)gkid.l0_idx * gkdi_l1_key_iteration * |
212 | 0 | gkdi_l2_key_iteration + |
213 | 0 | (uint64_t)gkid.l1_idx * gkdi_l2_key_iteration + |
214 | 0 | (uint64_t)gkid.l2_idx) * |
215 | 0 | gkdi_key_cycle_duration; |
216 | 0 | return true; |
217 | 0 | } |
218 | | |
219 | | /* |
220 | | * This returns the equivalent of |
221 | | * gkdi_get_key_start_time(gkdi_get_interval_id(time)). |
222 | | */ |
223 | | NTTIME gkdi_get_interval_start_time(const NTTIME time) |
224 | 0 | { |
225 | 0 | return time / gkdi_key_cycle_duration * gkdi_key_cycle_duration; |
226 | 0 | } |
227 | | |
228 | | bool gkid_less_than_or_equal_to(const struct Gkid g1, const struct Gkid g2) |
229 | 0 | { |
230 | 0 | if (g1.l0_idx != g2.l0_idx) { |
231 | 0 | return g1.l0_idx < g2.l0_idx; |
232 | 0 | } |
233 | | |
234 | 0 | if (g1.l1_idx != g2.l1_idx) { |
235 | 0 | return g1.l1_idx < g2.l1_idx; |
236 | 0 | } |
237 | | |
238 | 0 | return g1.l2_idx <= g2.l2_idx; |
239 | 0 | } |
240 | | |
241 | | bool gkdi_rollover_interval(const int64_t managed_password_interval, |
242 | | NTTIME *result) |
243 | 0 | { |
244 | | /* |
245 | | * This is actually a conservative reckoning. The interval could be one |
246 | | * higher than this maximum and not overflow. But there’s no reason to |
247 | | * support intervals that high (and Windows will start producing strange |
248 | | * results for intervals beyond that). |
249 | | */ |
250 | 0 | const int64_t maximum_interval = UINT64_MAX / gkdi_key_cycle_duration * |
251 | 0 | 10 / 24; |
252 | |
|
253 | 0 | if (managed_password_interval < 0 || |
254 | 0 | managed_password_interval > maximum_interval) |
255 | 0 | { |
256 | 0 | return false; |
257 | 0 | } |
258 | | |
259 | 0 | *result = (uint64_t)managed_password_interval * 24 / 10 * |
260 | 0 | gkdi_key_cycle_duration; |
261 | 0 | return true; |
262 | 0 | } |
263 | | |
264 | | struct GkdiContextShort { |
265 | | uint8_t buf[sizeof((struct GUID_ndr_buf){}.buf) + sizeof(int32_t) + |
266 | | sizeof(int32_t) + sizeof(int32_t)]; |
267 | | }; |
268 | | |
269 | | static NTSTATUS make_gkdi_context(const struct GkdiDerivationCtx *ctx, |
270 | | struct GkdiContextShort *out_ctx) |
271 | 0 | { |
272 | 0 | enum ndr_err_code ndr_err; |
273 | 0 | DATA_BLOB b = {.data = out_ctx->buf, .length = sizeof out_ctx->buf}; |
274 | |
|
275 | 0 | if (ctx->target_security_descriptor.length) { |
276 | 0 | return NT_STATUS_INVALID_PARAMETER; |
277 | 0 | } |
278 | | |
279 | 0 | ndr_err = ndr_push_struct_into_fixed_blob( |
280 | 0 | &b, ctx, (ndr_push_flags_fn_t)ndr_push_GkdiDerivationCtx); |
281 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
282 | 0 | return ndr_map_error2ntstatus(ndr_err); |
283 | 0 | } |
284 | | |
285 | 0 | return NT_STATUS_OK; |
286 | 0 | } |
287 | | |
288 | | static NTSTATUS make_gkdi_context_security_descriptor( |
289 | | TALLOC_CTX *mem_ctx, |
290 | | const struct GkdiDerivationCtx *ctx, |
291 | | const DATA_BLOB security_descriptor, |
292 | | DATA_BLOB *out_ctx) |
293 | 0 | { |
294 | 0 | enum ndr_err_code ndr_err; |
295 | 0 | struct GkdiDerivationCtx ctx_with_sd = *ctx; |
296 | |
|
297 | 0 | if (ctx_with_sd.target_security_descriptor.length != 0) { |
298 | 0 | return NT_STATUS_INVALID_PARAMETER; |
299 | 0 | } |
300 | | |
301 | 0 | ctx_with_sd.target_security_descriptor = security_descriptor; |
302 | |
|
303 | 0 | ndr_err = ndr_push_struct_blob(out_ctx, |
304 | 0 | mem_ctx, |
305 | 0 | &ctx_with_sd, |
306 | 0 | (ndr_push_flags_fn_t) |
307 | 0 | ndr_push_GkdiDerivationCtx); |
308 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
309 | 0 | return ndr_map_error2ntstatus(ndr_err); |
310 | 0 | } |
311 | | |
312 | 0 | return NT_STATUS_OK; |
313 | 0 | } |
314 | | |
315 | | struct GkdiContext { |
316 | | struct GkdiDerivationCtx ctx; |
317 | | gnutls_mac_algorithm_t algorithm; |
318 | | }; |
319 | | |
320 | | gnutls_mac_algorithm_t get_sp800_108_mac_algorithm( |
321 | | const struct KdfAlgorithm kdf_algorithm) |
322 | 0 | { |
323 | 0 | switch (kdf_algorithm.id) { |
324 | 0 | case KDF_ALGORITHM_SP800_108_CTR_HMAC: |
325 | 0 | switch (kdf_algorithm.param.sp800_108) { |
326 | 0 | case KDF_PARAM_SHA1: |
327 | 0 | return GNUTLS_MAC_SHA1; |
328 | 0 | case KDF_PARAM_SHA256: |
329 | 0 | return GNUTLS_MAC_SHA256; |
330 | 0 | case KDF_PARAM_SHA384: |
331 | 0 | return GNUTLS_MAC_SHA384; |
332 | 0 | case KDF_PARAM_SHA512: |
333 | 0 | return GNUTLS_MAC_SHA512; |
334 | 0 | } |
335 | 0 | break; |
336 | 0 | } |
337 | | |
338 | 0 | return GNUTLS_MAC_UNKNOWN; |
339 | 0 | } |
340 | | |
341 | | static NTSTATUS GkdiContext(const struct ProvRootKey *const root_key, |
342 | | struct GkdiContext *const ctx) |
343 | 0 | { |
344 | 0 | NTSTATUS status = NT_STATUS_OK; |
345 | 0 | gnutls_mac_algorithm_t algorithm = GNUTLS_MAC_UNKNOWN; |
346 | |
|
347 | 0 | if (ctx == NULL) { |
348 | 0 | status = NT_STATUS_INVALID_PARAMETER; |
349 | 0 | goto out; |
350 | 0 | } |
351 | | |
352 | 0 | if (root_key == NULL) { |
353 | 0 | status = NT_STATUS_INVALID_PARAMETER; |
354 | 0 | goto out; |
355 | 0 | } |
356 | | |
357 | 0 | if (root_key->version != root_key_version_1) { |
358 | 0 | status = NT_STATUS_NOT_SUPPORTED; |
359 | 0 | goto out; |
360 | 0 | } |
361 | | |
362 | 0 | if (root_key->data.length != GKDI_KEY_LEN) { |
363 | 0 | status = NT_STATUS_NOT_SUPPORTED; |
364 | 0 | goto out; |
365 | 0 | } |
366 | | |
367 | 0 | algorithm = get_sp800_108_mac_algorithm(root_key->kdf_algorithm); |
368 | 0 | if (algorithm == GNUTLS_MAC_UNKNOWN) { |
369 | 0 | status = NT_STATUS_NOT_SUPPORTED; |
370 | 0 | goto out; |
371 | 0 | } |
372 | | |
373 | | /* |
374 | | * The context comprises the GUID corresponding to the root key, the |
375 | | * GKID (which we shall initialize to zero), and the encoded target |
376 | | * security descriptor (which will initially be empty). |
377 | | */ |
378 | 0 | *ctx = (struct GkdiContext){ |
379 | 0 | .ctx = {.guid = root_key->id, |
380 | 0 | .l0_idx = 0, |
381 | 0 | .l1_idx = 0, |
382 | 0 | .l2_idx = 0, |
383 | 0 | .target_security_descriptor = {}}, |
384 | 0 | .algorithm = algorithm, |
385 | 0 | }; |
386 | 0 | out: |
387 | 0 | return status; |
388 | 0 | } |
389 | | |
390 | | static NTSTATUS compute_l1_seed_key(TALLOC_CTX *mem_ctx, |
391 | | struct GkdiContext *ctx, |
392 | | const DATA_BLOB security_descriptor, |
393 | | const struct ProvRootKey *const root_key, |
394 | | const struct Gkid gkid, |
395 | | uint8_t key[static const GKDI_KEY_LEN]) |
396 | 0 | { |
397 | 0 | NTSTATUS status = NT_STATUS_OK; |
398 | 0 | struct GkdiContextShort short_ctx; |
399 | 0 | int8_t n; |
400 | |
|
401 | 0 | ctx->ctx.l0_idx = gkid.l0_idx; |
402 | 0 | ctx->ctx.l1_idx = -1; |
403 | 0 | ctx->ctx.l2_idx = -1; |
404 | |
|
405 | 0 | status = make_gkdi_context(&ctx->ctx, &short_ctx); |
406 | 0 | if (!NT_STATUS_IS_OK(status)) { |
407 | 0 | goto out; |
408 | 0 | } |
409 | | |
410 | | /* Derive an L0 seed key with GKID = (L0, −1, −1). */ |
411 | | |
412 | 0 | status = samba_gnutls_sp800_108_derive_key(root_key->data.data, |
413 | 0 | root_key->data.length, |
414 | 0 | NULL, |
415 | 0 | 0, |
416 | 0 | kds_service, |
417 | 0 | sizeof kds_service, |
418 | 0 | short_ctx.buf, |
419 | 0 | sizeof short_ctx.buf, |
420 | 0 | ctx->algorithm, |
421 | 0 | key, |
422 | 0 | GKDI_KEY_LEN); |
423 | 0 | if (!NT_STATUS_IS_OK(status)) { |
424 | 0 | goto out; |
425 | 0 | } |
426 | | |
427 | | /* Derive an L1 seed key with GKID = (L0, 31, −1). */ |
428 | | |
429 | 0 | ctx->ctx.l1_idx = 31; |
430 | |
|
431 | 0 | { |
432 | 0 | DATA_BLOB security_descriptor_ctx; |
433 | |
|
434 | 0 | status = make_gkdi_context_security_descriptor( |
435 | 0 | mem_ctx, |
436 | 0 | &ctx->ctx, |
437 | 0 | security_descriptor, |
438 | 0 | &security_descriptor_ctx); |
439 | 0 | if (!NT_STATUS_IS_OK(status)) { |
440 | 0 | goto out; |
441 | 0 | } |
442 | | |
443 | 0 | status = samba_gnutls_sp800_108_derive_key( |
444 | 0 | key, |
445 | 0 | GKDI_KEY_LEN, |
446 | 0 | NULL, |
447 | 0 | 0, |
448 | 0 | kds_service, |
449 | 0 | sizeof kds_service, |
450 | 0 | security_descriptor_ctx.data, |
451 | 0 | security_descriptor_ctx.length, |
452 | 0 | ctx->algorithm, |
453 | 0 | key, |
454 | 0 | GKDI_KEY_LEN); |
455 | 0 | data_blob_free(&security_descriptor_ctx); |
456 | 0 | if (!NT_STATUS_IS_OK(status)) { |
457 | 0 | goto out; |
458 | 0 | } |
459 | 0 | } |
460 | | |
461 | 0 | for (n = 30; n >= gkid.l1_idx; --n) { |
462 | | /* Derive an L1 seed key with GKID = (L0, n, −1). */ |
463 | |
|
464 | 0 | ctx->ctx.l1_idx = n; |
465 | |
|
466 | 0 | status = make_gkdi_context(&ctx->ctx, &short_ctx); |
467 | 0 | if (!NT_STATUS_IS_OK(status)) { |
468 | 0 | goto out; |
469 | 0 | } |
470 | | |
471 | 0 | status = samba_gnutls_sp800_108_derive_key(key, |
472 | 0 | GKDI_KEY_LEN, |
473 | 0 | NULL, |
474 | 0 | 0, |
475 | 0 | kds_service, |
476 | 0 | sizeof kds_service, |
477 | 0 | short_ctx.buf, |
478 | 0 | sizeof short_ctx.buf, |
479 | 0 | ctx->algorithm, |
480 | 0 | key, |
481 | 0 | GKDI_KEY_LEN); |
482 | 0 | if (!NT_STATUS_IS_OK(status)) { |
483 | 0 | goto out; |
484 | 0 | } |
485 | 0 | } |
486 | | |
487 | 0 | out: |
488 | 0 | return status; |
489 | 0 | } |
490 | | |
491 | | static NTSTATUS derive_l2_seed_key(struct GkdiContext *ctx, |
492 | | const struct Gkid gkid, |
493 | | uint8_t key[static const GKDI_KEY_LEN]) |
494 | 0 | { |
495 | 0 | NTSTATUS status = NT_STATUS_OK; |
496 | 0 | int8_t n; |
497 | |
|
498 | 0 | ctx->ctx.l0_idx = gkid.l0_idx; |
499 | 0 | ctx->ctx.l1_idx = gkid.l1_idx; |
500 | |
|
501 | 0 | for (n = 31; n >= gkid.l2_idx; --n) { |
502 | 0 | struct GkdiContextShort short_ctx; |
503 | | |
504 | | /* Derive an L2 seed key with GKID = (L0, L1, n). */ |
505 | |
|
506 | 0 | ctx->ctx.l2_idx = n; |
507 | |
|
508 | 0 | status = make_gkdi_context(&ctx->ctx, &short_ctx); |
509 | 0 | if (!NT_STATUS_IS_OK(status)) { |
510 | 0 | goto out; |
511 | 0 | } |
512 | | |
513 | 0 | status = samba_gnutls_sp800_108_derive_key(key, |
514 | 0 | GKDI_KEY_LEN, |
515 | 0 | NULL, |
516 | 0 | 0, |
517 | 0 | kds_service, |
518 | 0 | sizeof kds_service, |
519 | 0 | short_ctx.buf, |
520 | 0 | sizeof short_ctx.buf, |
521 | 0 | ctx->algorithm, |
522 | 0 | key, |
523 | 0 | GKDI_KEY_LEN); |
524 | 0 | if (!NT_STATUS_IS_OK(status)) { |
525 | 0 | goto out; |
526 | 0 | } |
527 | 0 | } |
528 | | |
529 | 0 | out: |
530 | 0 | return status; |
531 | 0 | } |
532 | | |
533 | | enum GkidType gkid_key_type(const struct Gkid gkid) |
534 | 0 | { |
535 | 0 | if (gkid.l0_idx == -1) { |
536 | 0 | return GKID_DEFAULT; |
537 | 0 | } |
538 | | |
539 | 0 | if (gkid.l1_idx == -1) { |
540 | 0 | return GKID_L0_SEED_KEY; |
541 | 0 | } |
542 | | |
543 | 0 | if (gkid.l2_idx == -1) { |
544 | 0 | return GKID_L1_SEED_KEY; |
545 | 0 | } |
546 | | |
547 | 0 | return GKID_L2_SEED_KEY; |
548 | 0 | } |
549 | | |
550 | | bool gkid_is_valid(const struct Gkid gkid) |
551 | 0 | { |
552 | 0 | if (gkid.l0_idx < -1) { |
553 | 0 | return false; |
554 | 0 | } |
555 | | |
556 | 0 | if (gkid.l1_idx < -1 || gkid.l1_idx >= gkdi_l1_key_iteration) { |
557 | 0 | return false; |
558 | 0 | } |
559 | | |
560 | 0 | if (gkid.l2_idx < -1 || gkid.l2_idx >= gkdi_l2_key_iteration) { |
561 | 0 | return false; |
562 | 0 | } |
563 | | |
564 | 0 | if (gkid.l0_idx == -1 && gkid.l1_idx != -1) { |
565 | 0 | return false; |
566 | 0 | } |
567 | | |
568 | 0 | if (gkid.l1_idx == -1 && gkid.l2_idx != -1) { |
569 | 0 | return false; |
570 | 0 | } |
571 | | |
572 | 0 | return true; |
573 | 0 | } |
574 | | |
575 | | NTSTATUS compute_seed_key(TALLOC_CTX *mem_ctx, |
576 | | const DATA_BLOB target_security_descriptor, |
577 | | const struct ProvRootKey *const root_key, |
578 | | const struct Gkid gkid, |
579 | | uint8_t key[static const GKDI_KEY_LEN]) |
580 | 0 | { |
581 | 0 | NTSTATUS status = NT_STATUS_OK; |
582 | 0 | enum GkidType gkid_type; |
583 | 0 | struct GkdiContext ctx; |
584 | |
|
585 | 0 | if (!gkid_is_valid(gkid)) { |
586 | 0 | status = NT_STATUS_INVALID_PARAMETER; |
587 | 0 | goto out; |
588 | 0 | } |
589 | | |
590 | 0 | gkid_type = gkid_key_type(gkid); |
591 | 0 | if (gkid_type < GKID_L1_SEED_KEY) { |
592 | | /* Don’t allow derivation of L0 seed keys. */ |
593 | 0 | status = NT_STATUS_INVALID_PARAMETER; |
594 | 0 | goto out; |
595 | 0 | } |
596 | | |
597 | 0 | status = GkdiContext(root_key, &ctx); |
598 | 0 | if (!NT_STATUS_IS_OK(status)) { |
599 | 0 | goto out; |
600 | 0 | } |
601 | | |
602 | 0 | status = compute_l1_seed_key( |
603 | 0 | mem_ctx, &ctx, target_security_descriptor, root_key, gkid, key); |
604 | 0 | if (!NT_STATUS_IS_OK(status)) { |
605 | 0 | goto out; |
606 | 0 | } |
607 | | |
608 | 0 | if (gkid_type == GKID_L2_SEED_KEY) { |
609 | 0 | status = derive_l2_seed_key(&ctx, gkid, key); |
610 | 0 | if (!NT_STATUS_IS_OK(status)) { |
611 | 0 | goto out; |
612 | 0 | } |
613 | 0 | } |
614 | | |
615 | 0 | out: |
616 | 0 | return status; |
617 | 0 | } |
618 | | |
619 | | NTSTATUS kdf_sp_800_108_from_params( |
620 | | const DATA_BLOB *const kdf_param, |
621 | | struct KdfAlgorithm *const kdf_algorithm_out) |
622 | 0 | { |
623 | 0 | TALLOC_CTX *tmp_ctx = NULL; |
624 | 0 | NTSTATUS status = NT_STATUS_OK; |
625 | 0 | enum ndr_err_code err; |
626 | 0 | enum KdfSp800_108Param sp800_108_param = KDF_PARAM_SHA256; |
627 | 0 | struct KdfParameters kdf_parameters; |
628 | |
|
629 | 0 | if (kdf_param != NULL) { |
630 | 0 | tmp_ctx = talloc_new(NULL); |
631 | 0 | if (tmp_ctx == NULL) { |
632 | 0 | status = NT_STATUS_NO_MEMORY; |
633 | 0 | goto out; |
634 | 0 | } |
635 | | |
636 | 0 | err = ndr_pull_struct_blob(kdf_param, |
637 | 0 | tmp_ctx, |
638 | 0 | &kdf_parameters, |
639 | 0 | (ndr_pull_flags_fn_t) |
640 | 0 | ndr_pull_KdfParameters); |
641 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(err)) { |
642 | 0 | status = ndr_map_error2ntstatus(err); |
643 | 0 | DBG_WARNING("KdfParameters pull failed: %s\n", |
644 | 0 | nt_errstr(status)); |
645 | 0 | goto out; |
646 | 0 | } |
647 | | |
648 | 0 | if (kdf_parameters.hash_algorithm == NULL) { |
649 | 0 | status = NT_STATUS_NOT_SUPPORTED; |
650 | 0 | goto out; |
651 | 0 | } |
652 | | |
653 | | /* These string comparisons are case‐sensitive. */ |
654 | 0 | if (strcmp(kdf_parameters.hash_algorithm, "SHA1") == 0) { |
655 | 0 | sp800_108_param = KDF_PARAM_SHA1; |
656 | 0 | } else if (strcmp(kdf_parameters.hash_algorithm, "SHA256") == 0) |
657 | 0 | { |
658 | 0 | sp800_108_param = KDF_PARAM_SHA256; |
659 | 0 | } else if (strcmp(kdf_parameters.hash_algorithm, "SHA384") == 0) |
660 | 0 | { |
661 | 0 | sp800_108_param = KDF_PARAM_SHA384; |
662 | 0 | } else if (strcmp(kdf_parameters.hash_algorithm, "SHA512") == 0) |
663 | 0 | { |
664 | 0 | sp800_108_param = KDF_PARAM_SHA512; |
665 | 0 | } else { |
666 | 0 | status = NT_STATUS_NOT_SUPPORTED; |
667 | 0 | goto out; |
668 | 0 | } |
669 | 0 | } |
670 | | |
671 | 0 | *kdf_algorithm_out = (struct KdfAlgorithm){ |
672 | 0 | .id = KDF_ALGORITHM_SP800_108_CTR_HMAC, |
673 | 0 | .param.sp800_108 = sp800_108_param, |
674 | 0 | }; |
675 | 0 | out: |
676 | 0 | talloc_free(tmp_ctx); |
677 | 0 | return status; |
678 | 0 | } |
679 | | |
680 | | NTSTATUS kdf_algorithm_from_params(const char *const kdf_algorithm_id, |
681 | | const DATA_BLOB *const kdf_param, |
682 | | struct KdfAlgorithm *const kdf_algorithm_out) |
683 | 0 | { |
684 | 0 | if (kdf_algorithm_id == NULL) { |
685 | 0 | return NT_STATUS_INVALID_PARAMETER; |
686 | 0 | } |
687 | | |
688 | | /* This string comparison is case‐sensitive. */ |
689 | 0 | if (strcmp(kdf_algorithm_id, "SP800_108_CTR_HMAC") == 0) { |
690 | 0 | return kdf_sp_800_108_from_params(kdf_param, kdf_algorithm_out); |
691 | 0 | } |
692 | | |
693 | | /* Unknown algorithm. */ |
694 | 0 | return NT_STATUS_NOT_SUPPORTED; |
695 | 0 | } |