Beispiel #1
0
 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)
Beispiel #2
0
class NTLMContext(AuthContext):
    _AUTH_PROVIDERS = {
        'ntlm': ''
    }

    def __init__(self, username, password, cbt_app_data):
        if username is None:
            raise ValueError("Cannot use ntlm-auth with no username set")
        if password is None:
            raise ValueError("Cannot use ntlm-auth with no password set")
        super(NTLMContext, self).__init__(password, "ntlm", cbt_app_data)
        self._domain, self._username = self._get_domain_username(username)

    @property
    def domain(self):
        return self._domain

    @property
    def username(self):
        return self._username

    @property
    def complete(self):
        return self._context.complete

    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 step(self):
        msg1 = self._context.step()
        log.debug("NTLM Negotiate message: %s" % binascii.hexlify(msg1))

        msg2 = yield msg1
        log.debug("NTLM: Parsing Challenge message and generating "
                  "Authenticate message: %s" % binascii.hexlify(msg2))
        msg3 = self._context.step(msg2)

        yield msg3

    def wrap(self, data):
        wrapped_data = self._context.wrap(data)
        return wrapped_data[:16], wrapped_data[16:]

    def unwrap(self, header, data):
        return self._context.unwrap(header + data)
Beispiel #3
0
    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
Beispiel #4
0
    def __init__(self, username, password):
        if username is None:
            raise SMBAuthenticationError("The username must be set when using "
                                         "NTLM authentication")
        if password is None:
            raise SMBAuthenticationError("The password must be set when using "
                                         "NTLM authentication")

        log.info("Setting up NTLM Security Context for user %s" % username)
        self.domain, self.username = _split_username_and_domain(username)
        self.context = Ntlm(self.username, password, domain=self.domain)
Beispiel #5
0
    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)
class NtlmContext(object):
    def __init__(self, username, password):
        if username is None:
            raise SMBAuthenticationError("The username must be set when using "
                                         "NTLM authentication")
        if password is None:
            raise SMBAuthenticationError("The password must be set when using "
                                         "NTLM authentication")

        log.info("Setting up NTLM Security Context for user %s" % username)
        self.domain, self.username = _split_username_and_domain(username)
        self.context = Ntlm(self.username, password, domain=self.domain)

    @property
    def complete(self):
        return self.context.complete

    def step(self):
        log.info("NTLM: Generating Negotiate message")
        msg1 = self.context.step()
        log.debug("NTLM: Negotiate message: %s" % _bytes_to_hex(msg1))
        msg2 = yield msg1

        log.info(
            "NTLM: Parsing Challenge message and generating Authentication message"
        )
        log.debug("NTLM: Challenge message: %s" % _bytes_to_hex(msg2))
        msg3 = self.context.step(input_token=msg2)

        yield msg3

    def get_session_key(self):
        # The session_key was only recently added in ntlm-auth, we have the
        # fallback to the non-public interface for older versions where we
        # know this still works. This should be removed once ntlm-auth no
        # longer requires these older versions (>=1.4.0).
        return getattr(self.context, 'session_key',
                       self.context._session_security.exported_session_key)
Beispiel #7
0
    def test_fail_wrap_no_context(self):
        ntlm_context = NtlmContext("", "")
        with pytest.raises(NoAuthContextError) as err:
            ntlm_context.wrap(b"")
        assert str(err.value) == \
            "Cannot wrap data as no security context has been established"

        with pytest.raises(NoAuthContextError) as err:
            ntlm_context.unwrap(b"")
        assert str(err.value) == \
            "Cannot unwrap data as no security context has been established"
Beispiel #8
0
class Authentication(object):
    __slots__ = ('logger', '_ctx', '_method', '_sspi', '_channel_bindings',
                 '_args', '_user', '_domain', '_password', '_kerberos')

    def __init__(self, logger):
        self.logger = logger
        self._ctx = None
        self._method = None
        self._sspi = False
        self._channel_bindings = None
        self._kerberos = KerberosBackend()

    def _create_auth1_message_sspi(self,
                                   url,
                                   method_data,
                                   user=None,
                                   password=None,
                                   domain=None,
                                   flags=0,
                                   certificate=None):

        if isinstance(method_data, Method):
            method = method_data.id
        else:
            method = method_data

        server = None
        principal = None

        kwargs = {
            'user': user,
            'password': password,
            'domain': domain,
            'gssflags': flags
        }

        parsed = urlparse(url)
        host = parsed.netloc
        scheme = parsed.scheme.upper()
        hostname = host.rsplit(':', 1)[0]

        if scheme.startswith('HTTP'):
            service = 'HTTP'
        else:
            service = scheme

        need_inquire_cred = False

        if (self._kerberos.has_sspi_nego_support
                or self._kerberos.has_gssapi_support) and method in (
                    METHOD_NEGOTIATE, METHOD_KERBEROS):

            if certificate:
                self.set_certificate(certificate)

            if method == METHOD_NEGOTIATE:
                mech_oid = self._kerberos.GSS_MECH_OID_SPNEGO
            else:
                mech_oid = self._kerberos.GSS_MECH_OID_KRB5

            flags |= \
                self._kerberos.GSS_C_MUTUAL_FLAG | \
                self._kerberos.GSS_C_SEQUENCE_FLAG

            kwargs.update({'mech_oid': mech_oid, 'gssflags': flags})

            server = service + '@' + hostname

            if self._kerberos.has_gssapi_support:
                del kwargs['user']
                del kwargs['domain']

                if user and domain and '@' not in user:
                    principal = user + '@' + domain
                else:
                    principal = user

                if not kwargs['password']:
                    need_inquire_cred = True
                    del kwargs['password']

        elif self._kerberos.has_sspi_ntlm_support and method == METHOD_NTLM:
            # This is only for SSPI
            kwargs['mech_oid'] = self._kerberos.GSS_MECH_OID_NTLM
            server = hostname

        else:
            raise NotImplementedError('No supported auth methods')

        result = 0

        try:
            result, self._ctx = self._kerberos.authGSSClientInit(
                server, principal, **kwargs)

        except TypeError as e:
            self.logger.error(
                'GSSAPI: authGSSClientInit: failed to set password: %s', e)

            if password:
                raise NotImplementedError(
                    "ccs-pykerberos doesn't support password auth")

            del kwargs['password']
            result, self._ctx = self._kerberos.authGSSClientInit(
                server, principal, **kwargs)

        if result < 0:
            self.logger.error('GSSAPI: authGSSClientInit Result: %d', result)
            raise AuthenticationError(result)

        self.logger.debug(
            'GSSAPI: New context for SPN=%s%s (need inquire creds: %s)',
            server, '' if not principal else 'Principal=' + principal,
            need_inquire_cred)

        if need_inquire_cred:
            result = self._kerberos.authGSSClientInquireCred(self._ctx)
            if result < 0:
                raise AuthenticationError(result)

            self.logger.warning(
                'GSSAPI: Using principal: %s',
                self._kerberos.authGSSClientUserName(self._ctx),
            )

        kwargs = {}
        if self._channel_bindings:
            kwargs['channel_bindings'] = self._channel_bindings

        result = self._kerberos.authGSSClientStep(self._ctx, '', **kwargs)

        if result < 0:
            self.logger.error('GSSAPI: authGSSClientInit Result: %d', result)
            raise AuthenticationError(result)

        self._method = method
        self._sspi = True

        return Result(result == self._kerberos.CONTINUE, self._method,
                      self._kerberos.authGSSClientResponse(self._ctx))

    def _create_auth2_message_sspi(self, payload):
        kwargs = {}
        if self._channel_bindings:
            kwargs['channel_bindings'] = self._channel_bindings

        result = self._kerberos.authGSSClientStep(self._ctx, payload, **kwargs)

        payload = self._kerberos.authGSSClientResponse(self._ctx)

        if result < 0:
            self.logger.error('GSSAPI: authGSSClientStep (2) Step Result: %d',
                              result)
            raise AuthenticationError(result)
        elif result == self._kerberos.COMPLETE:
            if payload is not None:
                if self._method != METHOD_NTLM:
                    # Known bug for NTLM and winkerberos
                    self.logger.warning(
                        'GSSAPI: API bug: payload provided but step is not required'
                    )
                result = self._kerberos.CONTINUE
            else:
                self.logger.info(
                    'GSSAPI: authGSSClientStep (2) not required for %s',
                    self._method)

        return Result(result == self._kerberos.CONTINUE, self._method, payload)

    def set_certificate(self, certificate):
        # FIXME: Handle case with different hash function somehow..

        application_data = b'tls-server-end-point:' + hashlib.sha256(
            certificate).digest()

        self._channel_bindings = self._kerberos.channelBindings(
            application_data=application_data)

        self.logger.debug('Set channel bindings: %s -> %s',
                          repr(application_data), self._channel_bindings)

    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 _create_auth2_message_ntlm(self, payload):

        payload = b64encode(self._ctx.step(b64decode(payload))).decode('ascii')

        return Result(False, self._method, payload)

    def _create_auth1_message_basic(self, user, pw):
        response = b64encode((user + ':' + pw).encode('utf-8')).decode('ascii')

        self._method = METHOD_BASIC

        return Result(False, self._method, response)

    def _create_auth1_message_digest(self, domain, user, pw, args):
        response = make_digest_response(domain, user, pw, args)

        self._method = METHOD_DIGEST

        return Result(False, self._method, response)

    def create_auth1_message(self,
                             domain,
                             user,
                             pw,
                             url,
                             payloads,
                             certificate=None,
                             secure=True):

        if __debug__:
            assert (not domain or isinstance(domain, string_types))
            assert (not user or isinstance(user, string_types))
            assert (not pw or isinstance(pw, string_types))
            assert (not url or isinstance(url, string_types))
            assert (not certificate or isinstance(certificate, bytes))

        supported_auth_methods, _ = get_supported_methods(payloads, secure)
        for method in supported_auth_methods:
            if method.id in METHODS_KERBEROS:
                try:
                    return self._create_auth1_message_sspi(
                        url, method, user, pw, domain, certificate=certificate)

                except self._kerberos.GSSError as e:
                    self.logger.info('GSS error: method=%s error=%s (ignore)',
                                     method, e)

                except NotImplementedError:
                    self.logger.debug('Not supported conditions for method %s',
                                      method)

                except AuthenticationError as e:
                    self.logger.info('SSPI error: method=%s error=%s (ignore)',
                                     method, e)

                self._ctx = None

            elif user is None:
                continue

            elif method.id == METHOD_NTLM:
                self.logger.info(
                    'Fallback to py NTLM with creds: %s -> user=%s', url, user)
                return self._create_auth1_message_ntlm(domain, user, pw,
                                                       certificate)

            elif method.id == METHOD_DIGEST:
                self.logger.info(
                    'Fallback to py Digest with creds: %s -> user=%s', url,
                    user)
                return self._create_auth1_message_digest(
                    domain, user, pw, method.args)

            elif method.id == METHOD_BASIC:
                self.logger.info(
                    'Fallback to py Basic with creds: %s -> user=%s', url,
                    user)
                return self._create_auth1_message_basic(user, pw)

        if user is None:
            self.logger.info('No credentials found for URL=%s', url)

        raise AuthenticationError('No acceptable auth found for: URL: %s', url)

    def create_auth2_message(self, payload):
        if self._sspi:
            return self._create_auth2_message_sspi(payload)
        elif self._method == METHOD_NTLM:
            return self._create_auth2_message_ntlm(payload)
        else:
            raise AuthenticationError(
                'Invalid state (expected method {})'.format(self._method))
Beispiel #9
0
    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)
Beispiel #10
0
    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