def test_saslprep(self):
        try:
            import stringprep
        except ImportError:
            self.assertRaises(TypeError, saslprep, u"anything...")
            # Bytes strings are ignored.
            self.assertEqual(saslprep(b"user"), b"user")
        else:
            # Examples from RFC4013, Section 3.
            self.assertEqual(saslprep(u"I\u00ADX"), u"IX")
            self.assertEqual(saslprep(u"user"), u"user")
            self.assertEqual(saslprep(u"USER"), u"USER")
            self.assertEqual(saslprep(u"\u00AA"), u"a")
            self.assertEqual(saslprep(u"\u2168"), u"IX")
            self.assertRaises(ValueError, saslprep, u"\u0007")
            self.assertRaises(ValueError, saslprep, u"\u0627\u0031")

            # Bytes strings are ignored.
            self.assertEqual(saslprep(b"user"), b"user")
예제 #2
0
    def test_saslprep(self):
        try:
            import stringprep
        except ImportError:
            self.assertRaises(TypeError, saslprep, u"anything...")
            # Bytes strings are ignored.
            self.assertEqual(saslprep(b"user"), b"user")
        else:
            # Examples from RFC4013, Section 3.
            self.assertEqual(saslprep(u"I\u00ADX"), u"IX")
            self.assertEqual(saslprep(u"user"), u"user")
            self.assertEqual(saslprep(u"USER"), u"USER")
            self.assertEqual(saslprep(u"\u00AA"), u"a")
            self.assertEqual(saslprep(u"\u2168"), u"IX")
            self.assertRaises(ValueError, saslprep, u"\u0007")
            self.assertRaises(ValueError, saslprep, u"\u0627\u0031")

            # Bytes strings are ignored.
            self.assertEqual(saslprep(b"user"), b"user")
예제 #3
0
def _authenticate_scram(credentials, sock_info, mechanism):
    """Authenticate using SCRAM."""

    username = credentials.username
    if mechanism == 'SCRAM-SHA-256':
        digest = "sha256"
        digestmod = hashlib.sha256
        data = saslprep(credentials.password).encode("utf-8")
    else:
        digest = "sha1"
        digestmod = hashlib.sha1
        data = _password_digest(username, credentials.password).encode("utf-8")
    source = credentials.source
    cache = credentials.cache

    # Make local
    _hmac = hmac.HMAC

    user = username.encode("utf-8").replace(b"=", b"=3D").replace(b",", b"=2C")
    nonce = standard_b64encode(os.urandom(32))
    first_bare = b"n=" + user + b",r=" + nonce

    cmd = SON([('saslStart', 1),
               ('mechanism', mechanism),
               ('payload', Binary(b"n,," + first_bare)),
               ('autoAuthorize', 1)])
    res = sock_info.command(source, cmd)

    server_first = res['payload']
    parsed = _parse_scram_response(server_first)
    iterations = int(parsed[b'i'])
    if iterations < 4096:
        raise OperationFailure("Server returned an invalid iteration count.")
    salt = parsed[b's']
    rnonce = parsed[b'r']
    if not rnonce.startswith(nonce):
        raise OperationFailure("Server returned an invalid nonce.")

    without_proof = b"c=biws,r=" + rnonce
    if cache.data:
        client_key, server_key, csalt, citerations = cache.data
    else:
        client_key, server_key, csalt, citerations = None, None, None, None

    # Salt and / or iterations could change for a number of different
    # reasons. Either changing invalidates the cache.
    if not client_key or salt != csalt or iterations != citerations:
        salted_pass = _hi(
            digest, data, standard_b64decode(salt), iterations)
        client_key = _hmac(salted_pass, b"Client Key", digestmod).digest()
        server_key = _hmac(salted_pass, b"Server Key", digestmod).digest()
        cache.data = (client_key, server_key, salt, iterations)
    stored_key = digestmod(client_key).digest()
    auth_msg = b",".join((first_bare, server_first, without_proof))
    client_sig = _hmac(stored_key, auth_msg, digestmod).digest()
    client_proof = b"p=" + standard_b64encode(_xor(client_key, client_sig))
    client_final = b",".join((without_proof, client_proof))

    server_sig = standard_b64encode(
        _hmac(server_key, auth_msg, digestmod).digest())

    cmd = SON([('saslContinue', 1),
               ('conversationId', res['conversationId']),
               ('payload', Binary(client_final))])
    res = sock_info.command(source, cmd)

    parsed = _parse_scram_response(res['payload'])
    if not compare_digest(parsed[b'v'], server_sig):
        raise OperationFailure("Server returned an invalid signature.")

    # Depending on how it's configured, Cyrus SASL (which the server uses)
    # requires a third empty challenge.
    if not res['done']:
        cmd = SON([('saslContinue', 1),
                   ('conversationId', res['conversationId']),
                   ('payload', Binary(b''))])
        res = sock_info.command(source, cmd)
        if not res['done']:
            raise OperationFailure('SASL conversation failed to complete.')
예제 #4
0
def _authenticate_scram(credentials, sock_info, mechanism):
    """Authenticate using SCRAM."""
    username = credentials.username
    if mechanism == 'SCRAM-SHA-256':
        digest = "sha256"
        digestmod = hashlib.sha256
        data = saslprep(credentials.password).encode("utf-8")
    else:
        digest = "sha1"
        digestmod = hashlib.sha1
        data = _password_digest(username, credentials.password).encode("utf-8")
    source = credentials.source
    cache = credentials.cache

    # Make local
    _hmac = hmac.HMAC

    ctx = sock_info.auth_ctx.get(credentials)
    if ctx and ctx.speculate_succeeded():
        nonce, first_bare = ctx.scram_data
        res = ctx.speculative_authenticate
    else:
        nonce, first_bare, cmd = _authenticate_scram_start(
            credentials, mechanism)
        res = sock_info.command(source, cmd)

    server_first = res['payload']
    parsed = _parse_scram_response(server_first)
    iterations = int(parsed[b'i'])
    if iterations < 4096:
        raise OperationFailure("Server returned an invalid iteration count.")
    salt = parsed[b's']
    rnonce = parsed[b'r']
    if not rnonce.startswith(nonce):
        raise OperationFailure("Server returned an invalid nonce.")

    without_proof = b"c=biws,r=" + rnonce
    if cache.data:
        client_key, server_key, csalt, citerations = cache.data
    else:
        client_key, server_key, csalt, citerations = None, None, None, None

    # Salt and / or iterations could change for a number of different
    # reasons. Either changing invalidates the cache.
    if not client_key or salt != csalt or iterations != citerations:
        salted_pass = hashlib.pbkdf2_hmac(
            digest, data, standard_b64decode(salt), iterations)
        client_key = _hmac(salted_pass, b"Client Key", digestmod).digest()
        server_key = _hmac(salted_pass, b"Server Key", digestmod).digest()
        cache.data = (client_key, server_key, salt, iterations)
    stored_key = digestmod(client_key).digest()
    auth_msg = b",".join((first_bare, server_first, without_proof))
    client_sig = _hmac(stored_key, auth_msg, digestmod).digest()
    client_proof = b"p=" + standard_b64encode(_xor(client_key, client_sig))
    client_final = b",".join((without_proof, client_proof))

    server_sig = standard_b64encode(
        _hmac(server_key, auth_msg, digestmod).digest())

    cmd = SON([('saslContinue', 1),
               ('conversationId', res['conversationId']),
               ('payload', Binary(client_final))])
    res = sock_info.command(source, cmd)

    parsed = _parse_scram_response(res['payload'])
    if not hmac.compare_digest(parsed[b'v'], server_sig):
        raise OperationFailure("Server returned an invalid signature.")

    # A third empty challenge may be required if the server does not support
    # skipEmptyExchange: SERVER-44857.
    if not res['done']:
        cmd = SON([('saslContinue', 1),
                   ('conversationId', res['conversationId']),
                   ('payload', Binary(b''))])
        res = sock_info.command(source, cmd)
        if not res['done']:
            raise OperationFailure('SASL conversation failed to complete.')
예제 #5
0
    def test_scram(self):
        host, port = client_context.host, client_context.port

        client_context.create_user('testscram',
                                   'sha1',
                                   'pwd',
                                   roles=['dbOwner'],
                                   mechanisms=['SCRAM-SHA-1'])

        client_context.create_user('testscram',
                                   'sha256',
                                   'pwd',
                                   roles=['dbOwner'],
                                   mechanisms=['SCRAM-SHA-256'])

        client_context.create_user('testscram',
                                   'both',
                                   'pwd',
                                   roles=['dbOwner'],
                                   mechanisms=['SCRAM-SHA-1', 'SCRAM-SHA-256'])

        client = rs_or_single_client_noauth(event_listeners=[self.listener])
        self.assertTrue(client.testscram.authenticate('sha1', 'pwd'))
        client.testscram.command('dbstats')
        client.testscram.logout()
        self.assertTrue(
            client.testscram.authenticate('sha1',
                                          'pwd',
                                          mechanism='SCRAM-SHA-1'))
        client.testscram.command('dbstats')
        client.testscram.logout()
        self.assertRaises(OperationFailure,
                          client.testscram.authenticate,
                          'sha1',
                          'pwd',
                          mechanism='SCRAM-SHA-256')

        self.assertTrue(client.testscram.authenticate('sha256', 'pwd'))
        client.testscram.command('dbstats')
        client.testscram.logout()
        self.assertTrue(
            client.testscram.authenticate('sha256',
                                          'pwd',
                                          mechanism='SCRAM-SHA-256'))
        client.testscram.command('dbstats')
        client.testscram.logout()
        self.assertRaises(OperationFailure,
                          client.testscram.authenticate,
                          'sha256',
                          'pwd',
                          mechanism='SCRAM-SHA-1')

        self.listener.results.clear()
        self.assertTrue(client.testscram.authenticate('both', 'pwd'))
        started = self.listener.results['started'][0]
        self.assertEqual(started.command.get('mechanism'), 'SCRAM-SHA-256')
        client.testscram.command('dbstats')
        client.testscram.logout()
        self.assertTrue(
            client.testscram.authenticate('both',
                                          'pwd',
                                          mechanism='SCRAM-SHA-256'))
        client.testscram.command('dbstats')
        client.testscram.logout()
        self.assertTrue(
            client.testscram.authenticate('both',
                                          'pwd',
                                          mechanism='SCRAM-SHA-1'))
        client.testscram.command('dbstats')
        client.testscram.logout()

        self.assertRaises(OperationFailure, client.testscram.authenticate,
                          'not-a-user', 'pwd')

        if HAVE_STRINGPREP:
            client_context.create_user('testscram',
                                       saslprep(u'\u2168'),
                                       u'\u2168',
                                       roles=['dbOwner'],
                                       mechanisms=['SCRAM-SHA-256'])

            self.assertTrue(client.testscram.authenticate(
                u'\u2168', u'\u2168'))
            client.testscram.command('dbstats')
            client.testscram.logout()
            self.assertTrue(
                client.testscram.authenticate(u'\u2168',
                                              u'\u2168',
                                              mechanism='SCRAM-SHA-256'))
            client.testscram.command('dbstats')
            client.testscram.logout()
            self.assertRaises(OperationFailure,
                              client.testscram.authenticate,
                              u'\u2168',
                              u'\u2168',
                              mechanism='SCRAM-SHA-1')

            client = rs_or_single_client_noauth(
                u'mongodb://\u2168:\u2168@%s:%d/testscram' % (host, port))
            client.testscram.command('dbstats')

        self.listener.results.clear()
        client = rs_or_single_client_noauth(
            'mongodb://*****:*****@%s:%d/testscram' % (host, port),
            event_listeners=[self.listener])
        client.testscram.command('dbstats')
        started = self.listener.results['started'][0]
        self.assertEqual(started.command.get('mechanism'), 'SCRAM-SHA-256')

        client = rs_or_single_client_noauth(
            'mongodb://*****:*****@%s:%d/testscram?authMechanism=SCRAM-SHA-1' %
            (host, port))
        client.testscram.command('dbstats')

        client = rs_or_single_client_noauth(
            'mongodb://*****:*****@%s:%d/testscram?authMechanism=SCRAM-SHA-256' %
            (host, port))
        client.testscram.command('dbstats')

        if client_context.is_rs:
            uri = ('mongodb://*****:*****@%s:%d/testscram'
                   '?replicaSet=%s' %
                   (host, port, client_context.replica_set_name))
            client = single_client_noauth(uri)
            client.testscram.command('dbstats')
            db = client.get_database('testscram',
                                     read_preference=ReadPreference.SECONDARY)
            db.command('dbstats')