66e1da18f344be8abbc66179c4eda4c40a8e483f
[gd/samba-autobuild/.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         if (managed_password_interval < 0) {
245                 return false;
246         }
247
248         *result = (uint64_t)managed_password_interval * 24 / 10 *
249                   gkdi_key_cycle_duration;
250         return true;
251 }
252
253 struct GkdiContextShort {
254         uint8_t buf[sizeof((struct GUID_ndr_buf){}.buf) + sizeof(int32_t) +
255                     sizeof(int32_t) + sizeof(int32_t)];
256 };
257
258 static NTSTATUS make_gkdi_context(const struct GkdiDerivationCtx *ctx,
259                                   struct GkdiContextShort *out_ctx)
260 {
261         enum ndr_err_code ndr_err;
262         DATA_BLOB b = {.data = out_ctx->buf, .length = sizeof out_ctx->buf};
263
264         if (ctx->target_security_descriptor.length) {
265                 return NT_STATUS_INVALID_PARAMETER;
266         }
267
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);
272         }
273
274         return NT_STATUS_OK;
275 }
276
277 static NTSTATUS make_gkdi_context_security_descriptor(
278         TALLOC_CTX *mem_ctx,
279         const struct GkdiDerivationCtx *ctx,
280         const DATA_BLOB security_descriptor,
281         DATA_BLOB *out_ctx)
282 {
283         enum ndr_err_code ndr_err;
284         struct GkdiDerivationCtx ctx_with_sd = *ctx;
285
286         if (ctx_with_sd.target_security_descriptor.length != 0) {
287                 return NT_STATUS_INVALID_PARAMETER;
288         }
289
290         ctx_with_sd.target_security_descriptor = security_descriptor;
291
292         ndr_err = ndr_push_struct_blob(out_ctx,
293                                        mem_ctx,
294                                        &ctx_with_sd,
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);
299         }
300
301         return NT_STATUS_OK;
302 }
303
304 struct GkdiContext {
305         struct GkdiDerivationCtx ctx;
306         gnutls_mac_algorithm_t algorithm;
307 };
308
309 gnutls_mac_algorithm_t get_sp800_108_mac_algorithm(
310         const struct KdfAlgorithm kdf_algorithm)
311 {
312         switch (kdf_algorithm.id) {
313         case KDF_ALGORITHM_SP800_108_CTR_HMAC:
314                 switch (kdf_algorithm.param.sp800_108) {
315                 case KDF_PARAM_SHA1:
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;
323                 }
324                 break;
325         }
326
327         return GNUTLS_MAC_UNKNOWN;
328 }
329
330 static NTSTATUS GkdiContext(const struct ProvRootKey *const root_key,
331                             struct GkdiContext *const ctx)
332 {
333         NTSTATUS status = NT_STATUS_OK;
334         gnutls_mac_algorithm_t algorithm = GNUTLS_MAC_UNKNOWN;
335
336         if (ctx == NULL) {
337                 status = NT_STATUS_INVALID_PARAMETER;
338                 goto out;
339         }
340
341         if (root_key == NULL) {
342                 status = NT_STATUS_INVALID_PARAMETER;
343                 goto out;
344         }
345
346         if (root_key->version != root_key_version_1) {
347                 status = NT_STATUS_NOT_SUPPORTED;
348                 goto out;
349         }
350
351         if (root_key->data.length != GKDI_KEY_LEN) {
352                 status = NT_STATUS_NOT_SUPPORTED;
353                 goto out;
354         }
355
356         algorithm = get_sp800_108_mac_algorithm(root_key->kdf_algorithm);
357         if (algorithm == GNUTLS_MAC_UNKNOWN) {
358                 status = NT_STATUS_NOT_SUPPORTED;
359                 goto out;
360         }
361
362         /*
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).
366          */
367         *ctx = (struct GkdiContext){
368                 .ctx = {.guid = root_key->id,
369                         .l0_idx = 0,
370                         .l1_idx = 0,
371                         .l2_idx = 0,
372                         .target_security_descriptor = {}},
373                 .algorithm = algorithm,
374         };
375 out:
376         return status;
377 }
378
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])
385 {
386         NTSTATUS status = NT_STATUS_OK;
387         struct GkdiContextShort short_ctx;
388         int8_t n;
389
390         ctx->ctx.l0_idx = gkid.l0_idx;
391         ctx->ctx.l1_idx = -1;
392         ctx->ctx.l2_idx = -1;
393
394         status = make_gkdi_context(&ctx->ctx, &short_ctx);
395         if (!NT_STATUS_IS_OK(status)) {
396                 goto out;
397         }
398
399         /* Derive an L0 seed key with GKID = (L0, −1, −1). */
400
401         status = samba_gnutls_sp800_108_derive_key(root_key->data.data,
402                                                    root_key->data.length,
403                                                    NULL,
404                                                    0,
405                                                    kds_service,
406                                                    sizeof kds_service,
407                                                    short_ctx.buf,
408                                                    sizeof short_ctx.buf,
409                                                    ctx->algorithm,
410                                                    key,
411                                                    GKDI_KEY_LEN);
412         if (!NT_STATUS_IS_OK(status)) {
413                 goto out;
414         }
415
416         /* Derive an L1 seed key with GKID = (L0, 31, −1). */
417
418         ctx->ctx.l1_idx = 31;
419
420         {
421                 DATA_BLOB security_descriptor_ctx;
422
423                 status = make_gkdi_context_security_descriptor(
424                         mem_ctx,
425                         &ctx->ctx,
426                         security_descriptor,
427                         &security_descriptor_ctx);
428                 if (!NT_STATUS_IS_OK(status)) {
429                         goto out;
430                 }
431
432                 status = samba_gnutls_sp800_108_derive_key(
433                         key,
434                         GKDI_KEY_LEN,
435                         NULL,
436                         0,
437                         kds_service,
438                         sizeof kds_service,
439                         security_descriptor_ctx.data,
440                         security_descriptor_ctx.length,
441                         ctx->algorithm,
442                         key,
443                         GKDI_KEY_LEN);
444                 data_blob_free(&security_descriptor_ctx);
445                 if (!NT_STATUS_IS_OK(status)) {
446                         goto out;
447                 }
448         }
449
450         for (n = 30; n >= gkid.l1_idx; --n) {
451                 /* Derive an L1 seed key with GKID = (L0, n, −1). */
452
453                 ctx->ctx.l1_idx = n;
454
455                 status = make_gkdi_context(&ctx->ctx, &short_ctx);
456                 if (!NT_STATUS_IS_OK(status)) {
457                         goto out;
458                 }
459
460                 status = samba_gnutls_sp800_108_derive_key(key,
461                                                            GKDI_KEY_LEN,
462                                                            NULL,
463                                                            0,
464                                                            kds_service,
465                                                            sizeof kds_service,
466                                                            short_ctx.buf,
467                                                            sizeof short_ctx.buf,
468                                                            ctx->algorithm,
469                                                            key,
470                                                            GKDI_KEY_LEN);
471                 if (!NT_STATUS_IS_OK(status)) {
472                         goto out;
473                 }
474         }
475
476 out:
477         return status;
478 }
479
480 static NTSTATUS derive_l2_seed_key(struct GkdiContext *ctx,
481                                    const struct Gkid gkid,
482                                    uint8_t key[static const GKDI_KEY_LEN])
483 {
484         NTSTATUS status = NT_STATUS_OK;
485         int8_t n;
486
487         ctx->ctx.l0_idx = gkid.l0_idx;
488         ctx->ctx.l1_idx = gkid.l1_idx;
489
490         for (n = 31; n >= gkid.l2_idx; --n) {
491                 struct GkdiContextShort short_ctx;
492
493                 /* Derive an L2 seed key with GKID = (L0, L1, n). */
494
495                 ctx->ctx.l2_idx = n;
496
497                 status = make_gkdi_context(&ctx->ctx, &short_ctx);
498                 if (!NT_STATUS_IS_OK(status)) {
499                         goto out;
500                 }
501
502                 status = samba_gnutls_sp800_108_derive_key(key,
503                                                            GKDI_KEY_LEN,
504                                                            NULL,
505                                                            0,
506                                                            kds_service,
507                                                            sizeof kds_service,
508                                                            short_ctx.buf,
509                                                            sizeof short_ctx.buf,
510                                                            ctx->algorithm,
511                                                            key,
512                                                            GKDI_KEY_LEN);
513                 if (!NT_STATUS_IS_OK(status)) {
514                         goto out;
515                 }
516         }
517
518 out:
519         return status;
520 }
521
522 enum GkidType gkid_key_type(const struct Gkid gkid)
523 {
524         if (gkid.l0_idx == -1) {
525                 return GKID_DEFAULT;
526         }
527
528         if (gkid.l1_idx == -1) {
529                 return GKID_L0_SEED_KEY;
530         }
531
532         if (gkid.l2_idx == -1) {
533                 return GKID_L1_SEED_KEY;
534         }
535
536         return GKID_L2_SEED_KEY;
537 }
538
539 bool gkid_is_valid(const struct Gkid gkid)
540 {
541         if (gkid.l0_idx < -1) {
542                 return false;
543         }
544
545         if (gkid.l1_idx < -1 || gkid.l1_idx >= gkdi_l1_key_iteration) {
546                 return false;
547         }
548
549         if (gkid.l2_idx < -1 || gkid.l2_idx >= gkdi_l2_key_iteration) {
550                 return false;
551         }
552
553         if (gkid.l0_idx == -1 && gkid.l1_idx != -1) {
554                 return false;
555         }
556
557         if (gkid.l1_idx == -1 && gkid.l2_idx != -1) {
558                 return false;
559         }
560
561         return true;
562 }
563
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])
569 {
570         NTSTATUS status = NT_STATUS_OK;
571         enum GkidType gkid_type;
572         struct GkdiContext ctx;
573
574         if (!gkid_is_valid(gkid)) {
575                 status = NT_STATUS_INVALID_PARAMETER;
576                 goto out;
577         }
578
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;
583                 goto out;
584         }
585
586         status = GkdiContext(root_key, &ctx);
587         if (!NT_STATUS_IS_OK(status)) {
588                 goto out;
589         }
590
591         status = compute_l1_seed_key(
592                 mem_ctx, &ctx, target_security_descriptor, root_key, gkid, key);
593         if (!NT_STATUS_IS_OK(status)) {
594                 goto out;
595         }
596
597         if (gkid_type == GKID_L2_SEED_KEY) {
598                 status = derive_l2_seed_key(&ctx, gkid, key);
599                 if (!NT_STATUS_IS_OK(status)) {
600                         goto out;
601                 }
602         }
603
604 out:
605         return status;
606 }
607
608 NTSTATUS kdf_sp_800_108_from_params(
609         const DATA_BLOB *const kdf_param,
610         struct KdfAlgorithm *const kdf_algorithm_out)
611 {
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;
617
618         if (kdf_param != NULL) {
619                 tmp_ctx = talloc_new(NULL);
620                 if (tmp_ctx == NULL) {
621                         status = NT_STATUS_NO_MEMORY;
622                         goto out;
623                 }
624
625                 err = ndr_pull_struct_blob(kdf_param,
626                                            tmp_ctx,
627                                            &kdf_parameters,
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",
633                                     nt_errstr(status));
634                         goto out;
635                 }
636
637                 if (kdf_parameters.hash_algorithm == NULL) {
638                         status = NT_STATUS_NOT_SUPPORTED;
639                         goto out;
640                 }
641
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)
646                 {
647                         sp800_108_param = KDF_PARAM_SHA256;
648                 } else if (strcmp(kdf_parameters.hash_algorithm, "SHA384") == 0)
649                 {
650                         sp800_108_param = KDF_PARAM_SHA384;
651                 } else if (strcmp(kdf_parameters.hash_algorithm, "SHA512") == 0)
652                 {
653                         sp800_108_param = KDF_PARAM_SHA512;
654                 } else {
655                         status = NT_STATUS_NOT_SUPPORTED;
656                         goto out;
657                 }
658         }
659
660         *kdf_algorithm_out = (struct KdfAlgorithm){
661                 .id = KDF_ALGORITHM_SP800_108_CTR_HMAC,
662                 .param.sp800_108 = sp800_108_param,
663         };
664 out:
665         talloc_free(tmp_ctx);
666         return status;
667 }
668
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)
672 {
673         if (kdf_algorithm_id == NULL) {
674                 return NT_STATUS_INVALID_PARAMETER;
675         }
676
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);
680         }
681
682         /* Unknown algorithm. */
683         return NT_STATUS_NOT_SUPPORTED;
684 }