Coverage Report

Created: 2025-07-23 07:04

/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
}