]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
lib:crypto: Add more GKDI functions
authorJo Sutton <josutton@catalyst.net.nz>
Tue, 13 Feb 2024 00:04:48 +0000 (13:04 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Fri, 16 Feb 2024 02:41:36 +0000 (02:41 +0000)
Signed-off-by: Jo Sutton <josutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
lib/crypto/gkdi.c
lib/crypto/gkdi.h

index a39a32add822f2d606c000f6abca58a3285a581a..92348f286ac303cdd9f5e095734cb9d1507d4f4c 100644 (file)
 
 #include "lib/util/bytearray.h"
 
+#include "librpc/ndr/libndr.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"
+#include "lib/util/data_blob.h"
 
 static const uint8_t kds_service[] = {
        /* “KDS service” as a NULL‐terminated UTF‐16LE string. */
@@ -38,6 +40,182 @@ static const uint8_t kds_service[] = {
        'r', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, 0,   0,
 };
 
+static struct Gkid gkid_from_u32_indices(const uint32_t l0_idx,
+                                        const uint32_t l1_idx,
+                                        const uint32_t l2_idx)
+{
+       /* Catch out‐of‐range indices. */
+       if (l0_idx > INT32_MAX || l1_idx > INT8_MAX || l2_idx > INT8_MAX) {
+               return invalid_gkid;
+       }
+
+       return Gkid(l0_idx, l1_idx, l2_idx);
+}
+
+NTSTATUS gkdi_pull_KeyEnvelope(TALLOC_CTX *mem_ctx,
+                              const DATA_BLOB *key_env_blob,
+                              struct KeyEnvelope *key_env_out)
+{
+       NTSTATUS status = NT_STATUS_OK;
+       enum ndr_err_code err;
+
+       if (key_env_blob == NULL) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (key_env_out == NULL) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       err = ndr_pull_struct_blob(key_env_blob,
+                                  mem_ctx,
+                                  key_env_out,
+                                  (ndr_pull_flags_fn_t)ndr_pull_KeyEnvelope);
+       status = ndr_map_error2ntstatus(err);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* If we felt so inclined, we could check the version field here. */
+
+       return status;
+}
+
+/*
+ * Retrieve the GKID and root key ID from a KeyEnvelope blob. The returned
+ * structure is guaranteed to have a valid GKID.
+ */
+const struct KeyEnvelopeId *gkdi_pull_KeyEnvelopeId(
+       const DATA_BLOB key_env_blob,
+       struct KeyEnvelopeId *key_env_out)
+{
+       TALLOC_CTX *tmp_ctx = NULL;
+       struct KeyEnvelope key_env;
+       const struct KeyEnvelopeId *key_env_ret = NULL;
+       NTSTATUS status;
+
+       if (key_env_out == NULL) {
+               goto out;
+       }
+
+       tmp_ctx = talloc_new(NULL);
+       if (tmp_ctx == NULL) {
+               goto out;
+       }
+
+       status = gkdi_pull_KeyEnvelope(tmp_ctx, &key_env_blob, &key_env);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto out;
+       }
+
+       {
+               const struct Gkid gkid = gkid_from_u32_indices(
+                       key_env.l0_index, key_env.l1_index, key_env.l2_index);
+               if (!gkid_is_valid(gkid)) {
+                       /* The KeyId is not valid: we can’t use it. */
+                       goto out;
+               }
+
+               *key_env_out = (struct KeyEnvelopeId){
+                       .root_key_id = key_env.root_key_id, .gkid = gkid};
+       }
+
+       /* Return a pointer to the buffer passed in by the caller. */
+       key_env_ret = key_env_out;
+
+out:
+       TALLOC_FREE(tmp_ctx);
+       return key_env_ret;
+}
+
+NTSTATUS ProvRootKey(TALLOC_CTX *mem_ctx,
+                    const struct GUID root_key_id,
+                    const int32_t version,
+                    const DATA_BLOB root_key_data,
+                    const NTTIME create_time,
+                    const NTTIME use_start_time,
+                    const char *const domain_id,
+                    const struct KdfAlgorithm kdf_algorithm,
+                    const struct ProvRootKey **const root_key_out)
+{
+       NTSTATUS status = NT_STATUS_OK;
+       struct ProvRootKey *root_key = NULL;
+
+       if (root_key_out == NULL) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       *root_key_out = NULL;
+
+       root_key = talloc(mem_ctx, struct ProvRootKey);
+       if (root_key == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       *root_key = (struct ProvRootKey){
+               .id = root_key_id,
+               .data = {.data = talloc_steal(root_key, root_key_data.data),
+                        .length = root_key_data.length},
+               .create_time = create_time,
+               .use_start_time = use_start_time,
+               .domain_id = talloc_steal(root_key, domain_id),
+               .kdf_algorithm = kdf_algorithm,
+               .version = version,
+       };
+
+       *root_key_out = root_key;
+       return status;
+}
+
+struct Gkid gkdi_get_interval_id(const NTTIME time)
+{
+       return Gkid(time / (gkdi_l1_key_iteration * gkdi_l2_key_iteration *
+                           gkdi_key_cycle_duration),
+                   time / (gkdi_l2_key_iteration * gkdi_key_cycle_duration) %
+                           gkdi_l1_key_iteration,
+                   time / gkdi_key_cycle_duration % gkdi_l2_key_iteration);
+}
+
+NTTIME gkdi_get_key_start_time(const struct Gkid gkid)
+{
+       return (gkid.l0_idx * gkdi_l1_key_iteration * gkdi_l2_key_iteration +
+               gkid.l1_idx * gkdi_l2_key_iteration + gkid.l2_idx) *
+              gkdi_key_cycle_duration;
+}
+
+/*
+ * This returns the equivalent of
+ * gkdi_get_key_start_time(gkdi_get_interval_id(time)).
+ */
+NTTIME gkdi_get_interval_start_time(const NTTIME time)
+{
+       return time % gkdi_key_cycle_duration;
+}
+
+bool gkid_less_than_or_equal_to(const struct Gkid g1, const struct Gkid g2)
+{
+       if (g1.l0_idx != g2.l0_idx) {
+               return g1.l0_idx < g2.l0_idx;
+       }
+
+       if (g1.l1_idx != g2.l1_idx) {
+               return g1.l1_idx < g2.l1_idx;
+       }
+
+       return g1.l2_idx <= g2.l2_idx;
+}
+
+bool gkdi_rollover_interval(const int64_t managed_password_interval,
+                           NTTIME *result)
+{
+       if (managed_password_interval < 0) {
+               return false;
+       }
+
+       *result = (uint64_t)managed_password_interval * 24 / 10 *
+                 gkdi_key_cycle_duration;
+       return true;
+}
+
 struct GkdiContextShort {
        uint8_t buf[sizeof((struct GUID_ndr_buf){}.buf) + sizeof(int32_t) +
                    sizeof(int32_t) + sizeof(int32_t)];
@@ -392,3 +570,81 @@ NTSTATUS compute_seed_key(TALLOC_CTX *mem_ctx,
 out:
        return status;
 }
+
+NTSTATUS kdf_sp_800_108_from_params(
+       const DATA_BLOB *const kdf_param,
+       struct KdfAlgorithm *const kdf_algorithm_out)
+{
+       TALLOC_CTX *tmp_ctx = NULL;
+       NTSTATUS status = NT_STATUS_OK;
+       enum ndr_err_code err;
+       enum KdfSp800_108Param sp800_108_param = KDF_PARAM_SHA256;
+       struct KdfParameters kdf_parameters;
+
+       if (kdf_param != NULL) {
+               tmp_ctx = talloc_new(NULL);
+               if (tmp_ctx == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto out;
+               }
+
+               err = ndr_pull_struct_blob(kdf_param,
+                                          tmp_ctx,
+                                          &kdf_parameters,
+                                          (ndr_pull_flags_fn_t)
+                                                  ndr_pull_KdfParameters);
+               if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+                       status = ndr_map_error2ntstatus(err);
+                       DBG_WARNING("KdfParameters pull failed: %s\n",
+                                   nt_errstr(status));
+                       goto out;
+               }
+
+               if (kdf_parameters.hash_algorithm == NULL) {
+                       status = NT_STATUS_NOT_SUPPORTED;
+                       goto out;
+               }
+
+               /* These string comparisons are case‐sensitive. */
+               if (strcmp(kdf_parameters.hash_algorithm, "SHA1") == 0) {
+                       sp800_108_param = KDF_PARAM_SHA1;
+               } else if (strcmp(kdf_parameters.hash_algorithm, "SHA256") == 0)
+               {
+                       sp800_108_param = KDF_PARAM_SHA256;
+               } else if (strcmp(kdf_parameters.hash_algorithm, "SHA384") == 0)
+               {
+                       sp800_108_param = KDF_PARAM_SHA384;
+               } else if (strcmp(kdf_parameters.hash_algorithm, "SHA512") == 0)
+               {
+                       sp800_108_param = KDF_PARAM_SHA512;
+               } else {
+                       status = NT_STATUS_NOT_SUPPORTED;
+                       goto out;
+               }
+       }
+
+       *kdf_algorithm_out = (struct KdfAlgorithm){
+               .id = KDF_ALGORITHM_SP800_108_CTR_HMAC,
+               .param.sp800_108 = sp800_108_param,
+       };
+out:
+       talloc_free(tmp_ctx);
+       return status;
+}
+
+NTSTATUS kdf_algorithm_from_params(const char *const kdf_algorithm_id,
+                                  const DATA_BLOB *const kdf_param,
+                                  struct KdfAlgorithm *const kdf_algorithm_out)
+{
+       if (kdf_algorithm_id == NULL) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* This string comparison is case‐sensitive. */
+       if (strcmp(kdf_algorithm_id, "SP800_108_CTR_HMAC") == 0) {
+               return kdf_sp_800_108_from_params(kdf_param, kdf_algorithm_out);
+       }
+
+       /* Unknown algorithm. */
+       return NT_STATUS_NOT_SUPPORTED;
+}
index 1c98216e147d372c6b3bc9d5a089a364474c755d..0786d228a193e66cc932ea08a51513ada2039745 100644 (file)
@@ -65,6 +65,16 @@ struct ProvRootKey {
        int32_t version;
 };
 
+NTSTATUS ProvRootKey(TALLOC_CTX *mem_ctx,
+                    const struct GUID root_key_id,
+                    const int32_t version,
+                    const DATA_BLOB root_key_data,
+                    const NTTIME create_time,
+                    const NTTIME use_start_time,
+                    const char *const domain_id,
+                    const struct KdfAlgorithm kdf_algorithm,
+                    const struct ProvRootKey **const root_key_out);
+
 struct Gkid {
        int32_t l0_idx;
        int8_t l1_idx; /* [range(0, 31)] */
@@ -78,6 +88,37 @@ enum GkidType {
        GKID_L2_SEED_KEY = 2,
 };
 
+/*
+ * Construct a GKID. The caller must check the returned GKID is valid before
+ * using it!
+ */
+static inline struct Gkid Gkid(int32_t l0_idx, int8_t l1_idx, int8_t l2_idx)
+{
+       return (struct Gkid){l0_idx, l1_idx, l2_idx};
+}
+
+static const struct Gkid invalid_gkid = {
+       INT32_MIN,
+       INT8_MIN,
+       INT8_MIN,
+};
+
+static const uint32_t key_envelope_magic = 0x4b53444b; /* ‘KDSK’ */
+
+struct KeyEnvelopeId {
+       struct GUID root_key_id;
+       struct Gkid gkid;
+};
+
+struct KeyEnvelope;
+NTSTATUS gkdi_pull_KeyEnvelope(TALLOC_CTX *mem_ctx,
+                              const DATA_BLOB *pwd_id_blob,
+                              struct KeyEnvelope *pwd_id_out);
+
+const struct KeyEnvelopeId *gkdi_pull_KeyEnvelopeId(
+       const DATA_BLOB key_env,
+       struct KeyEnvelopeId *key_env_out);
+
 enum GkidType gkid_key_type(const struct Gkid gkid);
 
 bool gkid_is_valid(const struct Gkid gkid);
@@ -90,6 +131,17 @@ static const int64_t gkdi_max_clock_skew = 3000000000;           /* five minutes */
 
 #define GKDI_KEY_LEN 64
 
+struct Gkid gkdi_get_interval_id(const NTTIME time);
+
+NTTIME gkdi_get_key_start_time(const struct Gkid gkid);
+
+NTTIME gkdi_get_interval_start_time(const NTTIME time);
+
+bool gkid_less_than_or_equal_to(const struct Gkid g1, const struct Gkid g2);
+
+bool gkdi_rollover_interval(const int64_t managed_password_interval,
+                           NTTIME *result);
+
 gnutls_mac_algorithm_t get_sp800_108_mac_algorithm(
        const struct KdfAlgorithm kdf_algorithm);
 
@@ -99,4 +151,13 @@ NTSTATUS compute_seed_key(TALLOC_CTX *mem_ctx,
                          const struct Gkid gkid,
                          uint8_t out[static const GKDI_KEY_LEN]);
 
+NTSTATUS kdf_sp_800_108_from_params(
+       const DATA_BLOB *const kdf_param,
+       struct KdfAlgorithm *const kdf_algorithm_out);
+
+NTSTATUS kdf_algorithm_from_params(
+       const char *const kdf_algorithm_id,
+       const DATA_BLOB *const kdf_param,
+       struct KdfAlgorithm *const kdf_algorithm_out);
+
 #endif /* LIB_CRYPTO_GKDI_H */