def get_cbt_data(response): """ Create Channel Binding for TLS data See: - https://tools.ietf.org/html/rfc5929 - https://github.com/jborean93/ntlm-auth#ntlmv2 - https://github.com/requests/requests-ntlm/pull/116#discussion_r325961121 - https://support.microsoft.com/en-za/help/976918/authentication-failure-from-non-windows-ntlm-or-kerberos-servers # noqa :param response: HTTP Response object """ cert_hash_bytes = get_server_cert(response) if not cert_hash_bytes: logger.debug("server cert not found, channel binding tokens (CBT) wont be used") return None channel_binding_type = b"tls-server-end-point" # https://tools.ietf.org/html/rfc5929#section-4 data_type = GssChannelBindingsStruct.APPLICATION_DATA cbt_data = GssChannelBindingsStruct() cbt_data[data_type] = b":".join([channel_binding_type, cert_hash_bytes]) logger.debug("cbt data: %s", cbt_data.get_data()) return cbt_data
def _get_channel_bindings_value(server_certificate_hash): """ Get's the MD5 hash of the gss_channel_bindings_struct to add to the AV_PAIR MSV_AV_CHANNEL_BINDINGS. This method takes in the SHA256 hash (Hash of the DER encoded certificate of the server we are connecting to) and add's it to the gss_channel_bindings_struct. It then gets the MD5 hash and converts this to a byte array in preparation of adding it to the AV_PAIR structure. :param server_certificate_hash: The SHA256 hash of the server certificate (DER encoded) NTLM is authenticated to :return channel_bindings: An MD5 hash of the gss_channel_bindings_struct to add to the AV_PAIR MsvChannelBindings """ # Channel Binding Tokens support, used for NTLMv2 # Decode the SHA256 certificate hash certificate_digest = base64.b16decode(server_certificate_hash) # Initialise the GssChannelBindingsStruct and add the # certificate_digest to the application_data field gss_channel_bindings = GssChannelBindingsStruct() gss_channel_bindings[gss_channel_bindings.APPLICATION_DATA] = \ b'tls-server-end-point:' + certificate_digest # Get the gss_channel_bindings_struct and create an MD5 hash channel_bindings_struct_data = gss_channel_bindings.get_data() channel_bindings = hashlib.md5(channel_bindings_struct_data).digest() return channel_bindings
def _get_channel_bindings_value(server_certificate_hash): """ https://msdn.microsoft.com/en-us/library/windows/desktop/dd919963%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 https://blogs.msdn.microsoft.com/openspecification/2013/03/26/ntlm-and-channel-binding-hash-aka-extended-protection-for-authentication/ Get's the MD5 hash of the gss_channel_bindings_struct to add to the AV_PAIR MSV_AV_CHANNEL_BINDINGS. This method takes in the SHA256 hash (Hash of the DER encoded certificate of the server we are connecting to) and add's it to the gss_channel_bindings_struct. It then gets the MD5 hash and converts this to a byte array in preparation of adding it to the AV_PAIR structure. :param server_certificate_hash: The SHA256 hash of the server certificate (DER encoded) NTLM is authenticated to :return channel_bindings: An MD5 hash of the gss_channel_bindings_struct to add to the AV_PAIR MsvChannelBindings """ # Channel Binding Tokens support, used for NTLMv2 # Decode the SHA256 certificate hash certificate_digest = base64.b16decode(server_certificate_hash) # Initialise the GssChannelBindingsStruct and add the certificate_digest to the application_data field gss_channel_bindings = GssChannelBindingsStruct() gss_channel_bindings[gss_channel_bindings.APPLICATION_DATA] = 'tls-server-end-point:'.encode() + certificate_digest # Get the gss_channel_bindings_struct and create an MD5 hash channel_bindings_struct_data = gss_channel_bindings.get_data() channel_bindings_hash = hashlib.md5(channel_bindings_struct_data).hexdigest() try: cbt_value = bytearray.fromhex(channel_bindings_hash) except TypeError: # Work-around for Python 2.6 bug cbt_value = bytearray.fromhex(unicode(channel_bindings_hash)) channel_bindings = bytes(cbt_value) return channel_bindings
def test_application_data(self): struct = GssChannelBindingsStruct() struct[GssChannelBindingsStruct.APPLICATION_DATA] = b"abc" expected = b"\x00\x00\x00\x00" \ b"\x00\x00\x00\x00" \ b"\x00\x00\x00\x00" \ b"\x00\x00\x00\x00" \ b"\x03\x00\x00\x00" \ b"\x61\x62\x63" actual = struct.get_data() assert actual == expected
def init_context(self): cbt_struct = None if self.cbt_app_data: cbt_struct = GssChannelBindingsStruct() cbt_struct[cbt_struct.APPLICATION_DATA] = self.cbt_app_data self._context = NtlmContext(self.username, self.password, self.domain, cbt_data=cbt_struct)
def test_full_data(self): struct = GssChannelBindingsStruct() struct[GssChannelBindingsStruct.INITIATOR_ADDTYPE] = 2 struct[GssChannelBindingsStruct.INITIATOR_ADDRESS] = b"abc" struct[GssChannelBindingsStruct.ACCEPTOR_ADDRTYPE] = 4 struct[GssChannelBindingsStruct.ACCEPTOR_ADDRESS] = b"def" struct[GssChannelBindingsStruct.APPLICATION_DATA] = b"ghi" expected = b"\x02\x00\x00\x00" \ b"\x03\x00\x00\x00" \ b"\x61\x62\x63" \ b"\x04\x00\x00\x00" \ b"\x03\x00\x00\x00" \ b"\x64\x65\x66" \ b"\x03\x00\x00\x00" \ b"\x67\x68\x69" actual = struct.get_data() assert actual == expected
def _get_channel_bindings_value(server_certificate_hash): """ https://msdn.microsoft.com/en-us/library/windows/desktop/dd919963%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 https://blogs.msdn.microsoft.com/openspecification/2013/03/26/ntlm-and-channel-binding-hash-aka-extended-protection-for-authentication/ Get's the MD5 hash of the gss_channel_bindings_struct to add to the AV_PAIR MSV_AV_CHANNEL_BINDINGS. This method takes in the SHA256 hash (Hash of the DER encoded certificate of the server we are connecting to) and add's it to the gss_channel_bindings_struct. It then gets the MD5 hash and converts this to a byte array in preparation of adding it to the AV_PAIR structure. :param server_certificate_hash: The SHA256 hash of the server certificate (DER encoded) NTLM is authenticated to :return channel_bindings: An MD5 hash of the gss_channel_bindings_struct to add to the AV_PAIR MsvChannelBindings """ # Channel Binding Tokens support, used for NTLMv2 # Decode the SHA256 certificate hash certificate_digest = base64.b16decode(server_certificate_hash) # Initialise the GssChannelBindingsStruct and add the certificate_digest to the application_data field gss_channel_bindings = GssChannelBindingsStruct() gss_channel_bindings[ gss_channel_bindings. APPLICATION_DATA] = 'tls-server-end-point:'.encode( ) + certificate_digest # Get the gss_channel_bindings_struct and create an MD5 hash channel_bindings_struct_data = gss_channel_bindings.get_data() channel_bindings_hash = hashlib.md5( channel_bindings_struct_data).hexdigest() try: cbt_value = bytearray.fromhex(channel_bindings_hash) except TypeError: # Work-around for Python 2.6 bug cbt_value = bytearray.fromhex(unicode(channel_bindings_hash)) channel_bindings = bytes(cbt_value) return channel_bindings
def retry_with_ntlm_auth(self, auth_header_field, auth_header, response, auth_type, args): try: cert_hash = self._get_server_cert(response) cbt_data = GssChannelBindingsStruct() cbt_data[cbt_data.APPLICATION_DATA] = b"tls-server-end-point:" + \ base64.b16decode(cert_hash) except Exception: cbt_data = None context = NtlmContext(self.username, self.password, self.domain, cbt_data=cbt_data, ntlm_compatibility=self.ntlm_compatibility) # Consume the original response contents and release the connection for # later response.content response.raw.release_conn() # Create the negotiate request msg1_req = response.request.copy() msg1 = context.step() msg1_header = "%s %s" % (auth_type, base64.b64encode(msg1).decode()) msg1_req.headers[auth_header] = msg1_header # Send the negotiate request and receive the challenge message disable_stream_args = dict(args, stream=False) msg2_resp = response.connection.send(msg1_req, **disable_stream_args) msg2_resp.content msg2_resp.raw.release_conn() # Parse the challenge response in the ntlm_context msg2_header = msg2_resp.headers[auth_header_field] msg2 = msg2_header.replace(auth_type + ' ', '') msg3 = context.step(base64.b64decode(msg2)) # Create the authenticate request msg3_req = msg2_resp.request.copy() msg3_header = auth_type + ' ' + base64.b64encode(msg3).decode() msg3_req.headers[auth_header] = msg3_header # Send the authenticate request final_response = msg2_resp.connection.send(msg3_req, **args) final_response.history.append(response) final_response.history.append(msg2_resp) return final_response
def _create_auth1_message_ntlm(self, domain, user, pw, certificate): domain = domain or None workstation = None cbt_data = None ntlm_compatibility = 1 if certificate is not None: ntlm_compatibility = 3 cbt_data = GssChannelBindingsStruct() cbt_data[cbt_data.APPLICATION_DATA] = \ b'tls-server-end-point:' + hashlib.sha256( certificate).digest() self._ctx = NtlmContext(user, pw, domain, workstation, cbt_data, ntlm_compatibility) payload = b64encode(self._ctx.step()).decode('ascii') self._method = METHOD_NTLM return Result(True, self._method, payload)
def get_nt_challenge_response(self, lm_challenge_response, server_certificate_hash=None, cbt_data=None): """ [MS-NLMP] v28.0 2016-07-14 3.3.1 - NTLM v1 Authentication 3.3.2 - NTLM v2 Authentication This method returns the NtChallengeResponse key based on the ntlm_compatibility chosen and the target_info supplied by the CHALLENGE_MESSAGE. It is quite different from what is set in the document as it combines the NTLMv1, NTLM2 and NTLMv2 methods into one and calls separate methods based on the ntlm_compatibility value chosen. :param lm_challenge_response: The LmChallengeResponse calculated beforehand, used to get the key_exchange_key value :param server_certificate_hash: This is deprecated and will be removed in a future version, use cbt_data instead :param cbt_data: The GssChannelBindingsStruct to bind in the NTLM response :return response: (NtChallengeResponse) - The NT response to the server challenge. Computed by the client :return session_base_key: (SessionBaseKey) - A session key calculated from the user password challenge :return target_info: (AV_PAIR) - The AV_PAIR structure used in the nt_challenge calculations """ if self._negotiate_flags & \ NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY and \ self._ntlm_compatibility < 3: # The compatibility level is less than 3 which means it doesn't # support NTLMv2 but we want extended security so use NTLM2 which # is different from NTLMv2 # [MS-NLMP] - 3.3.1 NTLMv1 Authentication response, session_base_key = \ self._get_NTLM2_response(self._password, self._server_challenge, self._client_challenge) lm_hash = comphash._lmowfv1(self._password) key_exchange_key = \ compkeys._get_exchange_key_ntlm_v1(self._negotiate_flags, session_base_key, self._server_challenge, lm_challenge_response, lm_hash) target_info = None elif 0 <= self._ntlm_compatibility < 3: response, session_base_key = \ self._get_NTLMv1_response(self._password, self._server_challenge) lm_hash = comphash._lmowfv1(self._password) key_exchange_key = \ compkeys._get_exchange_key_ntlm_v1(self._negotiate_flags, session_base_key, self._server_challenge, lm_challenge_response, lm_hash) target_info = None else: if self._server_target_info is None: target_info = ntlm_auth.messages.TargetInfo() else: target_info = self._server_target_info if target_info[AvId.MSV_AV_TIMESTAMP] is None: timestamp = get_windows_timestamp() else: timestamp = target_info[AvId.MSV_AV_TIMESTAMP] # [MS-NLMP] If the CHALLENGE_MESSAGE TargetInfo field has an # MsvAvTimestamp present, the client SHOULD provide a MIC target_info[AvId.MSV_AV_FLAGS] = \ struct.pack("<L", AvFlags.MIC_PROVIDED) if server_certificate_hash is not None and cbt_data is None: warnings.warn( "Manually creating the cbt stuct from the cert " "hash will be removed in a newer version of " "ntlm-auth. Send the actual CBT struct using " "cbt_data instead", DeprecationWarning) certificate_digest = base64.b16decode(server_certificate_hash) cbt_data = GssChannelBindingsStruct() cbt_data[cbt_data.APPLICATION_DATA] = \ b'tls-server-end-point:' + certificate_digest if cbt_data is not None: cbt_bytes = cbt_data.get_data() cbt_hash = hashlib.md5(cbt_bytes).digest() target_info[AvId.MSV_AV_CHANNEL_BINDINGS] = cbt_hash response, session_base_key = \ self._get_NTLMv2_response(self._user_name, self._password, self._domain_name, self._server_challenge, self._client_challenge, timestamp, target_info) key_exchange_key = \ compkeys._get_exchange_key_ntlm_v2(session_base_key) return response, key_exchange_key, target_info
def test_ntlm_context_with_mic(self, monkeypatch): monkeypatch.setattr('os.urandom', lambda s: b"\xaa" * 8) monkeypatch.setattr('ntlm_auth.messages.get_version', lambda s: b"\x05\x01\x28\x0A\x00\x00\x00\x0F") monkeypatch.setattr('ntlm_auth.messages.get_random_export_session_key', lambda: b"\x55" * 16) monkeypatch.setattr('ntlm_auth.compute_response.get_windows_timestamp', lambda: b"\x00" * 8) ch = 'E3CA49271E5089CC48CE82109F1324F41DBEDDC29A777410C738F7868C4FF405' cbt_data = GssChannelBindingsStruct() cbt_data[cbt_data.APPLICATION_DATA] = b"tls-server-end-point:" + \ base64.b16decode(ch) ntlm_context = NtlmContext("User", "Password", "Domain", "COMPUTER", cbt_data=cbt_data) ntlm_context.reset_rc4_state( ) # Verifies it won't fail when the session security isn't set up. actual_nego = ntlm_context.step() expected_nego = b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" \ b"\x01\x00\x00\x00\x31\xb0\x88\xe2" \ b"\x06\x00\x06\x00\x28\x00\x00\x00" \ b"\x08\x00\x08\x00\x2e\x00\x00\x00" \ b"\x05\x01\x28\x0a\x00\x00\x00\x0f" \ b"\x44\x6f\x6d\x61\x69\x6e\x43\x4f" \ b"\x4d\x50\x55\x54\x45\x52" assert actual_nego == expected_nego assert not ntlm_context.mic_present assert not ntlm_context.complete challenge_msg = b"\x4E\x54\x4C\x4D\x53\x53\x50\x00" \ b"\x02\x00\x00\x00\x00\x00\x00\x00" \ b"\x38\x00\x00\x00\x33\x82\x8A\xE2" \ b"\x01\x23\x45\x67\x89\xAB\xCD\xEF" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" \ b"\x30\x00\x30\x00\x38\x00\x00\x00" \ b"\x06\x01\xB1\x1D\x00\x00\x00\x0F" \ b"\x02\x00\x0C\x00\x44\x00\x6F\x00" \ b"\x6D\x00\x61\x00\x69\x00\x6E\x00" \ b"\x01\x00\x0C\x00\x53\x00\x65\x00" \ b"\x72\x00\x76\x00\x65\x00\x72\x00" \ b"\x07\x00\x08\x00\x00\x00\x00\x00" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" actual_auth = ntlm_context.step(challenge_msg) expected_auth = b'\x4E\x54\x4C\x4D\x53\x53\x50\x00' \ b'\x03\x00\x00\x00\x18\x00\x18\x00' \ b'\x7C\x00\x00\x00\x7C\x00\x7C\x00' \ b'\x94\x00\x00\x00\x0C\x00\x0C\x00' \ b'\x58\x00\x00\x00\x08\x00\x08\x00' \ b'\x64\x00\x00\x00\x10\x00\x10\x00' \ b'\x6C\x00\x00\x00\x10\x00\x10\x00' \ b'\x10\x01\x00\x00\x31\x82\x8A\xE2' \ b'\x05\x01\x28\x0A\x00\x00\x00\x0F' \ b'\xC4\x45\x2C\xF7\xA8\x1E\x4D\x11' \ b'\xD0\x78\x18\x94\x09\x57\x5D\x9E' \ b'\x44\x00\x6F\x00\x6D\x00\x61\x00' \ b'\x69\x00\x6E\x00\x55\x00\x73\x00' \ b'\x65\x00\x72\x00\x43\x00\x4F\x00' \ b'\x4D\x00\x50\x00\x55\x00\x54\x00' \ b'\x45\x00\x52\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\xA1\x3D\x03\x8A' \ b'\xD0\xCA\x02\x64\x33\x89\x7C\x33' \ b'\x5E\x0F\x56\xDF\x01\x01\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\xAA\xAA\xAA\xAA' \ b'\xAA\xAA\xAA\xAA\x00\x00\x00\x00' \ b'\x02\x00\x0C\x00\x44\x00\x6F\x00' \ b'\x6D\x00\x61\x00\x69\x00\x6E\x00' \ b'\x01\x00\x0C\x00\x53\x00\x65\x00' \ b'\x72\x00\x76\x00\x65\x00\x72\x00' \ b'\x07\x00\x08\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\x06\x00\x04\x00' \ b'\x02\x00\x00\x00\x0A\x00\x10\x00' \ b'\x6E\xA1\x9D\xF0\x66\xDA\x46\x22' \ b'\x05\x1F\x9C\x4F\x92\xC6\xDF\x74' \ b'\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x1D\x08\x89\xD1\xA5\xEE\xED\x21' \ b'\x91\x9E\x1A\xB8\x27\xC3\x0B\x17' assert actual_auth == expected_auth assert ntlm_context.complete assert ntlm_context.mic_present request_msg = b"test req" response_msg = b"test res" actual_wrapped = ntlm_context.wrap(request_msg) expected_wrapped = b"\x01\x00\x00\x00\xbc\xe3\x23\xa1" \ b"\x72\x06\x23\x78\x00\x00\x00\x00" \ b"\x70\x80\x1e\x11\xfe\x6b\x3a\xad" assert actual_wrapped == expected_wrapped server_sec = SessionSecurity( ntlm_context._session_security.negotiate_flags, ntlm_context._session_security.exported_session_key, "server") server_unwrap = server_sec.unwrap(actual_wrapped[16:], actual_wrapped[0:16]) assert server_unwrap == request_msg response_wrapped = server_sec.wrap(response_msg) actual_unwrap = ntlm_context.unwrap(response_wrapped[1] + response_wrapped[0]) assert actual_unwrap == response_msg msg = b"Hello" actual_sig1 = ntlm_context.sign(msg) expected_sig1 = b"\x01\x00\x00\x00\x08\xF0\x0D\x86\x34\x05\x1A\x1D\x01\x00\x00\x00" assert actual_sig1 == expected_sig1 server_sec.verify_signature(msg, actual_sig1) actual_sig2 = ntlm_context.sign(msg) expected_sig2 = b"\x01\x00\x00\x00\x07\x64\x0C\x30\x1C\xD7\x76\xF0\x02\x00\x00\x00" assert actual_sig2 == expected_sig2 server_sec.verify_signature(msg, actual_sig2) ntlm_context.reset_rc4_state() actual_sig3 = ntlm_context.sign(msg) expected_sig3 = b"\x01\x00\x00\x00\x1E\xD4\xA3\xE5\xE8\x05\x74\x01\x03\x00\x00\x00" assert actual_sig3 == expected_sig3 server_sec.reset_rc4_state(outgoing=False) server_sec.verify_signature(msg, actual_sig3) server_sig = server_sec.get_signature(msg) ntlm_context.verify(msg, server_sig)
def test_ntlm_context(self, monkeypatch): monkeypatch.setattr('os.urandom', lambda s: b"\xaa" * 8) monkeypatch.setattr('ntlm_auth.messages.get_version', lambda s: b"\x05\x01\x28\x0A\x00\x00\x00\x0F") monkeypatch.setattr('ntlm_auth.messages.get_random_export_session_key', lambda: b"\x55" * 16) monkeypatch.setattr('ntlm_auth.compute_response.get_windows_timestamp', lambda: b"\x00" * 8) ch = 'E3CA49271E5089CC48CE82109F1324F41DBEDDC29A777410C738F7868C4FF405' cbt_data = GssChannelBindingsStruct() cbt_data[cbt_data.APPLICATION_DATA] = b"tls-server-end-point:" + \ base64.b16decode(ch) ntlm_context = NtlmContext("User", "Password", "Domain", "COMPUTER", cbt_data=cbt_data) actual_nego = ntlm_context.step() expected_nego = b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" \ b"\x01\x00\x00\x00\x31\xb0\x88\xe2" \ b"\x06\x00\x06\x00\x28\x00\x00\x00" \ b"\x08\x00\x08\x00\x2e\x00\x00\x00" \ b"\x05\x01\x28\x0a\x00\x00\x00\x0f" \ b"\x44\x6f\x6d\x61\x69\x6e\x43\x4f" \ b"\x4d\x50\x55\x54\x45\x52" assert actual_nego == expected_nego assert not ntlm_context.mic_present assert not ntlm_context.complete challenge_msg = b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" \ b"\x02\x00\x00\x00\x2f\x82\x88\xe2" \ b"\x38\x00\x00\x00\x33\x82\x8a\xe2" \ b"\x01\x23\x45\x67\x89\xab\xcd\xef" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" \ b"\x24\x00\x24\x00\x44\x00\x00\x00" \ b"\x06\x00\x70\x17\x00\x00\x00\x0f" \ b"\x53\x00\x65\x00\x72\x00\x76\x00" \ b"\x65\x00\x72\x00\x02\x00\x0c\x00" \ b"\x44\x00\x6f\x00\x6d\x00\x61\x00" \ b"\x69\x00\x6e\x00\x01\x00\x0c\x00" \ b"\x53\x00\x65\x00\x72\x00\x76\x00" \ b"\x65\x00\x72\x00\x00\x00\x00\x00" actual_auth = ntlm_context.step(challenge_msg) expected_auth = b'\x4e\x54\x4c\x4d\x53\x53\x50\x00' \ b'\x03\x00\x00\x00\x18\x00\x18\x00' \ b'\x6c\x00\x00\x00\x68\x00\x68\x00' \ b'\x84\x00\x00\x00\x0c\x00\x0c\x00' \ b'\x48\x00\x00\x00\x08\x00\x08\x00' \ b'\x54\x00\x00\x00\x10\x00\x10\x00' \ b'\x5c\x00\x00\x00\x10\x00\x10\x00' \ b'\xec\x00\x00\x00\x31\x82\x8a\xe2' \ b'\x05\x01\x28\x0a\x00\x00\x00\x0f' \ b'\x44\x00\x6f\x00\x6d\x00\x61\x00' \ b'\x69\x00\x6e\x00\x55\x00\x73\x00' \ b'\x65\x00\x72\x00\x43\x00\x4f\x00' \ b'\x4d\x00\x50\x00\x55\x00\x54\x00' \ b'\x45\x00\x52\x00\x86\xc3\x50\x97' \ b'\xac\x9c\xec\x10\x25\x54\x76\x4a' \ b'\x57\xcc\xcc\x19\xaa\xaa\xaa\xaa' \ b'\xaa\xaa\xaa\xaa\x04\x10\xc4\x7a' \ b'\xcf\x19\x97\x89\xde\x7f\x20\x11' \ b'\x95\x7a\xea\x50\x01\x01\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\xaa\xaa\xaa\xaa' \ b'\xaa\xaa\xaa\xaa\x00\x00\x00\x00' \ b'\x02\x00\x0c\x00\x44\x00\x6f\x00' \ b'\x6d\x00\x61\x00\x69\x00\x6e\x00' \ b'\x01\x00\x0c\x00\x53\x00\x65\x00' \ b'\x72\x00\x76\x00\x65\x00\x72\x00' \ b'\x0a\x00\x10\x00\x6e\xa1\x9d\xf0' \ b'\x66\xda\x46\x22\x05\x1f\x9c\x4f' \ b'\x92\xc6\xdf\x74\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\xe5\x69\x95\x1d' \ b'\x15\xd4\x73\x5f\x49\xe1\x4c\xf9' \ b'\xa7\xd3\xe6\x72' assert actual_auth == expected_auth assert ntlm_context.complete assert not ntlm_context.mic_present request_msg = b"test req" response_msg = b"test res" actual_wrapped = ntlm_context.wrap(request_msg) expected_wrapped = b"\x01\x00\x00\x00\xbc\xe3\x23\xa1" \ b"\x72\x06\x23\x78\x00\x00\x00\x00" \ b"\x70\x80\x1e\x11\xfe\x6b\x3a\xad" assert actual_wrapped == expected_wrapped server_sec = SessionSecurity( ntlm_context._session_security.negotiate_flags, ntlm_context._session_security.exported_session_key, "server") server_unwrap = server_sec.unwrap(actual_wrapped[16:], actual_wrapped[0:16]) assert server_unwrap == request_msg response_wrapped = server_sec.wrap(response_msg) actual_unwrap = ntlm_context.unwrap(response_wrapped[1] + response_wrapped[0]) assert actual_unwrap == response_msg