The branch, master has been updated via 5f5a49d78af lib:crypto: Add tests for GKDI key derivation via 9f16157f104 lib:crypto: Add implementation of GKDI key derivation via 6d280fac869 tests/krb5: Raise an error if root key data is the wrong length via 4946ab4c17f tests/krb5: Test that root key data is the correct length in bytes via 8277d7accf7 tests/krb5: Create root key just for implicit root key tests via bd3091dfc54 tests/krb5: Check properties of current GKDI key from b4563a24904 fuzz: allow max size conditional ACE round-trip failure
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 5f5a49d78af938304586bb4ee45aabc5f28f54c3 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Fri Dec 22 11:04:51 2023 +1300 lib:crypto: Add tests for GKDI key derivation Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Autobuild-User(master): Andrew Bartlett <abart...@samba.org> Autobuild-Date(master): Fri Dec 22 06:31:29 UTC 2023 on atb-devel-224 commit 9f16157f1049677434aadd25b47e338a66099e86 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Mon Nov 13 17:08:58 2023 +1300 lib:crypto: Add implementation of GKDI key derivation Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 6d280fac8690eb6985ce477f3f9bb6e5faf3256d Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Wed Dec 20 16:39:14 2023 +1300 tests/krb5: Raise an error if root key data is the wrong length Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 4946ab4c17f1d4615a98e4c8d1f5e82456aa5cf7 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Wed Dec 20 16:38:33 2023 +1300 tests/krb5: Test that root key data is the correct length in bytes Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 8277d7accf71cad3b33051d8a4d45d74968f35c3 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Dec 19 09:38:27 2023 +1300 tests/krb5: Create root key just for implicit root key tests Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit bd3091dfc545cb50734f7c9108be41f5cd4059b6 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Dec 19 09:37:40 2023 +1300 tests/krb5: Check properties of current GKDI key Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> ----------------------------------------------------------------------- Summary of changes: lib/crypto/gkdi.c | 375 ++++++++++++++++++++++++++ lib/crypto/gkdi.h | 67 +++++ lib/crypto/test_gkdi_key_derivation.c | 492 ++++++++++++++++++++++++++++++++++ lib/crypto/wscript | 12 +- librpc/idl/gkdi.idl | 8 + python/samba/tests/gkdi.py | 13 +- python/samba/tests/krb5/gkdi_tests.py | 75 ++++-- selftest/knownfail.d/gkdi | 1 + selftest/tests.py | 2 + 9 files changed, 1015 insertions(+), 30 deletions(-) create mode 100644 lib/crypto/test_gkdi_key_derivation.c Changeset truncated at 500 lines: diff --git a/lib/crypto/gkdi.c b/lib/crypto/gkdi.c index e049cf96bac..6799dcfd70e 100644 --- a/lib/crypto/gkdi.c +++ b/lib/crypto/gkdi.c @@ -18,4 +18,379 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ +#include "includes.h" +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> + +#include "lib/crypto/gnutls_helpers.h" + +#include "lib/util/bytearray.h" + +#include "librpc/gen_ndr/ndr_security.h" +#include "librpc/gen_ndr/gkdi.h" +#include "librpc/gen_ndr/ndr_gkdi.h" + #include "lib/crypto/gkdi.h" + +static const uint8_t kds_service[] = { + /* “KDS service” as a NULL‐terminated UTF‐16LE string. */ + 'K', 0, 'D', 0, 'S', 0, ' ', 0, 's', 0, 'e', 0, + 'r', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, 0, 0, +}; + +struct GkdiContextShort { + uint8_t buf[sizeof((struct GUID_ndr_buf){}.buf) + sizeof(int32_t) + + sizeof(int32_t) + sizeof(int32_t)]; +}; + +static NTSTATUS make_gkdi_context(const struct GkdiDerivationCtx *ctx, + struct GkdiContextShort *out_ctx) +{ + enum ndr_err_code ndr_err; + DATA_BLOB b = {.data = out_ctx->buf, .length = sizeof out_ctx->buf}; + + if (ctx->target_security_descriptor.length) { + return NT_STATUS_INVALID_PARAMETER; + } + + ndr_err = ndr_push_struct_into_fixed_blob( + &b, ctx, (ndr_push_flags_fn_t)ndr_push_GkdiDerivationCtx); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return ndr_map_error2ntstatus(ndr_err); + } + + return NT_STATUS_OK; +} + +static NTSTATUS make_gkdi_context_security_descriptor( + TALLOC_CTX *mem_ctx, + const struct GkdiDerivationCtx *ctx, + const DATA_BLOB security_descriptor, + DATA_BLOB *out_ctx) +{ + enum ndr_err_code ndr_err; + struct GkdiDerivationCtx ctx_with_sd = *ctx; + + if (ctx_with_sd.target_security_descriptor.length) { + return NT_STATUS_INVALID_PARAMETER; + } + + ctx_with_sd.target_security_descriptor = security_descriptor; + + ndr_err = ndr_push_struct_blob(out_ctx, + mem_ctx, + &ctx_with_sd, + (ndr_push_flags_fn_t) + ndr_push_GkdiDerivationCtx); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return ndr_map_error2ntstatus(ndr_err); + } + + return NT_STATUS_OK; +} + +struct GkdiContext { + struct GkdiDerivationCtx ctx; + gnutls_mac_algorithm_t algorithm; +}; + +gnutls_mac_algorithm_t get_sp800_108_mac_algorithm( + const struct KdfAlgorithm kdf_algorithm) +{ + switch (kdf_algorithm.id) { + case KDF_ALGORITHM_SP800_108_CTR_HMAC: + switch (kdf_algorithm.param.sp800_108) { + case KDF_PARAM_SHA1: + return GNUTLS_MAC_SHA1; + case KDF_PARAM_SHA256: + return GNUTLS_MAC_SHA256; + case KDF_PARAM_SHA384: + return GNUTLS_MAC_SHA384; + case KDF_PARAM_SHA512: + return GNUTLS_MAC_SHA512; + } + break; + } + + return GNUTLS_MAC_UNKNOWN; +} + +static NTSTATUS GkdiContext(const struct ProvRootKey *const root_key, + struct GkdiContext *const ctx) +{ + NTSTATUS status = NT_STATUS_OK; + gnutls_mac_algorithm_t algorithm = GNUTLS_MAC_UNKNOWN; + + if (ctx == NULL) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (root_key == NULL) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (root_key->version != root_key_version_1) { + status = NT_STATUS_NOT_SUPPORTED; + goto out; + } + + if (root_key->data.length != GKDI_KEY_LEN) { + status = NT_STATUS_NOT_SUPPORTED; + goto out; + } + + algorithm = get_sp800_108_mac_algorithm(root_key->kdf_algorithm); + if (algorithm == GNUTLS_MAC_UNKNOWN) { + status = NT_STATUS_NOT_SUPPORTED; + goto out; + } + + /* + * The context comprises the GUID corresponding to the root key, the + * GKID (which we shall initialize to zero), and the encoded target + * security descriptor (which will initially be empty). + */ + *ctx = (struct GkdiContext){ + .ctx = {.guid = root_key->id, + .l0_idx = 0, + .l1_idx = 0, + .l2_idx = 0, + .target_security_descriptor = {}}, + .algorithm = algorithm, + }; +out: + return status; +} + +static NTSTATUS compute_l1_seed_key( + TALLOC_CTX *mem_ctx, + struct GkdiContext *ctx, + const DATA_BLOB security_descriptor, + const struct ProvRootKey *const root_key, + const struct Gkid gkid, + uint8_t key[static const GKDI_KEY_LEN]) +{ + NTSTATUS status = NT_STATUS_OK; + struct GkdiContextShort short_ctx; + int8_t n; + + ctx->ctx.l0_idx = gkid.l0_idx; + ctx->ctx.l1_idx = -1; + ctx->ctx.l2_idx = -1; + + status = make_gkdi_context(&ctx->ctx, &short_ctx); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + /* Derive an L0 seed key with GKID = (L0, −1, −1). */ + + status = samba_gnutls_sp800_108_derive_key(root_key->data.data, + root_key->data.length, + NULL, + 0, + kds_service, + sizeof kds_service, + short_ctx.buf, + sizeof short_ctx.buf, + ctx->algorithm, + key, + GKDI_KEY_LEN); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + /* Derive an L1 seed key with GKID = (L0, 31, −1). */ + + ctx->ctx.l1_idx = 31; + + { + DATA_BLOB security_descriptor_ctx; + + status = make_gkdi_context_security_descriptor( + mem_ctx, + &ctx->ctx, + security_descriptor, + &security_descriptor_ctx); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + status = samba_gnutls_sp800_108_derive_key( + key, + GKDI_KEY_LEN, + NULL, + 0, + kds_service, + sizeof kds_service, + security_descriptor_ctx.data, + security_descriptor_ctx.length, + ctx->algorithm, + key, + GKDI_KEY_LEN); + data_blob_free(&security_descriptor_ctx); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + } + + for (n = 30; n >= gkid.l1_idx; --n) { + /* Derive an L1 seed key with GKID = (L0, n, −1). */ + + ctx->ctx.l1_idx = n; + + status = make_gkdi_context(&ctx->ctx, &short_ctx); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + status = samba_gnutls_sp800_108_derive_key(key, + GKDI_KEY_LEN, + NULL, + 0, + kds_service, + sizeof kds_service, + short_ctx.buf, + sizeof short_ctx.buf, + ctx->algorithm, + key, + GKDI_KEY_LEN); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + } + +out: + return status; +} + +static NTSTATUS derive_l2_seed_key(struct GkdiContext *ctx, + const struct Gkid gkid, + uint8_t key[static const GKDI_KEY_LEN]) +{ + NTSTATUS status = NT_STATUS_OK; + int8_t n; + + ctx->ctx.l0_idx = gkid.l0_idx; + ctx->ctx.l1_idx = gkid.l1_idx; + + for (n = 31; n >= gkid.l2_idx; --n) { + struct GkdiContextShort short_ctx; + + /* Derive an L2 seed key with GKID = (L0, L1, n). */ + + ctx->ctx.l2_idx = n; + + status = make_gkdi_context(&ctx->ctx, &short_ctx); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + status = samba_gnutls_sp800_108_derive_key(key, + GKDI_KEY_LEN, + NULL, + 0, + kds_service, + sizeof kds_service, + short_ctx.buf, + sizeof short_ctx.buf, + ctx->algorithm, + key, + GKDI_KEY_LEN); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + } + +out: + return status; +} + +static enum GkidType gkid_key_type(const struct Gkid gkid) +{ + if (gkid.l0_idx == -1) { + return GKID_DEFAULT; + } + + if (gkid.l1_idx == -1) { + return GKID_L0_SEED_KEY; + } + + if (gkid.l2_idx == -1) { + return GKID_L1_SEED_KEY; + } + + return GKID_L2_SEED_KEY; +} + +static bool gkid_is_valid(const struct Gkid gkid) +{ + if (gkid.l0_idx < -1) { + return false; + } + + if (gkid.l1_idx < -1 || gkid.l1_idx >= gkdi_l1_key_iteration) { + return false; + } + + if (gkid.l2_idx < -1 || gkid.l2_idx >= gkdi_l2_key_iteration) { + return false; + } + + if (gkid.l0_idx == -1 && gkid.l1_idx != -1) { + return false; + } + + if (gkid.l1_idx == -1 && gkid.l2_idx != -1) { + return false; + } + + return true; +} + +NTSTATUS compute_seed_key( + TALLOC_CTX *mem_ctx, + const DATA_BLOB target_security_descriptor, + const struct ProvRootKey *const root_key, + const struct Gkid gkid, + uint8_t key[static const GKDI_KEY_LEN]) +{ + NTSTATUS status = NT_STATUS_OK; + enum GkidType gkid_type; + struct GkdiContext ctx; + + if (!gkid_is_valid(gkid)) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + gkid_type = gkid_key_type(gkid); + if (gkid_type < GKID_L1_SEED_KEY) { + /* Don’t allow derivation of L0 seed keys. */ + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + status = GkdiContext(root_key, &ctx); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + status = compute_l1_seed_key( + mem_ctx, &ctx, target_security_descriptor, root_key, gkid, key); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + if (gkid_type == GKID_L2_SEED_KEY) { + status = derive_l2_seed_key(&ctx, gkid, key); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + } + +out: + return status; +} diff --git a/lib/crypto/gkdi.h b/lib/crypto/gkdi.h index b6c18a8f6ae..892bcc4b380 100644 --- a/lib/crypto/gkdi.h +++ b/lib/crypto/gkdi.h @@ -23,10 +23,77 @@ #include <stdint.h> +#include <gnutls/gnutls.h> + +#include "lib/util/data_blob.h" + +#include "libcli/util/ntstatus.h" + +#include "librpc/gen_ndr/misc.h" +#include "lib/util/time.h" +#include "talloc.h" + +enum KdfAlgorithmId { + KDF_ALGORITHM_SP800_108_CTR_HMAC, +}; + +enum KdfSp800_108Param { + KDF_PARAM_SHA1, + KDF_PARAM_SHA256, + KDF_PARAM_SHA384, + KDF_PARAM_SHA512, +}; + +struct KdfAlgorithm { + union { + enum KdfSp800_108Param sp800_108; + } param; + enum KdfAlgorithmId id; +}; + +enum { + root_key_version_1 = 1, +}; + +struct ProvRootKey { + struct GUID id; + DATA_BLOB data; + NTTIME create_time; + NTTIME use_start_time; + const char *domain_id; + struct KdfAlgorithm kdf_algorithm; + int32_t version; +}; + +struct Gkid { + int32_t l0_idx; + int8_t l1_idx; /* [range(0, 31)] */ + int8_t l2_idx; /* [range(0, 31)] */ +}; + +enum GkidType { + GKID_DEFAULT = -1, + GKID_L0_SEED_KEY = 0, + GKID_L1_SEED_KEY = 1, + GKID_L2_SEED_KEY = 2, +}; + static const int gkdi_l1_key_iteration = 32; static const int gkdi_l2_key_iteration = 32; static const int64_t gkdi_key_cycle_duration = 360000000000; static const int64_t gkdi_max_clock_skew = 3000000000; +#define GKDI_KEY_LEN 64 + +gnutls_mac_algorithm_t get_sp800_108_mac_algorithm( + const struct KdfAlgorithm kdf_algorithm); + +NTSTATUS compute_seed_key( + TALLOC_CTX *mem_ctx, + const DATA_BLOB target_security_descriptor, + const struct ProvRootKey *const root_key, + const struct Gkid gkid, + uint8_t out[static const GKDI_KEY_LEN]); + #endif /* LIB_CRYPTO_GKDI_H */ diff --git a/lib/crypto/test_gkdi_key_derivation.c b/lib/crypto/test_gkdi_key_derivation.c new file mode 100644 index 00000000000..22f33561a57 --- /dev/null +++ b/lib/crypto/test_gkdi_key_derivation.c @@ -0,0 +1,492 @@ +/* + * Unix SMB/CIFS implementation. + * + * Copyright (C) Catalyst.Net Ltd 2023 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> +#include <cmocka.h> + +#include "replace.h" +#include <talloc.h> +#include "libcli/util/ntstatus.h" -- Samba Shared Repository