2 Unix SMB/CIFS implementation.
3 Group Key Distribution Protocol functions
5 Copyright (C) Catalyst.Net Ltd 2023
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.
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.
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/>.
22 #include <gnutls/gnutls.h>
23 #include <gnutls/crypto.h>
25 #include "lib/crypto/gnutls_helpers.h"
27 #include "lib/util/bytearray.h"
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"
34 #include "lib/crypto/gkdi.h"
35 #include "lib/util/data_blob.h"
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,
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)
47 /* Catch out‐of‐range indices. */
48 if (l0_idx > INT32_MAX || l1_idx > INT8_MAX || l2_idx > INT8_MAX) {
52 return Gkid(l0_idx, l1_idx, l2_idx);
55 NTSTATUS gkdi_pull_KeyEnvelope(TALLOC_CTX *mem_ctx,
56 const DATA_BLOB *key_env_blob,
57 struct KeyEnvelope *key_env_out)
59 NTSTATUS status = NT_STATUS_OK;
60 enum ndr_err_code err;
62 if (key_env_blob == NULL) {
63 return NT_STATUS_INVALID_PARAMETER;
66 if (key_env_out == NULL) {
67 return NT_STATUS_INVALID_PARAMETER;
70 err = ndr_pull_struct_blob(key_env_blob,
73 (ndr_pull_flags_fn_t)ndr_pull_KeyEnvelope);
74 status = ndr_map_error2ntstatus(err);
75 if (!NT_STATUS_IS_OK(status)) {
79 /* If we felt so inclined, we could check the version field here. */
85 * Retrieve the GKID and root key ID from a KeyEnvelope blob. The returned
86 * structure is guaranteed to have a valid GKID.
88 const struct KeyEnvelopeId *gkdi_pull_KeyEnvelopeId(
89 const DATA_BLOB key_env_blob,
90 struct KeyEnvelopeId *key_env_out)
92 TALLOC_CTX *tmp_ctx = NULL;
93 struct KeyEnvelope key_env;
94 const struct KeyEnvelopeId *key_env_ret = NULL;
97 if (key_env_out == NULL) {
101 tmp_ctx = talloc_new(NULL);
102 if (tmp_ctx == NULL) {
106 status = gkdi_pull_KeyEnvelope(tmp_ctx, &key_env_blob, &key_env);
107 if (!NT_STATUS_IS_OK(status)) {
112 const struct Gkid gkid = gkid_from_u32_indices(
113 key_env.l0_index, key_env.l1_index, key_env.l2_index);
114 if (!gkid_is_valid(gkid)) {
115 /* The KeyId is not valid: we can’t use it. */
119 *key_env_out = (struct KeyEnvelopeId){
120 .root_key_id = key_env.root_key_id, .gkid = gkid};
123 /* Return a pointer to the buffer passed in by the caller. */
124 key_env_ret = key_env_out;
127 TALLOC_FREE(tmp_ctx);
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)
141 NTSTATUS status = NT_STATUS_OK;
142 struct ProvRootKey *root_key = NULL;
144 if (root_key_out == NULL) {
145 return NT_STATUS_INVALID_PARAMETER;
147 *root_key_out = NULL;
149 root_key = talloc(mem_ctx, struct ProvRootKey);
150 if (root_key == NULL) {
151 return NT_STATUS_NO_MEMORY;
154 *root_key = (struct ProvRootKey){
156 .data = {.data = talloc_steal(root_key, root_key_data.data),
157 .length = root_key_data.length},
158 .create_time = create_time,
159 .use_start_time = use_start_time,
160 .domain_id = talloc_steal(root_key, domain_id),
161 .kdf_algorithm = kdf_algorithm,
165 *root_key_out = root_key;
169 struct Gkid gkdi_get_interval_id(const NTTIME time)
171 return Gkid(time / (gkdi_l1_key_iteration * gkdi_l2_key_iteration *
172 gkdi_key_cycle_duration),
173 time / (gkdi_l2_key_iteration * gkdi_key_cycle_duration) %
174 gkdi_l1_key_iteration,
175 time / gkdi_key_cycle_duration % gkdi_l2_key_iteration);
178 bool gkdi_get_key_start_time(const struct Gkid gkid, NTTIME *start_time_out)
180 if (!gkid_is_valid(gkid)) {
185 enum GkidType key_type = gkid_key_type(gkid);
186 if (key_type != GKID_L2_SEED_KEY) {
193 * Make sure that the GKID is not so large its start time can’t
194 * be represented in NTTIME.
196 static const struct Gkid max_gkid = {
198 (gkdi_l1_key_iteration * gkdi_l2_key_iteration *
199 gkdi_key_cycle_duration),
201 (gkdi_l2_key_iteration *
202 gkdi_key_cycle_duration) %
203 gkdi_l1_key_iteration,
204 UINT64_MAX / gkdi_key_cycle_duration %
205 gkdi_l2_key_iteration};
206 if (!gkid_less_than_or_equal_to(gkid, max_gkid)) {
211 *start_time_out = ((uint64_t)gkid.l0_idx * gkdi_l1_key_iteration *
212 gkdi_l2_key_iteration +
213 (uint64_t)gkid.l1_idx * gkdi_l2_key_iteration +
214 (uint64_t)gkid.l2_idx) *
215 gkdi_key_cycle_duration;
220 * This returns the equivalent of
221 * gkdi_get_key_start_time(gkdi_get_interval_id(time)).
223 NTTIME gkdi_get_interval_start_time(const NTTIME time)
225 return time / gkdi_key_cycle_duration * gkdi_key_cycle_duration;
228 bool gkid_less_than_or_equal_to(const struct Gkid g1, const struct Gkid g2)
230 if (g1.l0_idx != g2.l0_idx) {
231 return g1.l0_idx < g2.l0_idx;
234 if (g1.l1_idx != g2.l1_idx) {
235 return g1.l1_idx < g2.l1_idx;
238 return g1.l2_idx <= g2.l2_idx;
241 bool gkdi_rollover_interval(const int64_t managed_password_interval,
244 if (managed_password_interval < 0) {
248 *result = (uint64_t)managed_password_interval * 24 / 10 *
249 gkdi_key_cycle_duration;
253 struct GkdiContextShort {
254 uint8_t buf[sizeof((struct GUID_ndr_buf){}.buf) + sizeof(int32_t) +
255 sizeof(int32_t) + sizeof(int32_t)];
258 static NTSTATUS make_gkdi_context(const struct GkdiDerivationCtx *ctx,
259 struct GkdiContextShort *out_ctx)
261 enum ndr_err_code ndr_err;
262 DATA_BLOB b = {.data = out_ctx->buf, .length = sizeof out_ctx->buf};
264 if (ctx->target_security_descriptor.length) {
265 return NT_STATUS_INVALID_PARAMETER;
268 ndr_err = ndr_push_struct_into_fixed_blob(
269 &b, ctx, (ndr_push_flags_fn_t)ndr_push_GkdiDerivationCtx);
270 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
271 return ndr_map_error2ntstatus(ndr_err);
277 static NTSTATUS make_gkdi_context_security_descriptor(
279 const struct GkdiDerivationCtx *ctx,
280 const DATA_BLOB security_descriptor,
283 enum ndr_err_code ndr_err;
284 struct GkdiDerivationCtx ctx_with_sd = *ctx;
286 if (ctx_with_sd.target_security_descriptor.length != 0) {
287 return NT_STATUS_INVALID_PARAMETER;
290 ctx_with_sd.target_security_descriptor = security_descriptor;
292 ndr_err = ndr_push_struct_blob(out_ctx,
295 (ndr_push_flags_fn_t)
296 ndr_push_GkdiDerivationCtx);
297 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
298 return ndr_map_error2ntstatus(ndr_err);
305 struct GkdiDerivationCtx ctx;
306 gnutls_mac_algorithm_t algorithm;
309 gnutls_mac_algorithm_t get_sp800_108_mac_algorithm(
310 const struct KdfAlgorithm kdf_algorithm)
312 switch (kdf_algorithm.id) {
313 case KDF_ALGORITHM_SP800_108_CTR_HMAC:
314 switch (kdf_algorithm.param.sp800_108) {
316 return GNUTLS_MAC_SHA1;
317 case KDF_PARAM_SHA256:
318 return GNUTLS_MAC_SHA256;
319 case KDF_PARAM_SHA384:
320 return GNUTLS_MAC_SHA384;
321 case KDF_PARAM_SHA512:
322 return GNUTLS_MAC_SHA512;
327 return GNUTLS_MAC_UNKNOWN;
330 static NTSTATUS GkdiContext(const struct ProvRootKey *const root_key,
331 struct GkdiContext *const ctx)
333 NTSTATUS status = NT_STATUS_OK;
334 gnutls_mac_algorithm_t algorithm = GNUTLS_MAC_UNKNOWN;
337 status = NT_STATUS_INVALID_PARAMETER;
341 if (root_key == NULL) {
342 status = NT_STATUS_INVALID_PARAMETER;
346 if (root_key->version != root_key_version_1) {
347 status = NT_STATUS_NOT_SUPPORTED;
351 if (root_key->data.length != GKDI_KEY_LEN) {
352 status = NT_STATUS_NOT_SUPPORTED;
356 algorithm = get_sp800_108_mac_algorithm(root_key->kdf_algorithm);
357 if (algorithm == GNUTLS_MAC_UNKNOWN) {
358 status = NT_STATUS_NOT_SUPPORTED;
363 * The context comprises the GUID corresponding to the root key, the
364 * GKID (which we shall initialize to zero), and the encoded target
365 * security descriptor (which will initially be empty).
367 *ctx = (struct GkdiContext){
368 .ctx = {.guid = root_key->id,
372 .target_security_descriptor = {}},
373 .algorithm = algorithm,
379 static NTSTATUS compute_l1_seed_key(TALLOC_CTX *mem_ctx,
380 struct GkdiContext *ctx,
381 const DATA_BLOB security_descriptor,
382 const struct ProvRootKey *const root_key,
383 const struct Gkid gkid,
384 uint8_t key[static const GKDI_KEY_LEN])
386 NTSTATUS status = NT_STATUS_OK;
387 struct GkdiContextShort short_ctx;
390 ctx->ctx.l0_idx = gkid.l0_idx;
391 ctx->ctx.l1_idx = -1;
392 ctx->ctx.l2_idx = -1;
394 status = make_gkdi_context(&ctx->ctx, &short_ctx);
395 if (!NT_STATUS_IS_OK(status)) {
399 /* Derive an L0 seed key with GKID = (L0, −1, −1). */
401 status = samba_gnutls_sp800_108_derive_key(root_key->data.data,
402 root_key->data.length,
408 sizeof short_ctx.buf,
412 if (!NT_STATUS_IS_OK(status)) {
416 /* Derive an L1 seed key with GKID = (L0, 31, −1). */
418 ctx->ctx.l1_idx = 31;
421 DATA_BLOB security_descriptor_ctx;
423 status = make_gkdi_context_security_descriptor(
427 &security_descriptor_ctx);
428 if (!NT_STATUS_IS_OK(status)) {
432 status = samba_gnutls_sp800_108_derive_key(
439 security_descriptor_ctx.data,
440 security_descriptor_ctx.length,
444 data_blob_free(&security_descriptor_ctx);
445 if (!NT_STATUS_IS_OK(status)) {
450 for (n = 30; n >= gkid.l1_idx; --n) {
451 /* Derive an L1 seed key with GKID = (L0, n, −1). */
455 status = make_gkdi_context(&ctx->ctx, &short_ctx);
456 if (!NT_STATUS_IS_OK(status)) {
460 status = samba_gnutls_sp800_108_derive_key(key,
467 sizeof short_ctx.buf,
471 if (!NT_STATUS_IS_OK(status)) {
480 static NTSTATUS derive_l2_seed_key(struct GkdiContext *ctx,
481 const struct Gkid gkid,
482 uint8_t key[static const GKDI_KEY_LEN])
484 NTSTATUS status = NT_STATUS_OK;
487 ctx->ctx.l0_idx = gkid.l0_idx;
488 ctx->ctx.l1_idx = gkid.l1_idx;
490 for (n = 31; n >= gkid.l2_idx; --n) {
491 struct GkdiContextShort short_ctx;
493 /* Derive an L2 seed key with GKID = (L0, L1, n). */
497 status = make_gkdi_context(&ctx->ctx, &short_ctx);
498 if (!NT_STATUS_IS_OK(status)) {
502 status = samba_gnutls_sp800_108_derive_key(key,
509 sizeof short_ctx.buf,
513 if (!NT_STATUS_IS_OK(status)) {
522 enum GkidType gkid_key_type(const struct Gkid gkid)
524 if (gkid.l0_idx == -1) {
528 if (gkid.l1_idx == -1) {
529 return GKID_L0_SEED_KEY;
532 if (gkid.l2_idx == -1) {
533 return GKID_L1_SEED_KEY;
536 return GKID_L2_SEED_KEY;
539 bool gkid_is_valid(const struct Gkid gkid)
541 if (gkid.l0_idx < -1) {
545 if (gkid.l1_idx < -1 || gkid.l1_idx >= gkdi_l1_key_iteration) {
549 if (gkid.l2_idx < -1 || gkid.l2_idx >= gkdi_l2_key_iteration) {
553 if (gkid.l0_idx == -1 && gkid.l1_idx != -1) {
557 if (gkid.l1_idx == -1 && gkid.l2_idx != -1) {
564 NTSTATUS compute_seed_key(TALLOC_CTX *mem_ctx,
565 const DATA_BLOB target_security_descriptor,
566 const struct ProvRootKey *const root_key,
567 const struct Gkid gkid,
568 uint8_t key[static const GKDI_KEY_LEN])
570 NTSTATUS status = NT_STATUS_OK;
571 enum GkidType gkid_type;
572 struct GkdiContext ctx;
574 if (!gkid_is_valid(gkid)) {
575 status = NT_STATUS_INVALID_PARAMETER;
579 gkid_type = gkid_key_type(gkid);
580 if (gkid_type < GKID_L1_SEED_KEY) {
581 /* Don’t allow derivation of L0 seed keys. */
582 status = NT_STATUS_INVALID_PARAMETER;
586 status = GkdiContext(root_key, &ctx);
587 if (!NT_STATUS_IS_OK(status)) {
591 status = compute_l1_seed_key(
592 mem_ctx, &ctx, target_security_descriptor, root_key, gkid, key);
593 if (!NT_STATUS_IS_OK(status)) {
597 if (gkid_type == GKID_L2_SEED_KEY) {
598 status = derive_l2_seed_key(&ctx, gkid, key);
599 if (!NT_STATUS_IS_OK(status)) {
608 NTSTATUS kdf_sp_800_108_from_params(
609 const DATA_BLOB *const kdf_param,
610 struct KdfAlgorithm *const kdf_algorithm_out)
612 TALLOC_CTX *tmp_ctx = NULL;
613 NTSTATUS status = NT_STATUS_OK;
614 enum ndr_err_code err;
615 enum KdfSp800_108Param sp800_108_param = KDF_PARAM_SHA256;
616 struct KdfParameters kdf_parameters;
618 if (kdf_param != NULL) {
619 tmp_ctx = talloc_new(NULL);
620 if (tmp_ctx == NULL) {
621 status = NT_STATUS_NO_MEMORY;
625 err = ndr_pull_struct_blob(kdf_param,
628 (ndr_pull_flags_fn_t)
629 ndr_pull_KdfParameters);
630 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
631 status = ndr_map_error2ntstatus(err);
632 DBG_WARNING("KdfParameters pull failed: %s\n",
637 if (kdf_parameters.hash_algorithm == NULL) {
638 status = NT_STATUS_NOT_SUPPORTED;
642 /* These string comparisons are case‐sensitive. */
643 if (strcmp(kdf_parameters.hash_algorithm, "SHA1") == 0) {
644 sp800_108_param = KDF_PARAM_SHA1;
645 } else if (strcmp(kdf_parameters.hash_algorithm, "SHA256") == 0)
647 sp800_108_param = KDF_PARAM_SHA256;
648 } else if (strcmp(kdf_parameters.hash_algorithm, "SHA384") == 0)
650 sp800_108_param = KDF_PARAM_SHA384;
651 } else if (strcmp(kdf_parameters.hash_algorithm, "SHA512") == 0)
653 sp800_108_param = KDF_PARAM_SHA512;
655 status = NT_STATUS_NOT_SUPPORTED;
660 *kdf_algorithm_out = (struct KdfAlgorithm){
661 .id = KDF_ALGORITHM_SP800_108_CTR_HMAC,
662 .param.sp800_108 = sp800_108_param,
665 talloc_free(tmp_ctx);
669 NTSTATUS kdf_algorithm_from_params(const char *const kdf_algorithm_id,
670 const DATA_BLOB *const kdf_param,
671 struct KdfAlgorithm *const kdf_algorithm_out)
673 if (kdf_algorithm_id == NULL) {
674 return NT_STATUS_INVALID_PARAMETER;
677 /* This string comparison is case‐sensitive. */
678 if (strcmp(kdf_algorithm_id, "SP800_108_CTR_HMAC") == 0) {
679 return kdf_sp_800_108_from_params(kdf_param, kdf_algorithm_out);
682 /* Unknown algorithm. */
683 return NT_STATUS_NOT_SUPPORTED;