#!/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 .
#
import sys
import os
sys.path.insert(0, "bin/python")
os.environ["PYTHONUNBUFFERED"] = "1"
from samba.dcerpc import gkdi, misc
from samba.ndr import ndr_pack, ndr_unpack
import samba.tests
def utf16_encoded_len(s: str) -> int:
"""Return the number of bytes required to encode a string as null‐terminated
UTF‐16."""
if "\x00" in s:
raise ValueError("string contains an embedded null")
return len(s.encode("utf-16-le")) + 2
class KeyEnvelopeTests(samba.tests.TestCase):
key_envelope_blob = (
b"\x01\x00\x00\x00KDSK\x02\x00\x00\x00j\x01\x00\x00\x01\x00\x00\x00"
b"\x0e\x00\x00\x001\"\x92\x9d'\xaf;\xb7\x10V\xae\xb1\x8e\xec\xa7\x1a"
b"\x00\x00\x00\x00\x18\x00\x00\x00\x18\x00\x00\x00e\x00x\x00a\x00m\x00"
b"p\x00l\x00e\x00.\x00c\x00o\x00m\x00\x00\x00e\x00x\x00a\x00m\x00p\x00l\x00"
b"e\x00.\x00c\x00o\x00m\x00\x00\x00"
)
root_key_id = misc.GUID("9d922231-af27-b73b-1056-aeb18eeca71a")
domain_name = "example.com"
forest_name = "example.com"
def test_unpack(self):
"""Unpack a GKDI Key Envelope blob and check its fields."""
envelope = ndr_unpack(gkdi.KeyEnvelope, self.key_envelope_blob)
self.assertEqual(1, envelope.version)
self.assertEqual(int.from_bytes(b"KDSK", byteorder="little"), envelope.magic)
self.assertEqual(gkdi.ENVELOPE_FLAG_KEY_MAY_ENCRYPT_NEW_DATA, envelope.flags)
self.assertEqual(362, envelope.l0_index)
self.assertEqual(1, envelope.l1_index)
self.assertEqual(14, envelope.l2_index)
self.assertEqual(self.root_key_id, envelope.root_key_id)
self.assertEqual(0, envelope.additional_info_len)
self.assertFalse(envelope.additional_info)
self.assertEqual(self.domain_name, envelope.domain_name)
self.assertEqual(utf16_encoded_len(self.domain_name), envelope.domain_name_len)
self.assertEqual(self.forest_name, envelope.forest_name)
self.assertEqual(utf16_encoded_len(self.forest_name), envelope.forest_name_len)
def test_pack(self):
"""Create a GKDI Key Envelope object and test that it packs to the
blob we expect."""
envelope = gkdi.KeyEnvelope()
envelope.version = 1
envelope.flags = gkdi.ENVELOPE_FLAG_KEY_MAY_ENCRYPT_NEW_DATA
envelope.l0_index = 362
envelope.l1_index = 1
envelope.l2_index = 14
envelope.root_key_id = self.root_key_id
envelope.additional_info = []
envelope.additional_info_len = 0
envelope.domain_name = self.domain_name
envelope.forest_name = self.forest_name
self.assertEqual(self.key_envelope_blob, ndr_pack(envelope))
class GroupKeyEnvelopeTests(samba.tests.TestCase):
group_key_envelope_blob = (
b"\x01\x00\x00\x00KDSK\x00\x00\x00\x00j\x01\x00\x00\x01\x00\x00\x00"
b"\x0e\x00\x00\x00\x8c\xc4\x8c\xdevp\x94\x97\x05m\x897{Z\x80R&\x00\x00\x00"
b"\x1e\x00\x00\x00\x06\x00\x00\x00\x0c\x02\x00\x00\x00\x02\x00\x00"
b"\x00\x08\x00\x00@\x00\x00\x00@\x00\x00\x00\x18\x00\x00\x00\x18\x00\x00\x00"
b"S\x00P\x008\x000\x000\x00_\x001\x000\x008\x00_\x00C\x00T\x00R\x00_\x00"
b"H\x00M\x00A\x00C\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x0e\x00"
b"\x00\x00\x00\x00\x00\x00S\x00H\x00A\x005\x001\x002\x00\x00\x00D\x00H\x00"
b"\x00\x00\x0c\x02\x00\x00DHPM\x00\x01\x00\x00\x87\xa8\xe6\x1d\xb4\xb6"
b"f<\xff\xbb\xd1\x9ce\x19Y\x99\x8c\xee\xf6\x08f\r\xd0\xf2],\xee\xd4C^"
b";\x00\xe0\r\xf8\xf1\xd6\x19W\xd4\xfa\xf7\xdfEa\xb2\xaa0\x16\xc3\xd9\x114\t"
b"o\xaa;\xf4)m\x83\x0e\x9a| \x9e\x0cd\x97Qz\xbdZ\x8a\x9d0k\xcfg\xed\x91\xf9"
b'\xe6r[GX\xc0"\xe0\xb1\xefBu\xbf{l[\xfc\x11\xd4_\x90\x88\xb9A\xf5N\xb1\xe5'
b"\x9b\xb8\xbc9\xa0\xbf\x120\x7f\\O\xdbp\xc5\x81\xb2?v\xb6:\xca\xe1\xca\xa6"
b"\xb7\x90-RRg5H\x8a\x0e\xf1