af00ea4217eb28858cb973d15412dde9f16c648c
[samba.git] / lib / crypto / gkdi.c
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 {
47         /* Catch out‐of‐range indices. */
48         if (l0_idx > INT32_MAX || l1_idx > INT8_MAX || l2_idx > INT8_MAX) {
49                 return invalid_gkid;
50         }
51
52         return Gkid(l0_idx, l1_idx, l2_idx);
53 }
54
55 NTSTATUS gkdi_pull_KeyEnvelope(TALLOC_CTX *mem_ctx,
56                                const DATA_BLOB *key_env_blob,
57                                struct KeyEnvelope *key_env_out)
58 {
59         NTSTATUS status = NT_STATUS_OK;
60         enum ndr_err_code err;
61
62         if (key_env_blob == NULL) {
63                 return NT_STATUS_INVALID_PARAMETER;
64         }
65
66         if (key_env_out == NULL) {
67                 return NT_STATUS_INVALID_PARAMETER;
68         }
69
70         err = ndr_pull_struct_blob(key_env_blob,
71                                    mem_ctx,
72                                    key_env_out,
73                                    (ndr_pull_flags_fn_t)ndr_pull_KeyEnvelope);
74         status = ndr_map_error2ntstatus(err);
75         if (!NT_STATUS_IS_OK(status)) {
76                 return status;
77         }
78
79         /* If we felt so inclined, we could check the version field here. */
80
81         return status;
82 }
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 {
92         TALLOC_CTX *tmp_ctx = NULL;
93         struct KeyEnvelope key_env;
94         const struct KeyEnvelopeId *key_env_ret = NULL;
95         NTSTATUS status;
96
97         if (key_env_out == NULL) {
98                 goto out;
99         }
100
101         tmp_ctx = talloc_new(NULL);
102         if (tmp_ctx == NULL) {
103                 goto out;
104         }
105
106         status = gkdi_pull_KeyEnvelope(tmp_ctx, &key_env_blob, &key_env);
107         if (!NT_STATUS_IS_OK(status)) {
108                 goto out;
109         }
110
111         {
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. */
116                         goto out;
117                 }
118
119                 *key_env_out = (struct KeyEnvelopeId){
120                         .root_key_id = key_env.root_key_id, .gkid = gkid};
121         }
122
123         /* Return a pointer to the buffer passed in by the caller. */
124         key_env_ret = key_env_out;
125
126 out:
127         TALLOC_FREE(tmp_ctx);
128         return key_env_ret;
129 }
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 {
141         NTSTATUS status = NT_STATUS_OK;
142         struct ProvRootKey *root_key = NULL;
143
144         if (root_key_out == NULL) {
145                 return NT_STATUS_INVALID_PARAMETER;
146         }
147         *root_key_out = NULL;
148
149         root_key = talloc(mem_ctx, struct ProvRootKey);
150         if (root_key == NULL) {
151                 return NT_STATUS_NO_MEMORY;
152         }
153
154         *root_key = (struct ProvRootKey){
155                 .id = root_key_id,
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,
162                 .version = version,
163         };
164
165         *root_key_out = root_key;
166         return status;
167 }
168
169 struct Gkid gkdi_get_interval_id(const NTTIME time)
170 {
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);
176 }
177
178 bool gkdi_get_key_start_time(const struct Gkid gkid, NTTIME *start_time_out)
179 {
180         if (!gkid_is_valid(gkid)) {
181                 return false;
182         }
183
184         {
185                 enum GkidType key_type = gkid_key_type(gkid);
186                 if (key_type != GKID_L2_SEED_KEY) {
187                         return false;
188                 }
189         }
190
191         {
192                 /*
193                  * Make sure that the GKID is not so large its start time can’t
194                  * be represented in NTTIME.
195                  */
196                 static const struct Gkid max_gkid = {
197                         UINT64_MAX /
198                                 (gkdi_l1_key_iteration * gkdi_l2_key_iteration *
199                                  gkdi_key_cycle_duration),
200                         UINT64_MAX /
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)) {
207                         return false;
208                 }
209         }
210
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;
216         return true;
217 }
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 {
225         return time / gkdi_key_cycle_duration * gkdi_key_cycle_duration;
226 }
227
228 bool gkid_less_than_or_equal_to(const struct Gkid g1, const struct Gkid g2)
229 {
230         if (g1.l0_idx != g2.l0_idx) {
231                 return g1.l0_idx < g2.l0_idx;
232         }
233
234         if (g1.l1_idx != g2.l1_idx) {
235                 return g1.l1_idx < g2.l1_idx;
236         }
237
238         return g1.l2_idx <= g2.l2_idx;
239 }
240
241 bool gkdi_rollover_interval(const int64_t managed_password_interval,
242                             NTTIME *result)
243 {
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         const int64_t maximum_interval = UINT64_MAX / gkdi_key_cycle_duration *
251                                          10 / 24;
252
253         if (managed_password_interval < 0 ||
254             managed_password_interval > maximum_interval)
255         {
256                 return false;
257         }
258
259         *result = (uint64_t)managed_password_interval * 24 / 10 *
260                   gkdi_key_cycle_duration;
261         return true;
262 }
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 {
272         enum ndr_err_code ndr_err;
273         DATA_BLOB b = {.data = out_ctx->buf, .length = sizeof out_ctx->buf};
274
275         if (ctx->target_security_descriptor.length) {
276                 return NT_STATUS_INVALID_PARAMETER;
277         }
278
279         ndr_err = ndr_push_struct_into_fixed_blob(
280                 &b, ctx, (ndr_push_flags_fn_t)ndr_push_GkdiDerivationCtx);
281         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
282                 return ndr_map_error2ntstatus(ndr_err);
283         }
284
285         return NT_STATUS_OK;
286 }
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 {
294         enum ndr_err_code ndr_err;
295         struct GkdiDerivationCtx ctx_with_sd = *ctx;
296
297         if (ctx_with_sd.target_security_descriptor.length != 0) {
298                 return NT_STATUS_INVALID_PARAMETER;
299         }
300
301         ctx_with_sd.target_security_descriptor = security_descriptor;
302
303         ndr_err = ndr_push_struct_blob(out_ctx,
304                                        mem_ctx,
305                                        &ctx_with_sd,
306                                        (ndr_push_flags_fn_t)
307                                                ndr_push_GkdiDerivationCtx);
308         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
309                 return ndr_map_error2ntstatus(ndr_err);
310         }
311
312         return NT_STATUS_OK;
313 }
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 {
323         switch (kdf_algorithm.id) {
324         case KDF_ALGORITHM_SP800_108_CTR_HMAC:
325                 switch (kdf_algorithm.param.sp800_108) {
326                 case KDF_PARAM_SHA1:
327                         return GNUTLS_MAC_SHA1;
328                 case KDF_PARAM_SHA256:
329                         return GNUTLS_MAC_SHA256;
330                 case KDF_PARAM_SHA384:
331                         return GNUTLS_MAC_SHA384;
332                 case KDF_PARAM_SHA512:
333                         return GNUTLS_MAC_SHA512;
334                 }
335                 break;
336         }
337
338         return GNUTLS_MAC_UNKNOWN;
339 }
340
341 static NTSTATUS GkdiContext(const struct ProvRootKey *const root_key,
342                             struct GkdiContext *const ctx)
343 {
344         NTSTATUS status = NT_STATUS_OK;
345         gnutls_mac_algorithm_t algorithm = GNUTLS_MAC_UNKNOWN;
346
347         if (ctx == NULL) {
348                 status = NT_STATUS_INVALID_PARAMETER;
349                 goto out;
350         }
351
352         if (root_key == NULL) {
353                 status = NT_STATUS_INVALID_PARAMETER;
354                 goto out;
355         }
356
357         if (root_key->version != root_key_version_1) {
358                 status = NT_STATUS_NOT_SUPPORTED;
359                 goto out;
360         }
361
362         if (root_key->data.length != GKDI_KEY_LEN) {
363                 status = NT_STATUS_NOT_SUPPORTED;
364                 goto out;
365         }
366
367         algorithm = get_sp800_108_mac_algorithm(root_key->kdf_algorithm);
368         if (algorithm == GNUTLS_MAC_UNKNOWN) {
369                 status = NT_STATUS_NOT_SUPPORTED;
370                 goto out;
371         }
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         *ctx = (struct GkdiContext){
379                 .ctx = {.guid = root_key->id,
380                         .l0_idx = 0,
381                         .l1_idx = 0,
382                         .l2_idx = 0,
383                         .target_security_descriptor = {}},
384                 .algorithm = algorithm,
385         };
386 out:
387         return status;
388 }
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 {
397         NTSTATUS status = NT_STATUS_OK;
398         struct GkdiContextShort short_ctx;
399         int8_t n;
400
401         ctx->ctx.l0_idx = gkid.l0_idx;
402         ctx->ctx.l1_idx = -1;
403         ctx->ctx.l2_idx = -1;
404
405         status = make_gkdi_context(&ctx->ctx, &short_ctx);
406         if (!NT_STATUS_IS_OK(status)) {
407                 goto out;
408         }
409
410         /* Derive an L0 seed key with GKID = (L0, −1, −1). */
411
412         status = samba_gnutls_sp800_108_derive_key(root_key->data.data,
413                                                    root_key->data.length,
414                                                    NULL,
415                                                    0,
416                                                    kds_service,
417                                                    sizeof kds_service,
418                                                    short_ctx.buf,
419                                                    sizeof short_ctx.buf,
420                                                    ctx->algorithm,
421                                                    key,
422                                                    GKDI_KEY_LEN);
423         if (!NT_STATUS_IS_OK(status)) {
424                 goto out;
425         }
426
427         /* Derive an L1 seed key with GKID = (L0, 31, −1). */
428
429         ctx->ctx.l1_idx = 31;
430
431         {
432                 DATA_BLOB security_descriptor_ctx;
433
434                 status = make_gkdi_context_security_descriptor(
435                         mem_ctx,
436                         &ctx->ctx,
437                         security_descriptor,
438                         &security_descriptor_ctx);
439                 if (!NT_STATUS_IS_OK(status)) {
440                         goto out;
441                 }
442
443                 status = samba_gnutls_sp800_108_derive_key(
444                         key,
445                         GKDI_KEY_LEN,
446                         NULL,
447                         0,
448                         kds_service,
449                         sizeof kds_service,
450                         security_descriptor_ctx.data,
451                         security_descriptor_ctx.length,
452                         ctx->algorithm,
453                         key,
454                         GKDI_KEY_LEN);
455                 data_blob_free(&security_descriptor_ctx);
456                 if (!NT_STATUS_IS_OK(status)) {
457                         goto out;
458                 }
459         }
460
461         for (n = 30; n >= gkid.l1_idx; --n) {
462                 /* Derive an L1 seed key with GKID = (L0, n, −1). */
463
464                 ctx->ctx.l1_idx = n;
465
466                 status = make_gkdi_context(&ctx->ctx, &short_ctx);
467                 if (!NT_STATUS_IS_OK(status)) {
468                         goto out;
469                 }
470
471                 status = samba_gnutls_sp800_108_derive_key(key,
472                                                            GKDI_KEY_LEN,
473                                                            NULL,
474                                                            0,
475                                                            kds_service,
476                                                            sizeof kds_service,
477                                                            short_ctx.buf,
478                                                            sizeof short_ctx.buf,
479                                                            ctx->algorithm,
480                                                            key,
481                                                            GKDI_KEY_LEN);
482                 if (!NT_STATUS_IS_OK(status)) {
483                         goto out;
484                 }
485         }
486
487 out:
488         return status;
489 }
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 {
495         NTSTATUS status = NT_STATUS_OK;
496         int8_t n;
497
498         ctx->ctx.l0_idx = gkid.l0_idx;
499         ctx->ctx.l1_idx = gkid.l1_idx;
500
501         for (n = 31; n >= gkid.l2_idx; --n) {
502                 struct GkdiContextShort short_ctx;
503
504                 /* Derive an L2 seed key with GKID = (L0, L1, n). */
505
506                 ctx->ctx.l2_idx = n;
507
508                 status = make_gkdi_context(&ctx->ctx, &short_ctx);
509                 if (!NT_STATUS_IS_OK(status)) {
510                         goto out;
511                 }
512
513                 status = samba_gnutls_sp800_108_derive_key(key,
514                                                            GKDI_KEY_LEN,
515                                                            NULL,
516                                                            0,
517                                                            kds_service,
518                                                            sizeof kds_service,
519                                                            short_ctx.buf,
520                                                            sizeof short_ctx.buf,
521                                                            ctx->algorithm,
522                                                            key,
523                                                            GKDI_KEY_LEN);
524                 if (!NT_STATUS_IS_OK(status)) {
525                         goto out;
526                 }
527         }
528
529 out:
530         return status;
531 }
532
533 enum GkidType gkid_key_type(const struct Gkid gkid)
534 {
535         if (gkid.l0_idx == -1) {
536                 return GKID_DEFAULT;
537         }
538
539         if (gkid.l1_idx == -1) {
540                 return GKID_L0_SEED_KEY;
541         }
542
543         if (gkid.l2_idx == -1) {
544                 return GKID_L1_SEED_KEY;
545         }
546
547         return GKID_L2_SEED_KEY;
548 }
549
550 bool gkid_is_valid(const struct Gkid gkid)
551 {
552         if (gkid.l0_idx < -1) {
553                 return false;
554         }
555
556         if (gkid.l1_idx < -1 || gkid.l1_idx >= gkdi_l1_key_iteration) {
557                 return false;
558         }
559
560         if (gkid.l2_idx < -1 || gkid.l2_idx >= gkdi_l2_key_iteration) {
561                 return false;
562         }
563
564         if (gkid.l0_idx == -1 && gkid.l1_idx != -1) {
565                 return false;
566         }
567
568         if (gkid.l1_idx == -1 && gkid.l2_idx != -1) {
569                 return false;
570         }
571
572         return true;
573 }
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 {
581         NTSTATUS status = NT_STATUS_OK;
582         enum GkidType gkid_type;
583         struct GkdiContext ctx;
584
585         if (!gkid_is_valid(gkid)) {
586                 status = NT_STATUS_INVALID_PARAMETER;
587                 goto out;
588         }
589
590         gkid_type = gkid_key_type(gkid);
591         if (gkid_type < GKID_L1_SEED_KEY) {
592                 /* Don’t allow derivation of L0 seed keys. */
593                 status = NT_STATUS_INVALID_PARAMETER;
594                 goto out;
595         }
596
597         status = GkdiContext(root_key, &ctx);
598         if (!NT_STATUS_IS_OK(status)) {
599                 goto out;
600         }
601
602         status = compute_l1_seed_key(
603                 mem_ctx, &ctx, target_security_descriptor, root_key, gkid, key);
604         if (!NT_STATUS_IS_OK(status)) {
605                 goto out;
606         }
607
608         if (gkid_type == GKID_L2_SEED_KEY) {
609                 status = derive_l2_seed_key(&ctx, gkid, key);
610                 if (!NT_STATUS_IS_OK(status)) {
611                         goto out;
612                 }
613         }
614
615 out:
616         return status;
617 }
618
619 NTSTATUS kdf_sp_800_108_from_params(
620         const DATA_BLOB *const kdf_param,
621         struct KdfAlgorithm *const kdf_algorithm_out)
622 {
623         TALLOC_CTX *tmp_ctx = NULL;
624         NTSTATUS status = NT_STATUS_OK;
625         enum ndr_err_code err;
626         enum KdfSp800_108Param sp800_108_param = KDF_PARAM_SHA256;
627         struct KdfParameters kdf_parameters;
628
629         if (kdf_param != NULL) {
630                 tmp_ctx = talloc_new(NULL);
631                 if (tmp_ctx == NULL) {
632                         status = NT_STATUS_NO_MEMORY;
633                         goto out;
634                 }
635
636                 err = ndr_pull_struct_blob(kdf_param,
637                                            tmp_ctx,
638                                            &kdf_parameters,
639                                            (ndr_pull_flags_fn_t)
640                                                    ndr_pull_KdfParameters);
641                 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
642                         status = ndr_map_error2ntstatus(err);
643                         DBG_WARNING("KdfParameters pull failed: %s\n",
644                                     nt_errstr(status));
645                         goto out;
646                 }
647
648                 if (kdf_parameters.hash_algorithm == NULL) {
649                         status = NT_STATUS_NOT_SUPPORTED;
650                         goto out;
651                 }
652
653                 /* These string comparisons are case‐sensitive. */
654                 if (strcmp(kdf_parameters.hash_algorithm, "SHA1") == 0) {
655                         sp800_108_param = KDF_PARAM_SHA1;
656                 } else if (strcmp(kdf_parameters.hash_algorithm, "SHA256") == 0)
657                 {
658                         sp800_108_param = KDF_PARAM_SHA256;
659                 } else if (strcmp(kdf_parameters.hash_algorithm, "SHA384") == 0)
660                 {
661                         sp800_108_param = KDF_PARAM_SHA384;
662                 } else if (strcmp(kdf_parameters.hash_algorithm, "SHA512") == 0)
663                 {
664                         sp800_108_param = KDF_PARAM_SHA512;
665                 } else {
666                         status = NT_STATUS_NOT_SUPPORTED;
667                         goto out;
668                 }
669         }
670
671         *kdf_algorithm_out = (struct KdfAlgorithm){
672                 .id = KDF_ALGORITHM_SP800_108_CTR_HMAC,
673                 .param.sp800_108 = sp800_108_param,
674         };
675 out:
676         talloc_free(tmp_ctx);
677         return status;
678 }
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 {
684         if (kdf_algorithm_id == NULL) {
685                 return NT_STATUS_INVALID_PARAMETER;
686         }
687
688         /* This string comparison is case‐sensitive. */
689         if (strcmp(kdf_algorithm_id, "SP800_108_CTR_HMAC") == 0) {
690                 return kdf_sp_800_108_from_params(kdf_param, kdf_algorithm_out);
691         }
692
693         /* Unknown algorithm. */
694         return NT_STATUS_NOT_SUPPORTED;
695 }