From: Douglas Bagnall Date: Wed, 30 Jul 2025 03:15:04 +0000 (+1200) Subject: python: add helpers to construct KeyCredentialLinkDn objects X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=0cedf27dcfda591a2421950c5f4ad38db41eaff6;p=thirdparty%2Fsamba.git python: add helpers to construct KeyCredentialLinkDn objects We want to ensure the as best we can that the binary blob is in a useful format. This will be used by samba-tool. Signed-off-by: Douglas Bagnall Reviewed-by: Gary Lockyer --- diff --git a/python/samba/key_credential_link.py b/python/samba/key_credential_link.py index 5371f8b4167..2ef5e00cbff 100644 --- a/python/samba/key_credential_link.py +++ b/python/samba/key_credential_link.py @@ -18,9 +18,26 @@ """Functions for processing key_credential_link""" +from hashlib import sha256 +import struct +from typing import Optional, Union -from samba.samdb import BinaryDn +from cryptography.hazmat.primitives.serialization import ( + load_der_public_key, + load_pem_public_key, + PublicFormat, + Encoding) + +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey + +from cryptography.x509 import ( + load_pem_x509_certificate, + load_der_x509_certificate) + + +from samba.samdb import SamDB, BinaryDn from samba.ndr import ndr_unpack, ndr_pack +from ldb import Dn from samba.dcerpc import keycredlink @@ -54,3 +71,146 @@ class KeyCredentialLinkDn(BinaryDn): except Exception as e: raise ValueError("Could not parse value as KEYCREDENTIALLINK_BLOB " f" (internal error: {e})") + + +def get_public_key(data:bytes, encoding:str): + """decode a key in PEM or DER format. + + If it turns out to be a certificate or something, we try to get + the public key from that. + + So far only RSA keys are supported. + """ + if encoding is None: + if data[:11] == b'-----BEGIN ': + encoding = 'PEM' + else: + encoding = 'DER' + + encoding = encoding.upper() + + # The cryptography module also supports ssh keys, PKCS1, and other + # formats, as well as non-RSA keys and extracting public keys from + # private. It might not be wise to tolerate all of this, but we + # can do it by adding to key_fns and cert_fns here. + if encoding == 'PEM': + key_fns = [load_pem_public_key] + cert_fns = [load_pem_x509_certificate] + elif encoding == 'DER': + key_fns = [load_der_public_key] + cert_fns = [load_der_x509_certificate] + else: + raise ValueError(f"Public key encoding '{encoding}' not supported " + "(try 'PEM' or 'DER')") + + key = None + for fn in key_fns: + try: + key = fn(data) + break + except ValueError: + continue + + if key is None: + for fn in cert_fns: + try: + cert = fn(data) + key = cert.public_key() + break + except ValueError: + continue + + if key is None: + raise ValueError("could not decode public key") + + if not isinstance(key, RSAPublicKey): + raise ValueError("Currently only RSA Public Keys are supported " + f"(not '{key}')") + + return key + + +def kcl_entry_bytes(entry_type:int, data:bytes) -> bytes: + """helper to pack key credential link entries""" + return struct.pack('