mirror of
https://github.com/samba-team/samba.git
synced 2025-01-11 05:18:09 +03:00
1381581334
Signed-off-by: Jo Sutton <josutton@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
747 lines
26 KiB
Python
Executable File
747 lines
26 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
# 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/>.
|
||
#
|
||
|
||
import sys
|
||
import os
|
||
|
||
sys.path.insert(0, "bin/python")
|
||
os.environ["PYTHONUNBUFFERED"] = "1"
|
||
|
||
import secrets
|
||
|
||
from typing import ClassVar, Optional
|
||
|
||
from samba.dcerpc import gkdi, misc
|
||
from samba.gkdi import (
|
||
Algorithm,
|
||
Gkid,
|
||
KEY_CYCLE_DURATION,
|
||
KEY_LEN_BYTES,
|
||
MAX_CLOCK_SKEW,
|
||
NtTime,
|
||
NtTimeDelta,
|
||
SeedKeyPair,
|
||
)
|
||
from samba.hresult import HRES_E_INVALIDARG, HRES_NTE_BAD_KEY, HRES_NTE_NO_KEY
|
||
from samba.nt_time import timedelta_from_nt_time_delta
|
||
|
||
from samba.tests.gkdi import GetKeyError, GkdiBaseTest, ROOT_KEY_START_TIME
|
||
from samba.tests.krb5.kdc_base_test import KDCBaseTest
|
||
|
||
|
||
class GkdiKdcBaseTest(GkdiBaseTest, KDCBaseTest):
|
||
def new_root_key(self, *args, **kwargs) -> misc.GUID:
|
||
samdb = self.get_samdb()
|
||
domain_dn = self.get_server_dn(samdb)
|
||
return self.create_root_key(samdb, domain_dn, *args, **kwargs)
|
||
|
||
def gkdi_conn(self) -> gkdi.gkdi:
|
||
# The seed keys used by Group Managed Service Accounts correspond to the
|
||
# Enterprise DCs SID (S-1-5-9); as such they can be retrieved only by
|
||
# server accounts.
|
||
return self.gkdi_connect(
|
||
self.dc_host,
|
||
self.get_lp(),
|
||
self.get_cached_creds(account_type=self.AccountType.SERVER),
|
||
)
|
||
|
||
def check_rpc_get_key(
|
||
self, root_key_id: Optional[misc.GUID], gkid: Gkid
|
||
) -> SeedKeyPair:
|
||
got_key_pair = self.rpc_get_key(
|
||
self.gkdi_conn(), self.gmsa_sd, root_key_id, gkid
|
||
)
|
||
expected_key_pair = self.get_key(
|
||
self.get_samdb(),
|
||
self.gmsa_sd,
|
||
root_key_id,
|
||
gkid,
|
||
root_key_id_hint=got_key_pair.root_key_id if root_key_id is None else None,
|
||
)
|
||
self.assertEqual(
|
||
got_key_pair.root_key_id,
|
||
expected_key_pair.root_key_id,
|
||
"root key IDs must match",
|
||
)
|
||
self.assertEqual(got_key_pair, expected_key_pair, "key pairs must match")
|
||
|
||
return got_key_pair
|
||
|
||
|
||
class GkdiExplicitRootKeyTests(GkdiKdcBaseTest):
|
||
def test_current_l0_idx(self):
|
||
"""Request a key with the current L0 index. This index is regularly
|
||
incremented every 427 days or so."""
|
||
root_key_id = self.new_root_key()
|
||
|
||
# It actually doesn’t matter what we specify for the L1 and L2 indices.
|
||
# We’ll get the same result regardless — they just cannot specify a key
|
||
# from the future.
|
||
current_gkid = self.current_gkid(self.get_samdb())
|
||
key = self.check_rpc_get_key(root_key_id, current_gkid)
|
||
|
||
self.assertEqual(current_gkid, key.gkid)
|
||
self.assertEqual(root_key_id, key.root_key_id)
|
||
|
||
def test_previous_l0_idx(self):
|
||
"""Request a key with a previous L0 index."""
|
||
root_key_id = self.new_root_key(use_start_time=ROOT_KEY_START_TIME)
|
||
|
||
# It actually doesn’t matter what we specify for the L1 and L2 indices.
|
||
# We’ll get the same result regardless.
|
||
previous_l0_idx = self.current_gkid(self.get_samdb()).l0_idx - 1
|
||
key = self.check_rpc_get_key(root_key_id, Gkid(previous_l0_idx, 0, 0))
|
||
|
||
# Expect to get an L1 seed key.
|
||
self.assertIsNotNone(key.l1_key)
|
||
self.assertIsNone(key.l2_key)
|
||
self.assertEqual(Gkid(previous_l0_idx, 31, 31), key.gkid)
|
||
self.assertEqual(root_key_id, key.root_key_id)
|
||
|
||
def test_algorithm_sha1(self):
|
||
"""Test with the SHA1 algorithm."""
|
||
key = self.check_rpc_get_key(
|
||
self.new_root_key(hash_algorithm=Algorithm.SHA1),
|
||
self.current_gkid(self.get_samdb()),
|
||
)
|
||
self.assertIs(Algorithm.SHA1, key.hash_algorithm)
|
||
|
||
def test_algorithm_sha256(self):
|
||
"""Test with the SHA256 algorithm."""
|
||
key = self.check_rpc_get_key(
|
||
self.new_root_key(hash_algorithm=Algorithm.SHA256),
|
||
self.current_gkid(self.get_samdb()),
|
||
)
|
||
self.assertIs(Algorithm.SHA256, key.hash_algorithm)
|
||
|
||
def test_algorithm_sha384(self):
|
||
"""Test with the SHA384 algorithm."""
|
||
key = self.check_rpc_get_key(
|
||
self.new_root_key(hash_algorithm=Algorithm.SHA384),
|
||
self.current_gkid(self.get_samdb()),
|
||
)
|
||
self.assertIs(Algorithm.SHA384, key.hash_algorithm)
|
||
|
||
def test_algorithm_sha512(self):
|
||
"""Test with the SHA512 algorithm."""
|
||
key = self.check_rpc_get_key(
|
||
self.new_root_key(hash_algorithm=Algorithm.SHA512),
|
||
self.current_gkid(self.get_samdb()),
|
||
)
|
||
self.assertIs(Algorithm.SHA512, key.hash_algorithm)
|
||
|
||
def test_algorithm_none(self):
|
||
"""Test without a specified algorithm."""
|
||
key = self.check_rpc_get_key(
|
||
self.new_root_key(hash_algorithm=None),
|
||
self.current_gkid(self.get_samdb()),
|
||
)
|
||
self.assertIs(Algorithm.SHA256, key.hash_algorithm)
|
||
|
||
def test_future_key(self):
|
||
"""Try to request a key from the future."""
|
||
root_key_id = self.new_root_key(use_start_time=ROOT_KEY_START_TIME)
|
||
|
||
future_gkid = self.current_gkid(
|
||
self.get_samdb(),
|
||
offset=timedelta_from_nt_time_delta(
|
||
NtTimeDelta(KEY_CYCLE_DURATION + MAX_CLOCK_SKEW)
|
||
)
|
||
)
|
||
|
||
with self.assertRaises(GetKeyError) as err:
|
||
self.get_key(self.get_samdb(), self.gmsa_sd, root_key_id, future_gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_E_INVALIDARG,
|
||
err.exception.args[0],
|
||
"requesting a key from the future should fail with INVALID_PARAMETER",
|
||
)
|
||
|
||
with self.assertRaises(GetKeyError) as rpc_err:
|
||
self.rpc_get_key(self.gkdi_conn(), self.gmsa_sd, root_key_id, future_gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_E_INVALIDARG,
|
||
rpc_err.exception.args[0],
|
||
"requesting a key from the future should fail with INVALID_PARAMETER",
|
||
)
|
||
|
||
def test_root_key_use_start_time_zero(self):
|
||
"""Attempt to use a root key with an effective time of zero."""
|
||
root_key_id = self.new_root_key(use_start_time=NtTime(0))
|
||
|
||
gkid = self.current_gkid(self.get_samdb())
|
||
|
||
with self.assertRaises(GetKeyError) as err:
|
||
self.get_key(self.get_samdb(), self.gmsa_sd, root_key_id, gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_NTE_BAD_KEY,
|
||
err.exception.args[0],
|
||
"using a root key with an effective time of zero should fail with BAD_KEY",
|
||
)
|
||
|
||
with self.assertRaises(GetKeyError) as rpc_err:
|
||
self.rpc_get_key(self.gkdi_conn(), self.gmsa_sd, root_key_id, gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_NTE_BAD_KEY,
|
||
rpc_err.exception.args[0],
|
||
"using a root key with an effective time of zero should fail with BAD_KEY",
|
||
)
|
||
|
||
def test_root_key_use_start_time_too_low(self):
|
||
"""Attempt to use a root key with an effective time set too low."""
|
||
root_key_id = self.new_root_key(use_start_time=NtTime(ROOT_KEY_START_TIME - 1))
|
||
|
||
gkid = self.current_gkid(self.get_samdb())
|
||
|
||
with self.assertRaises(GetKeyError) as err:
|
||
self.get_key(self.get_samdb(), self.gmsa_sd, root_key_id, gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_E_INVALIDARG,
|
||
err.exception.args[0],
|
||
"using a root key with too low effective time should fail with"
|
||
" INVALID_PARAMETER",
|
||
)
|
||
|
||
with self.assertRaises(GetKeyError) as rpc_err:
|
||
self.rpc_get_key(self.gkdi_conn(), self.gmsa_sd, root_key_id, gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_E_INVALIDARG,
|
||
rpc_err.exception.args[0],
|
||
"using a root key with too low effective time should fail with"
|
||
" INVALID_PARAMETER",
|
||
)
|
||
|
||
def test_before_valid(self):
|
||
"""Attempt to use a key before it is valid."""
|
||
gkid = self.current_gkid(self.get_samdb())
|
||
valid_start_time = NtTime(
|
||
gkid.start_nt_time() + KEY_CYCLE_DURATION + MAX_CLOCK_SKEW
|
||
)
|
||
|
||
# Using a valid root key is allowed.
|
||
valid_root_key_id = self.new_root_key(use_start_time=valid_start_time)
|
||
self.check_rpc_get_key(valid_root_key_id, gkid)
|
||
|
||
# But attempting to use a root key that is not yet valid will result in
|
||
# an INVALID_PARAMETER error.
|
||
invalid_root_key_id = self.new_root_key(use_start_time=valid_start_time + 1)
|
||
|
||
with self.assertRaises(GetKeyError) as err:
|
||
self.get_key(self.get_samdb(), self.gmsa_sd, invalid_root_key_id, gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_E_INVALIDARG,
|
||
err.exception.args[0],
|
||
"using a key before it is valid should fail with INVALID_PARAMETER",
|
||
)
|
||
|
||
with self.assertRaises(GetKeyError) as rpc_err:
|
||
self.rpc_get_key(self.gkdi_conn(), self.gmsa_sd, invalid_root_key_id, gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_E_INVALIDARG,
|
||
rpc_err.exception.args[0],
|
||
"using a key before it is valid should fail with INVALID_PARAMETER",
|
||
)
|
||
|
||
def test_non_existent_root_key(self):
|
||
"""Attempt to use a non‐existent root key."""
|
||
root_key_id = misc.GUID(secrets.token_bytes(16))
|
||
|
||
gkid = self.current_gkid(self.get_samdb())
|
||
|
||
with self.assertRaises(GetKeyError) as err:
|
||
self.get_key(self.get_samdb(), self.gmsa_sd, root_key_id, gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_NTE_NO_KEY,
|
||
err.exception.args[0],
|
||
"using a non‐existent root key should fail with NO_KEY",
|
||
)
|
||
|
||
with self.assertRaises(GetKeyError) as rpc_err:
|
||
self.rpc_get_key(self.gkdi_conn(), self.gmsa_sd, root_key_id, gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_NTE_NO_KEY,
|
||
rpc_err.exception.args[0],
|
||
"using a non‐existent root key should fail with NO_KEY",
|
||
)
|
||
|
||
def test_root_key_wrong_length(self):
|
||
"""Attempt to use a root key that is the wrong length."""
|
||
root_key_id = self.new_root_key(data=bytes(KEY_LEN_BYTES // 2))
|
||
|
||
gkid = self.current_gkid(self.get_samdb())
|
||
|
||
with self.assertRaises(GetKeyError) as err:
|
||
self.get_key(self.get_samdb(), self.gmsa_sd, root_key_id, gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_NTE_BAD_KEY,
|
||
err.exception.args[0],
|
||
"using a root key that is the wrong length should fail with BAD_KEY",
|
||
)
|
||
|
||
with self.assertRaises(GetKeyError) as rpc_err:
|
||
self.rpc_get_key(self.gkdi_conn(), self.gmsa_sd, root_key_id, gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_NTE_BAD_KEY,
|
||
rpc_err.exception.args[0],
|
||
"using a root key that is the wrong length should fail with BAD_KEY",
|
||
)
|
||
|
||
|
||
class GkdiImplicitRootKeyTests(GkdiKdcBaseTest):
|
||
_root_key: ClassVar[misc.GUID]
|
||
|
||
@classmethod
|
||
def setUpClass(cls) -> None:
|
||
super().setUpClass()
|
||
|
||
cls._root_key = None
|
||
|
||
def setUp(self) -> None:
|
||
super().setUp()
|
||
|
||
if self._root_key is None:
|
||
# GKDI requires a root key to operate. Creating a root key here
|
||
# saves creating one before every test.
|
||
#
|
||
# We cannot delete this key after the tests have run, as Windows
|
||
# might have decided to cache it to be used in subsequent runs. It
|
||
# will keep a root key cached even if the corresponding AD object
|
||
# has been deleted, leading to various problems later.
|
||
cls = type(self)
|
||
cls._root_key = self.new_root_key(use_start_time=ROOT_KEY_START_TIME)
|
||
|
||
def test_l1_seed_key(self):
|
||
"""Request a key and expect to receive an L1 seed key."""
|
||
gkid = Gkid(300, 0, 31)
|
||
key = self.check_rpc_get_key(None, gkid)
|
||
|
||
# Expect to get an L1 seed key.
|
||
self.assertIsNotNone(key.l1_key)
|
||
self.assertIsNone(key.l2_key)
|
||
self.assertEqual(gkid, key.gkid)
|
||
|
||
def test_l2_seed_key(self):
|
||
"""Request a key and expect to receive an L2 seed key."""
|
||
gkid = Gkid(300, 0, 0)
|
||
key = self.check_rpc_get_key(None, gkid)
|
||
|
||
# Expect to get an L2 seed key.
|
||
self.assertIsNone(key.l1_key)
|
||
self.assertIsNotNone(key.l2_key)
|
||
self.assertEqual(gkid, key.gkid)
|
||
|
||
def test_both_seed_keys(self):
|
||
"""Request a key and expect to receive L1 and L2 seed keys."""
|
||
gkid = Gkid(300, 1, 0)
|
||
key = self.check_rpc_get_key(None, gkid)
|
||
|
||
# Expect to get both L1 and L2 seed keys.
|
||
self.assertIsNotNone(key.l1_key)
|
||
self.assertIsNotNone(key.l2_key)
|
||
self.assertEqual(gkid, key.gkid)
|
||
|
||
def test_both_seed_keys_no_hint(self):
|
||
"""Request a key, but don’t specify ‘root_key_id_hint’."""
|
||
gkid = Gkid(300, 1, 0)
|
||
key = self.get_key(
|
||
self.get_samdb(),
|
||
self.gmsa_sd,
|
||
None,
|
||
gkid,
|
||
)
|
||
|
||
# Expect to get both L1 and L2 seed keys.
|
||
self.assertIsNotNone(key.l1_key)
|
||
self.assertIsNotNone(key.l2_key)
|
||
self.assertEqual(gkid, key.gkid)
|
||
|
||
def test_request_l0_seed_key(self):
|
||
"""Attempt to request an L0 seed key."""
|
||
gkid = Gkid.l0_seed_key(300)
|
||
|
||
with self.assertRaises(GetKeyError) as err:
|
||
self.get_key(self.get_samdb(), self.gmsa_sd, None, gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_E_INVALIDARG,
|
||
err.exception.args[0],
|
||
"requesting an L0 seed key should fail with INVALID_PARAMETER",
|
||
)
|
||
|
||
with self.assertRaises(GetKeyError) as rpc_err:
|
||
self.rpc_get_key(self.gkdi_conn(), self.gmsa_sd, None, gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_E_INVALIDARG,
|
||
rpc_err.exception.args[0],
|
||
"requesting an L0 seed key should fail with INVALID_PARAMETER",
|
||
)
|
||
|
||
def test_request_l1_seed_key(self):
|
||
"""Attempt to request an L1 seed key."""
|
||
gkid = Gkid.l1_seed_key(300, 0)
|
||
|
||
with self.assertRaises(GetKeyError) as err:
|
||
self.get_key(self.get_samdb(), self.gmsa_sd, None, gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_E_INVALIDARG,
|
||
err.exception.args[0],
|
||
"requesting an L1 seed key should fail with INVALID_PARAMETER",
|
||
)
|
||
|
||
with self.assertRaises(GetKeyError) as rpc_err:
|
||
self.rpc_get_key(self.gkdi_conn(), self.gmsa_sd, None, gkid)
|
||
|
||
self.assertEqual(
|
||
HRES_E_INVALIDARG,
|
||
rpc_err.exception.args[0],
|
||
"requesting an L1 seed key should fail with INVALID_PARAMETER",
|
||
)
|
||
|
||
def test_request_default_seed_key(self):
|
||
"""Try to make a request with the default GKID."""
|
||
gkid = Gkid.default()
|
||
|
||
self.assertRaises(
|
||
NotImplementedError,
|
||
self.get_key,
|
||
self.get_samdb(),
|
||
self.gmsa_sd,
|
||
None,
|
||
gkid,
|
||
)
|
||
|
||
self.rpc_get_key(self.gkdi_conn(), self.gmsa_sd, None, gkid)
|
||
|
||
|
||
class GkdiSelfTests(GkdiKdcBaseTest):
|
||
def test_current_l0_idx_l1_seed_key(self):
|
||
"""Request a key with the current L0 index, expecting to receive an L1
|
||
seed key."""
|
||
root_key_id = self.new_root_key(
|
||
use_start_time=ROOT_KEY_START_TIME,
|
||
hash_algorithm=Algorithm.SHA512,
|
||
guid=misc.GUID("89f70521-9d66-441f-c314-1b462f9b1052"),
|
||
data=bytes.fromhex(
|
||
"a6ef87dbbbf86b6bbe55750b941f13ca99efe5185e2e2bded5b838d8a0e77647"
|
||
"0537e68cae45a7a0f4b1d6c9bf5494c3f879e172e326557cdbb6a56e8799a722"
|
||
),
|
||
)
|
||
|
||
current_gkid = Gkid(255, 24, 31)
|
||
key = self.get_key(
|
||
self.get_samdb(),
|
||
self.gmsa_sd,
|
||
root_key_id,
|
||
Gkid(255, 2, 5),
|
||
current_time=current_gkid.start_nt_time(),
|
||
)
|
||
|
||
# Expect to get an L1 seed key.
|
||
self.assertEqual(current_gkid, key.gkid)
|
||
self.assertEqual(root_key_id, key.root_key_id)
|
||
self.assertEqual(Algorithm.SHA512, key.hash_algorithm)
|
||
self.assertEqual(
|
||
bytes.fromhex(
|
||
"bd538a073490f3cf9451c933025de9b22c97eaddaffa94b379e2b919a4bed147"
|
||
"5bc67f6a9175b139c69204c57d4300a0141ffe34d12ced84614593b1aa13af1c"
|
||
),
|
||
key.l1_key,
|
||
)
|
||
self.assertIsNone(key.l2_key)
|
||
|
||
def test_current_l0_idx_l2_seed_key(self):
|
||
"""Request a key with the current L0 index, expecting to receive an L2
|
||
seed key."""
|
||
root_key_id = self.new_root_key(
|
||
use_start_time=ROOT_KEY_START_TIME,
|
||
hash_algorithm=Algorithm.SHA512,
|
||
guid=misc.GUID("1a3d6c30-aa81-cb7f-d3fe-80775d135dfe"),
|
||
data=bytes.fromhex(
|
||
"dfd95be3153a0805c65694e7d284aace5ab0aa493350025eb8dbc6df0b4e9256"
|
||
"fb4cbfbe6237ce3732694e2608760076b67082d39abd3c0fedba1b8873645064"
|
||
),
|
||
)
|
||
|
||
current_gkid = Gkid(321, 0, 12)
|
||
key = self.get_key(
|
||
self.get_samdb(),
|
||
self.gmsa_sd,
|
||
root_key_id,
|
||
Gkid(321, 0, 1),
|
||
current_time=current_gkid.start_nt_time(),
|
||
)
|
||
|
||
# Expect to get an L2 seed key.
|
||
self.assertEqual(current_gkid, key.gkid)
|
||
self.assertEqual(root_key_id, key.root_key_id)
|
||
self.assertEqual(Algorithm.SHA512, key.hash_algorithm)
|
||
self.assertIsNone(key.l1_key)
|
||
self.assertEqual(
|
||
bytes.fromhex(
|
||
"bbbd9376cd16c247ed40f5912d1908218c08f0915bae02fe02cbfb3753bde406"
|
||
"f9c553acd95143cf63906a0440e3cf237d2335ae4e4b9cd2d946a71351ebcb7b"
|
||
),
|
||
key.l2_key,
|
||
)
|
||
|
||
def test_current_l0_idx_both_seed_keys(self):
|
||
"""Request a key with the current L0 index, expecting to receive L1 and
|
||
L2 seed keys."""
|
||
root_key_id = self.new_root_key(
|
||
use_start_time=ROOT_KEY_START_TIME,
|
||
hash_algorithm=Algorithm.SHA512,
|
||
guid=misc.GUID("09de0b38-c743-7abf-44ea-7a3c3e404314"),
|
||
data=bytes.fromhex(
|
||
"d5912d0eb3bd60e1371b1e525dd83be7fc5baf77018b0dba6bd948b7a98ebe5a"
|
||
"f37674332506a46c52c108a62f2a3e89251ad1bde6d539004679c0658853bb68"
|
||
),
|
||
)
|
||
|
||
current_gkid = Gkid(123, 21, 0)
|
||
key = self.get_key(
|
||
self.get_samdb(),
|
||
self.gmsa_sd,
|
||
root_key_id,
|
||
Gkid(123, 2, 1),
|
||
current_time=current_gkid.start_nt_time(),
|
||
)
|
||
|
||
# Expect to get both L1 and L2 seed keys.
|
||
self.assertEqual(current_gkid, key.gkid)
|
||
self.assertEqual(root_key_id, key.root_key_id)
|
||
self.assertEqual(Algorithm.SHA512, key.hash_algorithm)
|
||
self.assertEqual(
|
||
bytes.fromhex(
|
||
"b1f7c5896e7dc791d9c0aaf8ca7dbab8c172a4f8b873db488a3c4cbd0f559b11"
|
||
"52ffba39d4aff2d9e8aada90b27a3c94a5af996f4b8f584a4f37ccab4d505d3d"
|
||
),
|
||
key.l1_key,
|
||
)
|
||
self.assertEqual(
|
||
bytes.fromhex(
|
||
"133c9bbd20d9227aeb38dfcd3be6bcbfc5983ba37202088ff5c8a70511214506"
|
||
"a69c195a8807cd844bcb955e9569c8e4d197759f28577cc126d15f16a7da4ee0"
|
||
),
|
||
key.l2_key,
|
||
)
|
||
|
||
def test_previous_l0_idx(self):
|
||
"""Request a key with a previous L0 index."""
|
||
root_key_id = self.new_root_key(
|
||
use_start_time=ROOT_KEY_START_TIME,
|
||
hash_algorithm=Algorithm.SHA512,
|
||
guid=misc.GUID("27136e8f-e093-6fe3-e57f-1d915b102e1c"),
|
||
data=bytes.fromhex(
|
||
"b41118c60a19cafa5ecf858d1a2a2216527b2daedf386e9d599e42a46add6c7d"
|
||
"c93868619761c880ff3674a77c6e5fbf3434d130a9727bb2cd2a2557bdcfc752"
|
||
),
|
||
)
|
||
|
||
key = self.get_key(
|
||
self.get_samdb(),
|
||
self.gmsa_sd,
|
||
root_key_id,
|
||
Gkid(100, 20, 30),
|
||
current_time=Gkid(101, 2, 3).start_nt_time(),
|
||
)
|
||
|
||
# Expect to get an L1 seed key.
|
||
self.assertEqual(Gkid(100, 31, 31), key.gkid)
|
||
self.assertEqual(root_key_id, key.root_key_id)
|
||
self.assertEqual(Algorithm.SHA512, key.hash_algorithm)
|
||
self.assertEqual(
|
||
bytes.fromhex(
|
||
"935cbdc06198eb28fa44b8d8278f51072c4613999236585041ede8e72d02fe95"
|
||
"e3454f046382cbc0a700779b79474dd7e080509d76302d2937407e96e3d3d022"
|
||
),
|
||
key.l1_key,
|
||
)
|
||
self.assertIsNone(key.l2_key)
|
||
|
||
def test_sha1(self):
|
||
"""Request a key derived with SHA1."""
|
||
root_key_id = self.new_root_key(
|
||
use_start_time=ROOT_KEY_START_TIME,
|
||
hash_algorithm=Algorithm.SHA1,
|
||
guid=misc.GUID("970abad6-fe55-073a-caf1-b801d3f26bd3"),
|
||
data=bytes.fromhex(
|
||
"3bed03bf0fb7d4013149154f24ca2d59b98db6d588cb1f54eca083855e25eb28"
|
||
"d3562a01adc78c4b70e0b72a59515863e7732b853fba02dd7646e63108441211"
|
||
),
|
||
)
|
||
|
||
current_gkid = Gkid(1, 2, 3)
|
||
key = self.get_key(
|
||
self.get_samdb(),
|
||
self.gmsa_sd,
|
||
root_key_id,
|
||
Gkid(1, 1, 1),
|
||
current_time=current_gkid.start_nt_time(),
|
||
)
|
||
|
||
# Expect to get both L1 and L2 seed keys.
|
||
self.assertEqual(current_gkid, key.gkid)
|
||
self.assertEqual(root_key_id, key.root_key_id)
|
||
self.assertEqual(Algorithm.SHA1, key.hash_algorithm)
|
||
self.assertEqual(
|
||
bytes.fromhex(
|
||
"576cb68f2e52eb739f817b488c3590d86f1c2c365f3fc9201d9c7fee7494853d"
|
||
"58746ee13e48f18aa6fa69f7157de3d07de34e13836792b7c088ffb6914a89c2"
|
||
),
|
||
key.l1_key,
|
||
)
|
||
self.assertEqual(
|
||
bytes.fromhex(
|
||
"3ffb825adaf116b6533207d568a30ed3d3f21c68840941c9456684f9afa11b05"
|
||
"6e0c59391b4d88c495d984c3d680029cc5c594630f34179119c1c5acaae5e90e"
|
||
),
|
||
key.l2_key,
|
||
)
|
||
|
||
def test_sha256(self):
|
||
"""Request a key derived with SHA256."""
|
||
root_key_id = self.new_root_key(
|
||
use_start_time=ROOT_KEY_START_TIME,
|
||
hash_algorithm=Algorithm.SHA256,
|
||
guid=misc.GUID("45e26207-ed33-dcd5-925a-518a0deef69e"),
|
||
data=bytes.fromhex(
|
||
"28b5b6503d3c1d24814de781bb7bfce3ef69eed1ce4809372bee2c506270c5f0"
|
||
"b5c6df597472623f256c86daa0991e8a11a1705f21b2cfdc0bb9db4ba23246a2"
|
||
),
|
||
)
|
||
|
||
current_gkid = Gkid(222, 22, 22)
|
||
key = self.get_key(
|
||
self.get_samdb(),
|
||
self.gmsa_sd,
|
||
root_key_id,
|
||
Gkid(222, 11, 0),
|
||
current_time=current_gkid.start_nt_time(),
|
||
)
|
||
|
||
# Expect to get both L1 and L2 seed keys.
|
||
self.assertEqual(current_gkid, key.gkid)
|
||
self.assertEqual(root_key_id, key.root_key_id)
|
||
self.assertEqual(Algorithm.SHA256, key.hash_algorithm)
|
||
self.assertEqual(
|
||
bytes.fromhex(
|
||
"57aced6e75f83f3af4f879b38b60f090b42e4bfa022fae3e6fd94280b469b0ec"
|
||
"15d8b853a870b5fbdf28708cce19273b74a573acbe0deda8ef515db4691e2dcb"
|
||
),
|
||
key.l1_key,
|
||
)
|
||
self.assertEqual(
|
||
bytes.fromhex(
|
||
"752a0879ae2424c0504c7493599f13e588e1bbdc252f83325ad5b1fb91c24c89"
|
||
"01d440f3ff9ffba59fcd65bb975732d9f383dd50b898174bb9393e383d25d540"
|
||
),
|
||
key.l2_key,
|
||
)
|
||
|
||
def test_sha384(self):
|
||
"""Request a key derived with SHA384."""
|
||
root_key_id = self.new_root_key(
|
||
use_start_time=ROOT_KEY_START_TIME,
|
||
hash_algorithm=Algorithm.SHA384,
|
||
guid=misc.GUID("66e6d9f7-4924-f3fc-fe34-605634d42ebd"),
|
||
data=bytes.fromhex(
|
||
"23e5ba86cbd88f7b432ee66dbb03bf4eebf401cbfc3df735d4d728b503c87f84"
|
||
"3207c6f6153f190dfe85a86cb8d8b74df13b25305981be8d7e29c96ee54c9630"
|
||
),
|
||
)
|
||
|
||
current_gkid = Gkid(287, 28, 27)
|
||
key = self.get_key(
|
||
self.get_samdb(),
|
||
self.gmsa_sd,
|
||
root_key_id,
|
||
Gkid(287, 8, 7),
|
||
current_time=current_gkid.start_nt_time(),
|
||
)
|
||
|
||
# Expect to get both L1 and L2 seed keys.
|
||
self.assertEqual(current_gkid, key.gkid)
|
||
self.assertEqual(root_key_id, key.root_key_id)
|
||
self.assertEqual(Algorithm.SHA384, key.hash_algorithm)
|
||
self.assertEqual(
|
||
bytes.fromhex(
|
||
"fabadd7a9a63df57d6832df7a735aebb6e181888b2eaf301a2e4ff9a70246d38"
|
||
"ab1d2416325bf3eb726a0267bab4bd950c7291f05ea5f17197ece56992af3eb8"
|
||
),
|
||
key.l1_key,
|
||
)
|
||
self.assertEqual(
|
||
bytes.fromhex(
|
||
"ec1c65634b5694818e1d341da9996db8f2a1ef6a2c776a7126a7ebd18b37a073"
|
||
"afdac44c41b167b14e4b872d485bbb6d7b70964215d0e84a2ff142a9d943f205"
|
||
),
|
||
key.l2_key,
|
||
)
|
||
|
||
def test_derive_key_exact(self):
|
||
"""Derive a key at an exact GKID."""
|
||
root_key_id = self.new_root_key(
|
||
use_start_time=ROOT_KEY_START_TIME,
|
||
hash_algorithm=Algorithm.SHA512,
|
||
guid=misc.GUID("d95fb06f-5a9c-1829-e20d-27f3f2ecfbeb"),
|
||
data=bytes.fromhex(
|
||
"489f3531c537774d432d6b97e3bc1f43d2e8c6dc17eb0e4fd9a0870d2f1ebf92"
|
||
"e2496668a8b5bd11aea2d32d0aab716f48fe569f5c9b50ff3f9bf5deaea572fb"
|
||
),
|
||
)
|
||
|
||
gkid = Gkid(333, 22, 11)
|
||
key = self.get_key_exact(
|
||
self.get_samdb(),
|
||
self.gmsa_sd,
|
||
root_key_id,
|
||
gkid,
|
||
current_time=self.current_nt_time(self.get_samdb()),
|
||
)
|
||
|
||
self.assertEqual(gkid, key.gkid)
|
||
self.assertEqual(root_key_id, key.root_key_id)
|
||
self.assertEqual(Algorithm.SHA512, key.hash_algorithm)
|
||
self.assertEqual(
|
||
bytes.fromhex(
|
||
"d6ab3b14f4f4c8908aa3464011b39f10a8bfadb9974af90f7d9a9fede2fdc6e5"
|
||
"f68a628ec00f9994a3abd8a52ae9e2db4f68e83648311e9d7765f2535515b5e2"
|
||
),
|
||
key.key,
|
||
)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
import unittest
|
||
|
||
unittest.main()
|