### Setup development environment Source: https://github.com/jborean93/sspilib/blob/main/README.md Clone the repository, install development requirements, set up pre-commit hooks, and install the package in editable mode. ```bash git clone https://github.com/jborean93/sspilib.git pip install -r requirements-dev.txt pre-commit install python -m pip install -e . ``` -------------------------------- ### Install sspilib from source Source: https://github.com/jborean93/sspilib/blob/main/README.md Clone the repository, build the package, and install the wheel file. ```bash git clone https://github.com/jborean93/sspilib.git python -m pip install build python -m build pip install dist/sspilib-*.whl ``` -------------------------------- ### Install sspilib using pip Source: https://github.com/jborean93/sspilib/blob/main/README.md Install the library using pip. ```bash pip install sspilib ``` -------------------------------- ### Client Authentication Example Source: https://github.com/jborean93/sspilib/blob/main/README.md Demonstrates client-side SSPI authentication using UserCredential and ClientSecurityContext. Requires a function `exchange_with_server` to handle token exchange. ```python import sspilib cred = sspilib.UserCredential( "username@DOMAIN.COM", "password", ) ctx = sspilib.ClientSecurityContext( credential=cred, target_name="host/server.domain.com", ) in_token = None while not ctx.complete: out_token = ctx.step(in_token) if not out_token: break # exchange_with_server() is a function that sends the out_token to the # server we are authenticating with. How this works depends on the app # protocol being used, e.g. HTTP, sockets, LDAP, etc. in_token = exchange_with_server(out_token) # Once authenticated we can wrap messages when talking to the server. The final # message being sent is dependent on the application protocol secret = b"secret data" wrapped_secret = ctx.wrap(secret) server_enc_resp = exchange_with_server(wrapped_secret) server_resp = ctx.unwrap(server_enc_resp).data ``` -------------------------------- ### Enumerate Available Security Packages Source: https://context7.com/jborean93/sspilib/llms.txt Lists all installed security packages available on the system, providing their names, maximum token sizes, and comments. The package name can be used directly with acquire_credentials_handle. This snippet also demonstrates how to check for specific package capabilities like GSS compatibility or delegation. ```python import sspilib.raw as sr packages = sr.enumerate_security_packages() for pkg in packages: print(f"{pkg.name!s:<20} max_token={pkg.max_token:>6} {pkg.comment}") # Example output (Windows): # Negotiate max_token= 65535 Microsoft Package Negotiator # Kerberos max_token= 65535 Microsoft Kerberos V1.0 # NTLM max_token= 2888 NTLM Security Package # ... # Look up a specific package's capabilities kerberos = next(p for p in packages if p.name == "Kerberos") from sspilib.raw import SecurityPackageCapability as Cap print("Supports GSS:", bool(kerberos.capabilities & Cap.SECPKG_FLAG_GSS_COMPATIBLE)) print("Supports delegation:", bool(kerberos.capabilities & Cap.SECPKG_FLAG_DELEGATION)) ``` -------------------------------- ### sspilib.raw.enumerate_security_packages Source: https://context7.com/jborean93/sspilib/llms.txt Lists available SSPI packages by wrapping `EnumerateSecurityPackages`. Returns a list of `SecPkgInfo` dataclasses describing each installed security package. ```APIDOC ## `sspilib.raw.enumerate_security_packages` — List available SSPI packages Wraps `EnumerateSecurityPackages`. Returns a list of `SecPkgInfo` dataclasses describing each installed security package. The `name` field can be used directly as the `package` argument to `acquire_credentials_handle`. ```python import sspilib.raw as sr packages = sr.enumerate_security_packages() for pkg in packages: print(f"{pkg.name!s:<20} max_token={pkg.max_token:>6} {pkg.comment}") # Example output (Windows): # Negotiate max_token= 65535 Microsoft Package Negotiator # Kerberos max_token= 65535 Microsoft Kerberos V1.0 # NTLM max_token= 2888 NTLM Security Package # ... # Look up a specific package's capabilities kerberos = next(p for p in packages if p.name == "Kerberos") from sspilib.raw import SecurityPackageCapability as Cap print("Supports GSS:", bool(kerberos.capabilities & Cap.SECPKG_FLAG_GSS_COMPATIBLE)) print("Supports delegation:", bool(kerberos.capabilities & Cap.SECPKG_FLAG_DELEGATION)) ``` ``` -------------------------------- ### Acquire Raw SSPI Credential Handle Source: https://context7.com/jborean93/sspilib/llms.txt Directly binds to AcquireCredentialsHandle to get a raw SSPI credential handle. Supports explicit identities and restricting Negotiate package sub-protocols. ```python import sspilib.raw as sr # Outbound credential with explicit identity identity = sr.WinNTAuthIdentity( username="alice", domain="CORP", password="s3cr3t!", ) result = sr.acquire_credentials_handle( principal=None, package="Negotiate", credential_use=sr.CredentialUse.SECPKG_CRED_OUTBOUND, auth_data=identity, ) cred: sr.CredHandle = result.credential expiry_filetime: int = result.expiry print("Credential acquired, expires (FILETIME):", expiry_filetime) # Inbound credential for a server (uses process token on Windows) s_cred_result = sr.acquire_credentials_handle( principal=None, package="Negotiate", credential_use=sr.CredentialUse.SECPKG_CRED_INBOUND, ) s_cred = s_cred_result.credential # Restrict Negotiate to NTLM only via package_list ntlm_only = sr.WinNTAuthIdentity(package_list="!Kerberos") ntlm_cred_result = sr.acquire_credentials_handle( None, "Negotiate", sr.CredentialUse.SECPKG_CRED_OUTBOUND, auth_data=ntlm_only ) ``` -------------------------------- ### Accept Security Context - Server Step 1 Source: https://context7.com/jborean93/sspilib/llms.txt Accepts the initial client token on the server side to begin the security context establishment. The output challenge token should be sent back to the client. ```python import socket import sspilib.raw as sr spn = f"host/{socket.gethostname()}" s_cred = sr.acquire_credentials_handle( None, "Negotiate", sr.CredentialUse.SECPKG_CRED_INBOUND ).credential # client_token_1 received from client's first ISC call client_token_1 = b"..." s_in = sr.SecBufferDesc([sr.SecBuffer(bytearray(client_token_1), sr.SecBufferType.SECBUFFER_TOKEN)]) s_out = sr.SecBufferDesc([sr.SecBuffer(None, sr.SecBufferType.SECBUFFER_TOKEN)]) s_res = sr.accept_security_context( credential=s_cred, context=None, input_buffers=s_in, context_req=sr.AscReq.ASC_REQ_ALLOCATE_MEMORY | sr.AscReq.ASC_REQ_CONFIDENTIALITY, target_data_rep=sr.TargetDataRep.SECURITY_NATIVE_DREP, output_buffers=s_out, ) server_challenge = s_out[0].data # send to client assert s_res.status == sr.NtStatus.SEC_I_CONTINUE_NEEDED ``` -------------------------------- ### Initialize Security Context - Client Step 2 Source: https://context7.com/jborean93/sspilib/llms.txt Processes the server's challenge token and generates the final client token to complete the security context establishment. This step reuses the context from the previous call. ```python # --- Step 2: process server challenge, generate final client token --- server_token = ... # bytes received from server c_in = sr.SecBufferDesc([sr.SecBuffer(bytearray(server_token), sr.SecBufferType.SECBUFFER_TOKEN)]) c_out2 = sr.SecBufferDesc([sr.SecBuffer(None, sr.SecBufferType.SECBUFFER_TOKEN)]) c_res2 = sr.initialize_security_context( credential=c_cred, context=c_res.context, # reuse context from step 1 target_name=spn, context_req=sr.IscReq.ISC_REQ_ALLOCATE_MEMORY | sr.IscReq.ISC_REQ_CONFIDENTIALITY, target_data_rep=sr.TargetDataRep.SECURITY_NATIVE_DREP, input_buffers=c_in, output_buffers=c_out2, ) assert c_res2.status == sr.NtStatus.SEC_E_OK print("Client context established, attributes:", c_res2.attributes) ``` -------------------------------- ### Initialize Security Context - Client Step 1 Source: https://context7.com/jborean93/sspilib/llms.txt Generates the initial client token for establishing a security context. This is the first step in the client-side authentication handshake. The output token should be sent to the server. ```python import socket import sspilib.raw as sr spn = f"host/{socket.gethostname()}" identity = sr.WinNTAuthIdentity(username="alice", domain="CORP", password="s3cr3t!") c_cred = sr.acquire_credentials_handle( None, "NTLM", sr.CredentialUse.SECPKG_CRED_OUTBOUND, auth_data=identity ).credential # --- Step 1: generate the first client token (no input) --- c_out = sr.SecBufferDesc([sr.SecBuffer(None, sr.SecBufferType.SECBUFFER_TOKEN)]) c_res = sr.initialize_security_context( credential=c_cred, context=None, target_name=spn, context_req=sr.IscReq.ISC_REQ_ALLOCATE_MEMORY | sr.IscReq.ISC_REQ_CONFIDENTIALITY, target_data_rep=sr.TargetDataRep.SECURITY_NATIVE_DREP, input_buffers=None, output_buffers=c_out, ) assert c_res.status == sr.NtStatus.SEC_I_CONTINUE_NEEDED client_token_1 = c_out[0].data # send to server ``` -------------------------------- ### initialize_security_context Source: https://context7.com/jborean93/sspilib/llms.txt Provides a low-level client-side authentication step using InitializeSecurityContext. It shows how to generate the initial client token and process the server's challenge to establish a security context. ```APIDOC ## `sspilib.raw.initialize_security_context` — Low-level client authentication step Direct binding to `InitializeSecurityContext`. Pass `context=None` on the first call and the returned `context` on subsequent calls. Returns `InitializeContextResult(context, attributes, expiry, status)`. Loop until `status == NtStatus.SEC_E_OK`. ```python import socket import sspilib.raw as sr spn = f"host/{socket.gethostname()}" identity = sr.WinNTAuthIdentity(username="alice", domain="CORP", password="s3cr3t!") c_cred = sr.acquire_credentials_handle( None, "NTLM", sr.CredentialUse.SECPKG_CRED_OUTBOUND, auth_data=identity ).credential # --- Step 1: generate the first client token (no input) --- c_out = sr.SecBufferDesc([sr.SecBuffer(None, sr.SecBufferType.SECBUFFER_TOKEN)]) c_res = sr.initialize_security_context( credential=c_cred, context=None, target_name=spn, context_req=sr.IscReq.ISC_REQ_ALLOCATE_MEMORY | sr.IscReq.ISC_REQ_CONFIDENTIALITY, target_data_rep=sr.TargetDataRep.SECURITY_NATIVE_DREP, input_buffers=None, output_buffers=c_out, ) assert c_res.status == sr.NtStatus.SEC_I_CONTINUE_NEEDED client_token_1 = c_out[0].data # send to server # --- Step 2: process server challenge, generate final client token --- server_token = ... # bytes received from server c_in = sr.SecBufferDesc([sr.SecBuffer(bytearray(server_token), sr.SecBufferType.SECBUFFER_TOKEN)]) c_out2 = sr.SecBufferDesc([sr.SecBuffer(None, sr.SecBufferType.SECBUFFER_TOKEN)]) c_res2 = sr.initialize_security_context( credential=c_cred, context=c_res.context, # reuse context from step 1 target_name=spn, context_req=sr.IscReq.ISC_REQ_ALLOCATE_MEMORY | sr.IscReq.ISC_REQ_CONFIDENTIALITY, target_data_rep=sr.TargetDataRep.SECURITY_NATIVE_DREP, input_buffers=c_in, output_buffers=c_out2, ) assert c_res2.status == sr.NtStatus.SEC_E_OK print("Client context established, attributes:", c_res2.attributes) ``` ``` -------------------------------- ### Complete Security Context Establishment Source: https://context7.com/jborean93/sspilib/llms.txt This snippet demonstrates the server-side completion of a security context after receiving the final client token. It uses `accept_security_context` and `complete_auth_token` to finalize the handshake. ```python final_client_token = b"..." s_in2 = sr.SecBufferDesc([sr.SecBuffer(bytearray(final_client_token), sr.SecBufferType.SECBUFFER_TOKEN)]) s_out2 = sr.SecBufferDesc([sr.SecBuffer(None, sr.SecBufferType.SECBUFFER_TOKEN)]) s_res2 = sr.accept_security_context( credential=s_cred, context=s_res.context, input_buffers=s_in2, context_req=sr.AscReq.ASC_REQ_ALLOCATE_MEMORY | sr.AscReq.ASC_REQ_CONFIDENTIALITY, target_data_rep=sr.TargetDataRep.SECURITY_NATIVE_DREP, output_buffers=s_out2, ) if s_res2.status == sr.NtStatus.SEC_I_COMPLETE_NEEDED: sr.complete_auth_token(s_res2.context, s_in2) print("Server context established, attributes:", s_res2.attributes) ``` -------------------------------- ### accept_security_context Source: https://context7.com/jborean93/sspilib/llms.txt Details the low-level server-side authentication step using AcceptSecurityContext. It illustrates how to process incoming client tokens and generate server challenges to establish a security context. ```APIDOC ## `sspilib.raw.accept_security_context` — Low-level server authentication step Direct binding to `AcceptSecurityContext`. Called in a loop on the server side with each client token. Returns `AcceptContextResult(context, attributes, expiry, status)`. When `status == NtStatus.SEC_I_COMPLETE_NEEDED`, call `complete_auth_token` before proceeding. ```python import socket import sspilib.raw as sr spn = f"host/{socket.gethostname()}" s_cred = sr.acquire_credentials_handle( None, "Negotiate", sr.CredentialUse.SECPKG_CRED_INBOUND ).credential # client_token_1 received from client's first ISC call client_token_1 = b"..." s_in = sr.SecBufferDesc([sr.SecBuffer(bytearray(client_token_1), sr.SecBufferType.SECBUFFER_TOKEN)]) s_out = sr.SecBufferDesc([sr.SecBuffer(None, sr.SecBufferType.SECBUFFER_TOKEN)]) s_res = sr.accept_security_context( credential=s_cred, context=None, input_buffers=s_in, context_req=sr.AscReq.ASC_REQ_ALLOCATE_MEMORY | sr.AscReq.ASC_REQ_CONFIDENTIALITY, target_data_rep=sr.TargetDataRep.SECURITY_NATIVE_DREP, output_buffers=s_out, ) server_challenge = s_out[0].data # send to client assert s_res.status == sr.NtStatus.SEC_I_CONTINUE_NEEDED ``` ``` -------------------------------- ### sspilib.KeytabCredential - From File Path Source: https://context7.com/jborean93/sspilib/llms.txt Acquire a Kerberos credential from a keytab file path. Suitable for service accounts on Windows. Specify 'accept' for server-side usage. ```python import pathlib import sspilib # From a keytab file path (service account) svc_cred = sspilib.KeytabCredential( username="http/webserver.corp.example.com@CORP.EXAMPLE.COM", keytab=pathlib.Path("/etc/krb5.keytab"), protocol="Kerberos", usage="accept", # server-side ) print(svc_cred) # http/webserver.corp.example.com@CORP.EXAMPLE.COM - Kerberos print(svc_cred.expiry) # datetime of credential expiry ``` -------------------------------- ### Compile SSPI extensions Source: https://github.com/jborean93/sspilib/blob/main/README.md Compile the Cython SSPI extensions in-place. ```bash python setup.py build_ext --inplace ``` -------------------------------- ### Acquire Credentials Handle with Packed Keytab Credential Source: https://context7.com/jborean93/sspilib/llms.txt Acquires inbound credentials using a packed keytab file. Ensure the keytab file is readable and the username/domain are correctly specified for the service principal. ```python import sspilib.raw as sr with open("/etc/krb5.keytab", "rb") as fh: keytab_bytes = fh.read() packed_id = sr.WinNTAuthIdentityPackedCredential( credential_type=sr.WinNTAuthCredentialType.SEC_WINNT_AUTH_DATA_TYPE_KEYTAB, credential=keytab_bytes, username="http/webserver@CORP.EXAMPLE.COM", domain=None, package_list="Kerberos", ) cred = sr.acquire_credentials_handle( None, "Negotiate", sr.CredentialUse.SECPKG_CRED_INBOUND, auth_data=packed_id ) print("Keytab credential acquired") ``` -------------------------------- ### Manage Security Buffers with SecBuffer and SecBufferDesc Source: https://context7.com/jborean93/sspilib/llms.txt Demonstrates the usage of SecBuffer for typed byte buffers and SecBufferDesc for grouping buffers for SSPI functions. Shows how to handle pre-allocated and SSPI-allocated buffers, copy data, and manage channel bindings. ```python import sspilib.raw as sr # Pre-allocated buffer (caller manages memory) buf = bytearray(4096) token_buf = sr.SecBuffer(buf, sr.SecBufferType.SECBUFFER_TOKEN) print("Buffer type:", token_buf.buffer_type) # SECBUFFER_TOKEN print("Buffer len:", token_buf.count) # 4096 # SSPI-allocated buffer (ISC_REQ_ALLOCATE_MEMORY) alloc_buf = sr.SecBuffer(None, sr.SecBufferType.SECBUFFER_TOKEN) desc = sr.SecBufferDesc([token_buf, alloc_buf]) print("Number of buffers:", len(desc)) # 2 for b in desc: print(b.buffer_type, b.count) # Safely copy data after an SSPI call data_copy: bytes = desc[0].data # copies bytes out # Zero-copy view (only valid while buf bytearray is alive) view: memoryview = desc[0].dangerous_get_view() # Channel bindings buffer cb = sr.SecChannelBindings(application_data=b"tls-server-end-point:" + bytes(32)) cb_buf_copy = cb.get_sec_buffer_copy() # safe copy cb_buf_ref = cb.dangerous_get_sec_buffer() # zero-copy reference ``` -------------------------------- ### sspilib.KeytabCredential - From Raw Bytes Source: https://context7.com/jborean93/sspilib/llms.txt Acquire a Kerberos credential from raw keytab bytes. Suitable for client-side initiation. ```python # From raw keytab bytes with open("/etc/krb5.keytab", "rb") as fh: keytab_data = fh.read() client_cred = sspilib.KeytabCredential( username="alice@CORP.EXAMPLE.COM", keytab=keytab_data, protocol="Kerberos", usage="initiate", # client-side ) ``` -------------------------------- ### Configure KDC Proxy Settings for Credentials Source: https://context7.com/jborean93/sspilib/llms.txt Configures credential attributes to route Kerberos KDC traffic through an HTTPS proxy using SecPkgCredKdcProxySettings. Ensure the proxy server string is correctly formatted. ```python import sspilib.raw as sr cred = sr.acquire_credentials_handle( None, "Kerberos", sr.CredentialUse.SECPKG_CRED_OUTBOUND ).credential proxy = sr.SecPkgCredKdcProxySettings( flags=sr.KdcProxySettingsFlags.KDC_PROXY_SETTINGS_FLAGS_FORCEPROXY, proxy_server="kdc-proxy.corp.example.com:443:KdcProxy", ) sr.set_credentials_attributes(cred, proxy) print("KDC proxy configured:", proxy.proxy_server) # All Kerberos tickets acquired using this credential now route through the proxy ``` -------------------------------- ### sspilib.ClientSecurityContext Source: https://context7.com/jborean93/sspilib/llms.txt Manages the full client-side SSPI authentication handshake. Call `step()` in a loop, exchanging output tokens with the server, until `ctx.complete` is `True`. ```APIDOC ## sspilib.ClientSecurityContext ### Description Manages the full client-side SSPI authentication handshake. Call `step()` in a loop, exchanging output tokens with the server, until `ctx.complete` is `True`. Default flags request mutual auth, replay/sequence detection, confidentiality, and integrity. After completion, use `wrap()`/`unwrap()` or `sign()`/`verify()` for message protection. ### Usage ```python import socket import sspilib def exchange_with_server(token: bytes) -> bytes: """Placeholder: send token to server, receive server token.""" ... cred = sspilib.UserCredential("alice@CORP.EXAMPLE.COM", "s3cr3t!") spn = f"host/{socket.getfqdn('server.corp.example.com')}" ctx = sspilib.ClientSecurityContext( credential=cred, target_name=spn, # Optional: override default flags # flags=sspilib.IscReq.ISC_REQ_MUTUAL_AUTH | sspilib.IscReq.ISC_REQ_CONFIDENTIALITY, ) # Authentication loop in_token = None while not ctx.complete: out_token = ctx.step(in_token) if not out_token: break in_token = exchange_with_server(out_token) print("Authenticated:", ctx.complete) print("Context expiry:", ctx.expiry) print("Negotiated flags:", ctx.attributes) ``` ``` -------------------------------- ### sspilib.UserCredential - Server Accept Credential Source: https://context7.com/jborean93/sspilib/llms.txt Create a UserCredential for server-side acceptance in a ServerSecurityContext. ```python # Server-side accept credential (for ServerSecurityContext) server_cred = sspilib.UserCredential(usage="accept") ``` -------------------------------- ### WinNTAuthIdentityPackedCredential Source: https://context7.com/jborean93/sspilib/llms.txt Demonstrates how to use WinNTAuthIdentityPackedCredential for non-password credential types like keytabs, and how to acquire credentials using it for inbound security context. ```APIDOC ## `sspilib.raw.WinNTAuthIdentityPackedCredential` — Packed credential identity Provides support for non-password credential types (keytab, certificate, FIDO, etc.) by packing opaque credential bytes with a type UUID. See `WinNTAuthCredentialType` for pre-defined type constants. ```python import sspilib.raw as sr with open("/etc/krb5.keytab", "rb") as fh: keytab_bytes = fh.read() packed_id = sr.WinNTAuthIdentityPackedCredential( credential_type=sr.WinNTAuthCredentialType.SEC_WINNT_AUTH_DATA_TYPE_KEYTAB, credential=keytab_bytes, username="http/webserver@CORP.EXAMPLE.COM", domain=None, package_list="Kerberos", ) cred = sr.acquire_credentials_handle( None, "Negotiate", sr.CredentialUse.SECPKG_CRED_INBOUND, auth_data=packed_id ) print("Keytab credential acquired") ``` ``` -------------------------------- ### Compile sspi-rs on Linux/macOS Source: https://github.com/jborean93/sspilib/blob/main/README.md Compile the sspi-rs native library and set environment variables for dynamic linking. ```bash cargo build \ --package sspi-ffi \ --release export LD_LIBRARY_PATH="${PWD}/target/release" export LIBRARY_PATH="${PWD}/target/release" ``` -------------------------------- ### sspilib.raw.acquire_credentials_handle Source: https://context7.com/jborean93/sspilib/llms.txt Acquire a raw SSPI credential handle. Direct binding to `AcquireCredentialsHandle`. Returns an `AcquireCredentialsResult(credential, expiry)` named tuple. The `credential` is a `CredHandle` suitable for `initialize_security_context` or `accept_security_context`. The `expiry` is a raw Windows FILETIME integer. ```APIDOC ## sspilib.raw.acquire_credentials_handle ### Description Acquire a raw SSPI credential handle. Direct binding to `AcquireCredentialsHandle`. Returns an `AcquireCredentialsResult(credential, expiry)` named tuple. The `credential` is a `CredHandle` suitable for `initialize_security_context` or `accept_security_context`. The `expiry` is a raw Windows FILETIME integer. ### Method `acquire_credentials_handle(principal: str | None, package: str, credential_use: CredentialUse, auth_data: WinNTAuthIdentity | None = None) -> AcquireCredentialsResult` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```python import sspilib.raw as sr # Outbound credential with explicit identity identity = sr.WinNTAuthIdentity( username="alice", domain="CORP", password="s3cr3t!", ) result = sr.acquire_credentials_handle( principal=None, package="Negotiate", credential_use=sr.CredentialUse.SECPKG_CRED_OUTBOUND, auth_data=identity, ) cred: sr.CredHandle = result.credential expiry_filetime: int = result.expiry print("Credential acquired, expires (FILETIME):", expiry_filetime) # Inbound credential for a server (uses process token on Windows) s_cred_result = sr.acquire_credentials_handle( principal=None, package="Negotiate", credential_use=sr.CredentialUse.SECPKG_CRED_INBOUND, ) s_cred = s_cred_result.credential # Restrict Negotiate to NTLM only via package_list ntlm_only = sr.WinNTAuthIdentity(package_list="!Kerberos") ntlm_cred_result = sr.acquire_credentials_handle( None, "Negotiate", sr.CredentialUse.SECPKG_CRED_OUTBOUND, auth_data=ntlm_only ) ``` ### Response #### Success Response (200) None #### Response Example None ``` -------------------------------- ### sspilib.ClientSecurityContext - Authentication Loop Source: https://context7.com/jborean93/sspilib/llms.txt Manages the client-side SSPI authentication handshake. Call step() in a loop, exchanging tokens with the server until complete. Default flags request mutual auth, replay/sequence detection, confidentiality, and integrity. ```python import socket import sspilib def exchange_with_server(token: bytes) -> bytes: """Placeholder: send token to server, receive server token.""" ... cred = sspilib.UserCredential("alice@CORP.EXAMPLE.COM", "s3cr3t!") spn = f"host/{socket.getfqdn('server.corp.example.com')}" ctx = sspilib.ClientSecurityContext( credential=cred, target_name=spn, # Optional: override default flags # flags=sspilib.IscReq.ISC_REQ_MUTUAL_AUTH | sspilib.IscReq.ISC_REQ_CONFIDENTIALITY, ) # Authentication loop in_token = None while not ctx.complete: out_token = ctx.step(in_token) if not out_token: break in_token = exchange_with_server(out_token) print("Authenticated:", ctx.complete) print("Context expiry:", ctx.expiry) print("Negotiated flags:", ctx.attributes) ``` -------------------------------- ### Query SSPILIb Context Attributes Source: https://context7.com/jborean93/sspilib/llms.txt Shows how to retrieve various properties of an established security context using `query_context_attributes`. This includes buffer sizing, authenticated username, negotiated package name, and the session key. ```python import sspilib.raw as sr # ctx is a fully established CtxtHandle # Buffer sizes needed for wrap/sign operations sizes = sr.query_context_attributes(ctx, sr.SecPkgContextSizes) print("Max signature:", sizes.max_signature) print("Security trailer:", sizes.security_trailer) print("Block size:", sizes.block_size) # The authenticated username names = sr.query_context_attributes(ctx, sr.SecPkgContextNames) print("Authenticated as:", names.username) # The negotiated security package pkg_info = sr.query_context_attributes(ctx, sr.SecPkgContextPackageInfo) print("Package:", pkg_info.name, "version:", pkg_info.version) # Session key (for derived key material) session = sr.query_context_attributes(ctx, sr.SecPkgContextSessionKey) print("Session key (hex):", session.session_key.hex()) ``` -------------------------------- ### Sign and Verify Messages with SSPILIb Raw API Source: https://context7.com/jborean93/sspilib/llms.txt Illustrates low-level message signing and verification using `make_signature` and `verify_signature`. This process generates or checks a Message Integrity Code (MIC) without encrypting the data. Requires an established `CtxtHandle`. ```python import sspilib.raw as sr sizes = sr.query_context_attributes(ctx, sr.SecPkgContextSizes) data = bytearray(b"message to sign") mic = bytearray(sizes.max_signature) sign_msg = sr.SecBufferDesc([ sr.SecBuffer(data, sr.SecBufferType.SECBUFFER_DATA), sr.SecBuffer(mic, sr.SecBufferType.SECBUFFER_TOKEN), ]) sr.make_signature(context=ctx, qop=0, message=sign_msg, seq_no=0) signature = sign_msg[1].data # transmit alongside data # ---- Verification side ---- verify_msg = sr.SecBufferDesc([ sr.SecBuffer(bytearray(b"message to sign"), sr.SecBufferType.SECBUFFER_DATA), sr.SecBuffer(bytearray(signature), sr.SecBufferType.SECBUFFER_TOKEN), ]) try: qop = sr.verify_signature(context=ctx, message=verify_msg, seq_no=0) print("Signature valid, QoP:", qop) except sr.WindowsError as e: print("Signature verification failed:", e.winerror, e.strerror) ``` -------------------------------- ### Acquire Credentials Handle with Identity Source: https://context7.com/jborean93/sspilib/llms.txt Acquires credentials for outbound NTLM authentication using WinNTAuthIdentity. Ensure the package_list is correctly set for the desired protocol. ```python netlogon_id = sr.WinNTAuthIdentity( username="alice", domain="CORP", password="s3cr3t!", package_list="NTLM", # only allow NTLM via Negotiate ) cred = sr.acquire_credentials_handle( None, "Kerberos", sr.CredentialUse.SECPKG_CRED_OUTBOUND, auth_data=netlogon_id ) ``` -------------------------------- ### sspilib.KeytabCredential Source: https://context7.com/jborean93/sspilib/llms.txt Acquire a Kerberos keytab-backed credential. Suitable for service accounts on Windows where a .keytab was generated. Accepts a file path or raw keytab bytes. ```APIDOC ## sspilib.KeytabCredential ### Description Provides an SSPI credential sourced from a keytab file or raw keytab bytes, suitable for service accounts on Windows where a `.keytab` was generated by tools like `ktpass.exe` or MIT `ktutil`. Accepts a file path (`str` / `pathlib.Path`) or raw bytes/bytearray/memoryview. ### Usage ```python import pathlib import sspilib # From a keytab file path (service account) svc_cred = sspilib.KeytabCredential( username="http/webserver.corp.example.com@CORP.EXAMPLE.COM", keytab=pathlib.Path("/etc/krb5.keytab"), protocol="Kerberos", usage="accept", # server-side ) print(svc_cred) # http/webserver.corp.example.com@CORP.EXAMPLE.COM - Kerberos print(svc_cred.expiry) # datetime of credential expiry # From raw keytab bytes with open("/etc/krb5.keytab", "rb") as fh: keytab_data = fh.read() client_cred = sspilib.KeytabCredential( username="alice@CORP.EXAMPLE.COM", keytab=keytab_data, protocol="Kerberos", usage="initiate", # client-side ) ``` ``` -------------------------------- ### sspilib.UserCredential - Implicit Credentials Source: https://context7.com/jborean93/sspilib/llms.txt Acquire implicit credentials for the current user. This is Windows-only. The default protocol is Negotiate. ```python import sspilib # Current user's implicit credentials (Windows only) cred_implicit = sspilib.UserCredential() print(cred_implicit) # CurrentUser - Negotiate print(cred_implicit.protocol) # Negotiate print(cred_implicit.expiry) # datetime of credential expiry ``` -------------------------------- ### Encrypt and Decrypt Messages with SSPILIb Raw API Source: https://context7.com/jborean93/sspilib/llms.txt Demonstrates low-level message encryption and decryption using `encrypt_message` and `decrypt_message`. Requires an established `CtxtHandle` and uses `SecBufferDesc` to manage token, data, and padding buffers. ```python import sspilib.raw as sr # Assume ctx is a fully established CtxtHandle and sizes is a SecPkgContextSizes sizes = sr.query_context_attributes(ctx, sr.SecPkgContextSizes) plaintext = bytearray(b"confidential data") token_buf = bytearray(sizes.security_trailer) padding_buf = bytearray(sizes.block_size) messag e = sr.SecBufferDesc([ sr.SecBuffer(token_buf, sr.SecBufferType.SECBUFFER_TOKEN), sr.SecBuffer(plaintext, sr.SecBufferType.SECBUFFER_DATA), sr.SecBuffer(padding_buf, sr.SecBufferType.SECBUFFER_PADDING), ]) sr.encrypt_message(context=ctx, qop=0, message=message, seq_no=0) # Assemble encrypted blob: token || data || padding encrypted_blob = message[0].data + message[1].data + message[2].data print("Encrypted length:", len(encrypted_blob)) # ---- Decryption side ---- stream_buf = bytearray(encrypted_blob) decrypt_msg = sr.SecBufferDesc([ sr.SecBuffer(stream_buf, sr.SecBufferType.SECBUFFER_STREAM), sr.SecBuffer(None, sr.SecBufferType.SECBUFFER_DATA), ]) qop = sr.decrypt_message(context=ctx, message=decrypt_msg, seq_no=0) print("Decrypted:", decrypt_msg[1].data) print("QoP:", qop) ``` -------------------------------- ### sspilib.raw.SecBuffer / sspilib.raw.SecBufferDesc Source: https://context7.com/jborean93/sspilib/llms.txt Provides management for security buffers (`SecBuffer`) and their descriptors (`SecBufferDesc`), which are used for SSPI input/output. ```APIDOC ## `sspilib.raw.SecBuffer` / `sspilib.raw.SecBufferDesc` — Security buffer management `SecBuffer` holds a typed byte buffer used for SSPI input/output. `SecBufferDesc` groups an ordered array of `SecBuffer` instances into the descriptor structure required by all SSPI functions. Buffers are mutated in-place by SSPI calls; use `.data` to copy out results. ```python import sspilib.raw as sr # Pre-allocated buffer (caller manages memory) buf = bytearray(4096) token_buf = sr.SecBuffer(buf, sr.SecBufferType.SECBUFFER_TOKEN) print("Buffer type:", token_buf.buffer_type) # SECBUFFER_TOKEN print("Buffer len:", token_buf.count) # 4096 # SSPI-allocated buffer (ISC_REQ_ALLOCATE_MEMORY) alloc_buf = sr.SecBuffer(None, sr.SecBufferType.SECBUFFER_TOKEN) desc = sr.SecBufferDesc([token_buf, alloc_buf]) print("Number of buffers:", len(desc)) # 2 for b in desc: print(b.buffer_type, b.count) # Safely copy data after an SSPI call data_copy: bytes = desc[0].data # copies bytes out # Zero-copy view (only valid while buf bytearray is alive) view: memoryview = desc[0].dangerous_get_view() # Channel bindings buffer cb = sr.SecChannelBindings(application_data=b"tls-server-end-point:" + bytes(32)) cb_buf_copy = cb.get_sec_buffer_copy() # safe copy cb_buf_ref = cb.dangerous_get_sec_buffer() # zero-copy reference ``` ``` -------------------------------- ### ServerSecurityContext Source: https://context7.com/jborean93/sspilib/llms.txt Manages the server-side SSPI authentication handshake. Call `step()` in a loop with each client token until `ctx.complete` is `True`. Mirrors `ClientSecurityContext` for message operations. ```APIDOC ## sspilib.ServerSecurityContext ### Description Manages the server-side SSPI authentication handshake. Call `step()` in a loop with each client token until `ctx.complete` is `True`. Mirrors `ClientSecurityContext` for message operations. ### Method `step(token: bytes | None) -> bytes | None` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```python import socket import sspilib spn = f"host/{socket.gethostname()}" # Server credential — must use usage="accept" s_cred = sspilib.UserCredential(usage="accept") c_cred = sspilib.UserCredential(protocol="NTLM") c_ctx = sspilib.ClientSecurityContext(credential=c_cred, target_name=spn) s_ctx = sspilib.ServerSecurityContext(credential=s_cred) # Simulate in-process token exchange s_token = None while not (c_ctx.complete and not s_token): c_token = c_ctx.step(s_token) s_token = s_ctx.step(c_token) if c_token else None assert c_ctx.complete and s_ctx.complete # Bidirectional encrypted messaging client_msg = b"secret payload" wrapped = c_ctx.wrap(client_msg) unwrapped = s_ctx.unwrap(wrapped) assert unwrapped.data == client_msg server_reply = b"acknowledged" wrapped_reply = s_ctx.wrap(server_reply) final = c_ctx.unwrap(wrapped_reply) assert final.data == server_reply ``` ### Response #### Success Response (200) None #### Response Example None ``` -------------------------------- ### sspilib.UserCredential - Explicit UPN Credentials Source: https://context7.com/jborean93/sspilib/llms.txt Acquire explicit credentials using UPN format with Kerberos protocol. ```python # Explicit UPN-format credentials using Kerberos cred_upn = sspilib.UserCredential( "alice@CORP.EXAMPLE.COM", "s3cr3t!", protocol="Kerberos", ) ``` -------------------------------- ### sspilib.UserCredential Source: https://context7.com/jborean93/sspilib/llms.txt Acquire a username/password SSPI credential. Supports UPN and Netlogon formats, with options to specify protocol, usage, and protocol lists. ```APIDOC ## sspilib.UserCredential ### Description Represents an SSPI credential backed by a username and password. Supports UPN (`user@DOMAIN.COM`) and Netlogon (`DOMAIN\user`) formats. Defaults to the `Negotiate` protocol and `"initiate"` (client) usage. The `protocol_list` keyword argument can restrict or deny sub-protocols for the `Negotiate` provider (prefix with `!` to deny). ### Usage ```python import sspilib # Current user's implicit credentials (Windows only) cred_implicit = sspilib.UserCredential() print(cred_implicit) # CurrentUser - Negotiate print(cred_implicit.protocol) # Negotiate print(cred_implicit.expiry) # datetime of credential expiry # Explicit UPN-format credentials using Kerberos cred_upn = sspilib.UserCredential( "alice@CORP.EXAMPLE.COM", "s3cr3t!", protocol="Kerberos", ) # Explicit Netlogon-format credentials cred_netlogon = sspilib.UserCredential( username="alice", password="s3cr3t!", domain="CORP", protocol="Negotiate", usage="initiate", protocol_list=["!NTLM"], # forbid NTLM, force Kerberos ) # Server-side accept credential (for ServerSecurityContext) server_cred = sspilib.UserCredential(usage="accept") ``` ``` -------------------------------- ### sspilib.raw.query_context_attributes Source: https://context7.com/jborean93/sspilib/llms.txt Wraps QueryContextAttributes. This function accepts a type subclassing SecPkgContext and returns a populated instance. It is useful for retrieving sizing info, the authenticated username, the negotiated package name, or the session key after the context is fully established. ```APIDOC ## sspilib.raw.query_context_attributes ### Description Wraps `QueryContextAttributes`. Accepts a type subclassing `SecPkgContext` and returns a populated instance. Useful for retrieving sizing info, the authenticated username, the negotiated package name, or the session key after the context is fully established. ### Usage ```python import sspilib.raw as sr # ctx is a fully established CtxtHandle # Buffer sizes needed for wrap/sign operations sizes = sr.query_context_attributes(ctx, sr.SecPkgContextSizes) print("Max signature:", sizes.max_signature) print("Security trailer:", sizes.security_trailer) print("Block size:", sizes.block_size) # The authenticated username names = sr.query_context_attributes(ctx, sr.SecPkgContextNames) print("Authenticated as:", names.username) # The negotiated security package pkg_info = sr.query_context_attributes(ctx, sr.SecPkgContextPackageInfo) print("Package:", pkg_info.name, "version:", pkg_info.version) # Session key (for derived key material) session = sr.query_context_attributes(ctx, sr.SecPkgContextSessionKey) print("Session key (hex):", session.session_key.hex()) ``` ``` -------------------------------- ### sspilib.raw.set_credentials_attributes Source: https://context7.com/jborean93/sspilib/llms.txt Configures credential attributes, specifically supporting SecPkgCredKdcProxySettings to route Kerberos KDC traffic through an HTTPS proxy. ```APIDOC ## `sspilib.raw.set_credentials_attributes` — Configure credential attributes Wraps `SetCredentialsAttributesW`. Currently supports `SecPkgCredKdcProxySettings` to route Kerberos KDC traffic through an HTTPS proxy (KDC Proxy / KKDCP). ```python import sspilib.raw as sr cred = sr.acquire_credentials_handle( None, "Kerberos", sr.CredentialUse.SECPKG_CRED_OUTBOUND ).credential proxy = sr.SecPkgCredKdcProxySettings( flags=sr.KdcProxySettingsFlags.KDC_PROXY_SETTINGS_FLAGS_FORCEPROXY, proxy_server="kdc-proxy.corp.example.com:443:KdcProxy", ) sr.set_credentials_attributes(cred, proxy) print("KDC proxy configured:", proxy.proxy_server) # All Kerberos tickets acquired using this credential now route through the proxy ``` ``` -------------------------------- ### sspilib.raw.WinNTAuthIdentity Source: https://context7.com/jborean93/sspilib/llms.txt Username/password authentication identity. Holds explicit credentials passed as `auth_data` to `acquire_credentials_handle`. Supports UPN or Netlogon formats and an optional `package_list` to control which sub-protocols the `Negotiate` provider may use. ```APIDOC ## sspilib.raw.WinNTAuthIdentity ### Description Username/password authentication identity. Holds explicit credentials passed as `auth_data` to `acquire_credentials_handle`. Supports UPN or Netlogon formats and an optional `package_list` to control which sub-protocols the `Negotiate` provider may use. ### Method `__init__(username: str, password: str | None = None, domain: str | None = None, package_list: str | None = None)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```python import sspilib.raw as sr # UPN format — no domain argument upn_id = sr.WinNTAuthIdentity(username="alice@CORP.EXAMPLE.COM", password="s3cr3t!") print(upn_id.username) # alice@CORP.EXAMPLE.COM print(upn_id.domain) # None print(upn_id.package_list) # None ``` ### Response #### Success Response (200) None #### Response Example None ``` -------------------------------- ### sspilib.UserCredential - Explicit Netlogon Credentials Source: https://context7.com/jborean93/sspilib/llms.txt Acquire explicit credentials using Netlogon format. The protocol_list can be used to forbid sub-protocols like NTLM. ```python # Explicit Netlogon-format credentials cred_netlogon = sspilib.UserCredential( username="alice", password="s3cr3t!", domain="CORP", protocol="Negotiate", usage="initiate", protocol_list=["!NTLM"], # forbid NTLM, force Kerberos ) ``` -------------------------------- ### Acquire Credentials Handle for Identity-Only Kerberos Source: https://context7.com/jborean93/sspilib/llms.txt Acquires credentials for Kerberos S4U/constrained delegation using identity-only flags. This is useful when a password is not available or desired. ```python # Identity-only (for Kerberos S4U/constrained delegation) identity_only = sr.WinNTAuthIdentity( username="bob@CORP.EXAMPLE.COM", flags=sr.WinNTAuthFlags.SEC_WINNT_AUTH_IDENTITY_ONLY, ) cred = sr.acquire_credentials_handle( None, "Kerberos", sr.CredentialUse.SECPKG_CRED_OUTBOUND, auth_data=identity_only ) ``` -------------------------------- ### WinNTAuthIdentity for Explicit Credentials Source: https://context7.com/jborean93/sspilib/llms.txt Holds explicit credentials for AcquireCredentialsHandle, supporting UPN or Netlogon formats and an optional package_list to control Negotiate provider sub-protocols. ```python import sspilib.raw as sr # UPN format — no domain argument upn_id = sr.WinNTAuthIdentity(username="alice@CORP.EXAMPLE.COM", password="s3cr3t!") print(upn_id.username) # alice@CORP.EXAMPLE.COM print(upn_id.domain) # None print(upn_id.package_list) # None ``` -------------------------------- ### Channel Bindings for TLS Integration Source: https://context7.com/jborean93/sspilib/llms.txt Ties an SSPI security context to an outer TLS channel to prevent man-in-the-middle attacks. Pass constructed SecChannelBindings to ClientSecurityContext or ServerSecurityContext. ```python import hashlib import ssl import sspilib # Example: compute tls-server-end-point binding from server certificate DER bytes with open("server.crt", "rb") as f: cert_der = f.read() # Hash algorithm is based on the certificate signature (sha256 for most modern certs) digest = hashlib.sha256(cert_der).digest() app_data = b"tls-server-end-point:" + digest bindings = sspilib.SecChannelBindings(application_data=app_data) cred = sspilib.UserCredential("alice@CORP.EXAMPLE.COM", "s3cr3t!") ctx = sspilib.ClientSecurityContext( credential=cred, target_name="host/server.corp.example.com", channel_bindings=bindings, ) in_token = None while not ctx.complete: out_token = ctx.step(in_token) if not out_token: break in_token = ... # exchange with server ```