Hello community, here is the log from the commit of package python-ntlm-auth for openSUSE:Factory checked in at 2020-07-16 12:15:24 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-ntlm-auth (Old) and /work/SRC/openSUSE:Factory/.python-ntlm-auth.new.3592 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-ntlm-auth" Thu Jul 16 12:15:24 2020 rev:7 rq:821007 version:1.5.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-ntlm-auth/python-ntlm-auth.changes 2019-08-27 10:27:54.179912117 +0200 +++ /work/SRC/openSUSE:Factory/.python-ntlm-auth.new.3592/python-ntlm-auth.changes 2020-07-16 12:16:40.494856867 +0200 @@ -1,0 +2,14 @@ +Wed Jul 15 05:51:54 UTC 2020 - Steve Kowalik <steven.kowa...@suse.com> + +- Update to 1.5.0: + * Added the `mic_present` property to the `NtlmContext` class to determine + if a MIC has been added to the authentication message. + * Added the `sign` and `verify` function to the `NtlmContext` to sign data + and verify signatures. + * Added the `reset_rc4_state` function to the `NtlmContext` to allow a + caller to reset the incoming and outgoing RC4 cipher. + * Added the `NTLMSSP_NEGOTIATE_UNICODE` flag to the negotiate message to + ensure the challenge and authentication message's text fields can be + unicode encoded + +------------------------------------------------------------------- Old: ---- python-ntlm-auth-1.4.0.tar.gz New: ---- python-ntlm-auth-1.5.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-ntlm-auth.spec ++++++ --- /var/tmp/diff_new_pack.WB84Ot/_old 2020-07-16 12:16:41.718858105 +0200 +++ /var/tmp/diff_new_pack.WB84Ot/_new 2020-07-16 12:16:41.722858108 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-ntlm-auth # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,11 +18,10 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-ntlm-auth -Version: 1.4.0 +Version: 1.5.0 Release: 0 Summary: NTLM low-level Python library License: MIT -Group: Development/Languages/Python URL: https://github.com/jborean93/ntlm-auth Source: https://github.com/jborean93/ntlm-auth/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz BuildRequires: %{python_module cryptography} ++++++ python-ntlm-auth-1.4.0.tar.gz -> python-ntlm-auth-1.5.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ntlm-auth-1.4.0/.travis.yml new/ntlm-auth-1.5.0/.travis.yml --- old/ntlm-auth-1.4.0/.travis.yml 2019-08-19 02:51:07.000000000 +0200 +++ new/ntlm-auth-1.5.0/.travis.yml 2020-06-16 08:07:58.000000000 +0200 @@ -1,6 +1,6 @@ language: python -dist: xenial +dist: bionic matrix: include: @@ -13,12 +13,14 @@ dist: trusty - python: 3.6 - python: 3.7 - - python: 3.8-dev + - python: 3.8 - # 3.8 is still an alpha and this is just running to make sure nothing major + - python: 3.9-dev + + # 3.9 is still an alpha and this is just running to make sure nothing major # breaks but we don't want it to stop the build allow_failures: - - python: 3.8-dev + - python: 3.9-dev install: - pip install --upgrade pip setuptools diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ntlm-auth-1.4.0/CHANGES.md new/ntlm-auth-1.5.0/CHANGES.md --- old/ntlm-auth-1.4.0/CHANGES.md 2019-08-19 02:51:07.000000000 +0200 +++ new/ntlm-auth-1.5.0/CHANGES.md 2020-06-16 08:07:58.000000000 +0200 @@ -1,5 +1,12 @@ # Changes +## 1.5.0 (Jun 16, 2020) + +* Added the `mic_present` property to the `NtlmContext` class to determine if a MIC has been added to the authentication message. +* Added the `sign` and `verify` function to the `NtlmContext` to sign data and verify signatures. +* Added the `reset_rc4_state` function to the `NtlmContext` to allow a caller to reset the incoming and outgoing RC4 cipher. +* Added the `NTLMSSP_NEGOTIATE_UNICODE` flag to the negotiate message to ensure the challenge and authentication message's text fields can be unicode encoded + ## 1.4.0 (Aug 19, 2019) * Added the `session_key` attribute to the `NtlmContext` class so the session key can be accessed in downstream libraries diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ntlm-auth-1.4.0/appveyor.yml new/ntlm-auth-1.5.0/appveyor.yml --- old/ntlm-auth-1.4.0/appveyor.yml 2019-08-19 02:51:07.000000000 +0200 +++ new/ntlm-auth-1.5.0/appveyor.yml 2020-06-16 08:07:58.000000000 +0200 @@ -13,14 +13,14 @@ # https://www.appveyor.com/docs/installed-software/#python - PYTHON: Python27 - PYTHON: Python27-x64 - - PYTHON: Python34 - - PYTHON: Python34-x64 - PYTHON: Python35 - PYTHON: Python35-x64 - PYTHON: Python36 - PYTHON: Python36-x64 - PYTHON: Python37 - PYTHON: Python37-x64 + - PYTHON: Python38 + - PYTHON: Python38-x64 services: - iis diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ntlm-auth-1.4.0/ntlm_auth/messages.py new/ntlm-auth-1.5.0/ntlm_auth/messages.py --- old/ntlm-auth-1.4.0/ntlm_auth/messages.py 2019-08-19 02:51:07.000000000 +0200 +++ new/ntlm-auth-1.5.0/ntlm_auth/messages.py 2020-06-16 08:07:58.000000000 +0200 @@ -110,10 +110,11 @@ negotiate_flags |= \ NegotiateFlags.NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED - # Set the encoding flag to use OEM, remove UNICODE if set as it isn't - # support in this message - negotiate_flags -= NegotiateFlags.NTLMSSP_NEGOTIATE_UNICODE - negotiate_flags |= NegotiateFlags.NTLMSSP_NEGOTIATE_OEM + # Set the encoding flag to use UNICODE, remove OEM if set. + negotiate_flags |= NegotiateFlags.NTLMSSP_NEGOTIATE_UNICODE + negotiate_flags &= ~NegotiateFlags.NTLMSSP_NEGOTIATE_OEM + + # The domain name and workstation are always OEM encoded. self.domain_name = self.domain_name.encode('ascii') self.workstation = self.workstation.encode('ascii') @@ -330,7 +331,7 @@ self.workstation = workstation if self.negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_UNICODE: - self.negotiate_flags -= NegotiateFlags.NTLMSSP_NEGOTIATE_OEM + self.negotiate_flags &= ~NegotiateFlags.NTLMSSP_NEGOTIATE_OEM encoding_value = 'utf-16-le' else: encoding_value = 'ascii' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ntlm-auth-1.4.0/ntlm_auth/ntlm.py new/ntlm-auth-1.5.0/ntlm_auth/ntlm.py --- old/ntlm-auth-1.4.0/ntlm_auth/ntlm.py 2019-08-19 02:51:07.000000000 +0200 +++ new/ntlm-auth-1.5.0/ntlm_auth/ntlm.py 2020-06-16 08:07:58.000000000 +0200 @@ -49,7 +49,6 @@ self._server_certificate_hash = None # deprecated for backwards compat self.ntlm_compatibility = ntlm_compatibility self.complete = False - self.session_key = None # Setting up our flags so the challenge message returns the target info # block if supported @@ -71,6 +70,23 @@ self._authenticate_message = None self._session_security = None + @property + def mic_present(self): + if self._authenticate_message: + return bool(self._authenticate_message.mic) + + return False + + @property + def session_key(self): + if self._authenticate_message: + return self._authenticate_message.exported_session_key + + def reset_rc4_state(self, outgoing=True): + """ Resets the signing cipher for the incoming or outgoing cipher. For SPNEGO for calculating mechListMIC. """ + if self._session_security: + self._session_security.reset_rc4_state(outgoing=outgoing) + def step(self, input_token=None): if self._negotiate_message is None: self._negotiate_message = NegotiateMessage(self.negotiate_flags, @@ -92,14 +108,19 @@ flags = struct.unpack("<I", flag_bytes)[0] if flags & NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL or \ flags & NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN: - self.session_key = self._authenticate_message.exported_session_key self._session_security = SessionSecurity( - flags, self._authenticate_message.exported_session_key + flags, self.session_key ) self.complete = True return self._authenticate_message.get_data() + def sign(self, data): + return self._session_security.get_signature(data) + + def verify(self, data, signature): + self._session_security.verify_signature(data, signature) + def wrap(self, data): if self._session_security is None: raise NoAuthContextError("Cannot wrap data as no security context " diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ntlm-auth-1.4.0/ntlm_auth/session_security.py new/ntlm-auth-1.5.0/ntlm_auth/session_security.py --- old/ntlm-auth-1.4.0/ntlm_auth/session_security.py 2019-08-19 02:51:07.000000000 +0200 +++ new/ntlm-auth-1.5.0/ntlm_auth/session_security.py 2020-06-16 08:07:58.000000000 +0200 @@ -102,36 +102,35 @@ self.exported_session_key = exported_session_key self.outgoing_seq_num = 0 self.incoming_seq_num = 0 - - client_sealing_key = \ - compkeys.get_seal_key(self.negotiate_flags, exported_session_key, - SignSealConstants.CLIENT_SEALING) - server_sealing_key = \ - compkeys.get_seal_key(self.negotiate_flags, exported_session_key, - SignSealConstants.SERVER_SEALING) + self._source = source + self._client_sealing_key = compkeys.get_seal_key(self.negotiate_flags, exported_session_key, + SignSealConstants.CLIENT_SEALING) + self._server_sealing_key = compkeys.get_seal_key(self.negotiate_flags, exported_session_key, + SignSealConstants.SERVER_SEALING) + + self.outgoing_handle = None + self.incoming_handle = None + self.reset_rc4_state(True) + self.reset_rc4_state(False) if source == "client": - self.outgoing_signing_key = \ - compkeys.get_sign_key(exported_session_key, - SignSealConstants.CLIENT_SIGNING) - self.incoming_signing_key = \ - compkeys.get_sign_key(exported_session_key, - SignSealConstants.SERVER_SIGNING) - self.outgoing_handle = ARC4(client_sealing_key) - self.incoming_handle = ARC4(server_sealing_key) + self.outgoing_signing_key = compkeys.get_sign_key(exported_session_key, SignSealConstants.CLIENT_SIGNING) + self.incoming_signing_key = compkeys.get_sign_key(exported_session_key, SignSealConstants.SERVER_SIGNING) elif source == "server": - self.outgoing_signing_key = \ - compkeys.get_sign_key(exported_session_key, - SignSealConstants.SERVER_SIGNING) - self.incoming_signing_key = \ - compkeys.get_sign_key(exported_session_key, - SignSealConstants.CLIENT_SIGNING) - self.outgoing_handle = ARC4(server_sealing_key) - self.incoming_handle = ARC4(client_sealing_key) + self.outgoing_signing_key = compkeys.get_sign_key(exported_session_key, SignSealConstants.SERVER_SIGNING) + self.incoming_signing_key = compkeys.get_sign_key(exported_session_key, SignSealConstants.CLIENT_SIGNING) else: raise ValueError("Invalid source parameter %s, must be client " "or server" % source) + def reset_rc4_state(self, outgoing=True): + csk = self._client_sealing_key + ssk = self._server_sealing_key + if outgoing: + self.outgoing_handle = ARC4(csk if self._source == 'client' else ssk) + else: + self.incoming_handle = ARC4(ssk if self._source == 'client' else csk) + def wrap(self, message): """ [MS-NLMP] v28.0 2016-07-14 @@ -147,11 +146,11 @@ """ if self.negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL: encrypted_message = self._seal_message(message) - signature = self._get_signature(message) + signature = self.get_signature(message) message = encrypted_message elif self.negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN: - signature = self._get_signature(message) + signature = self.get_signature(message) else: signature = None @@ -172,10 +171,10 @@ """ if self.negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL: message = self._unseal_message(message) - self._verify_signature(message, signature) + self.verify_signature(message, signature) elif self.negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN: - self._verify_signature(message, signature) + self.verify_signature(message, signature) return message @@ -207,7 +206,7 @@ decrypted_message = self.incoming_handle.update(message) return decrypted_message - def _get_signature(self, message): + def get_signature(self, message): """ [MS-NLMP] v28.0 2016-07-14 @@ -227,7 +226,7 @@ return signature.get_data() - def _verify_signature(self, message, signature): + def verify_signature(self, message, signature): """ Will verify that the signature received from the server matches up with the expected signature computed locally. Will throw an exception if diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ntlm-auth-1.4.0/setup.py new/ntlm-auth-1.5.0/setup.py --- old/ntlm-auth-1.4.0/setup.py 2019-08-19 02:51:07.000000000 +0200 +++ new/ntlm-auth-1.5.0/setup.py 2020-06-16 08:07:58.000000000 +0200 @@ -13,7 +13,7 @@ setup( name='ntlm-auth', - version='1.4.0', + version='1.5.0', packages=['ntlm_auth'], install_requires=[], extras_require={ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ntlm-auth-1.4.0/tests/test_messages.py new/ntlm-auth-1.5.0/tests/test_messages.py --- old/ntlm-auth-1.4.0/tests/test_messages.py 2019-08-19 02:51:07.000000000 +0200 +++ new/ntlm-auth-1.5.0/tests/test_messages.py 2020-06-16 08:07:58.000000000 +0200 @@ -71,7 +71,7 @@ def test_negotiate_with_all(self): expected = b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" \ - b"\x01\x00\x00\x00\x32\xb2\x02\xe2" \ + b"\x01\x00\x00\x00\x31\xb2\x02\xe2" \ b"\x06\x00\x06\x00\x28\x00\x00\x00" \ b"\x08\x00\x08\x00\x2e\x00\x00\x00" \ b"\x06\x01\xb1\x1d\x00\x00\x00\x0f" \ @@ -83,7 +83,7 @@ def test_negotiate_without_version(self): test_flags = 3791815219 - NegotiateFlags.NTLMSSP_NEGOTIATE_VERSION expected = b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" \ - b"\x01\x00\x00\x00\x32\xb2\x02\xe0" \ + b"\x01\x00\x00\x00\x31\xb2\x02\xe0" \ b"\x06\x00\x06\x00\x28\x00\x00\x00" \ b"\x08\x00\x08\x00\x2e\x00\x00\x00" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" \ @@ -94,7 +94,7 @@ def test_negotiate_without_domain_workstation(self): expected = b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" \ - b"\x01\x00\x00\x00\x32\x82\x02\xe2" \ + b"\x01\x00\x00\x00\x31\x82\x02\xe2" \ b"\x00\x00\x00\x00\x28\x00\x00\x00" \ b"\x00\x00\x00\x00\x28\x00\x00\x00" \ b"\x06\x01\xb1\x1d\x00\x00\x00\x0f" @@ -580,42 +580,42 @@ "COMPUTER") # Not a Microsoft example, using pre-computed value - expected = b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" \ + expected = 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"\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"\x06\x01\xb1\x1d\x00\x00\x00\x0f" \ - b"\x8b\x69\xf5\x92\xb2\xd7\x8f\xd7" \ - b"\x3a\x3a\x49\xdb\xfe\x19\x61\xbc" \ - 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"\x6C\x00\x00\x00\x10\x00\x10\x00" \ + b"\x10\x01\x00\x00\x31\x82\x8A\xE2" \ + b"\x06\x01\xB1\x1D\x00\x00\x00\x0F" \ + b"\xD2\xA1\x45\xDE\xA4\x25\x3E\x19" \ + b"\x10\xFE\x0F\x5B\x7A\x0D\x2A\x90" \ + 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\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"\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"\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" + b"\x1D\x08\x89\xD1\xA5\xEE\xED\x21" \ + b"\x91\x9E\x1A\xB8\x27\xC3\x0B\x17" actual = AuthenticateMessage("User", "Password", "Domain", "COMPUTER", test_challenge_message, 3, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ntlm-auth-1.4.0/tests/test_ntlm.py new/ntlm-auth-1.5.0/tests/test_ntlm.py --- old/ntlm-auth-1.4.0/tests/test_ntlm.py 2019-08-19 02:51:07.000000000 +0200 +++ new/ntlm-auth-1.5.0/tests/test_ntlm.py 2020-06-16 08:07:58.000000000 +0200 @@ -126,8 +126,7 @@ # permutations are in test_message.py def test_create_negotiate_message(self): ntlm_context = Ntlm() - expected = b'TlRMTVNTUAABAAAAMrCI4gYABgAoAAAACAAIAC4AAAA' \ - b'GAbEdAAAAD0RvbWFpbkNPTVBVVEVS' + expected = b'TlRMTVNTUAABAAAAMbCI4gYABgAoAAAACAAIAC4AAAAGAbEdAAAAD0RvbWFpbkNPTVBVVEVS' actual = ntlm_context.create_negotiate_message("Domain", "COMPUTER") assert actual == expected @@ -312,8 +311,6 @@ monkeypatch.setattr('ntlm_auth.compute_response.get_windows_timestamp', lambda: b"\x00" * 8) - import binascii - ch = 'E3CA49271E5089CC48CE82109F1324F41DBEDDC29A777410C738F7868C4FF405' cbt_data = GssChannelBindingsStruct() cbt_data[cbt_data.APPLICATION_DATA] = b"tls-server-end-point:" + \ @@ -322,13 +319,14 @@ 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\x32\xb0\x88\xe2" \ + 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" \ @@ -380,6 +378,111 @@ 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 + + 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" @@ -404,6 +507,28 @@ ) 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_fail_wrap_no_context(self): ntlm_context = NtlmContext("", "") with pytest.raises(NoAuthContextError) as err: