Exemple #1
0
    def authenticate_scram_sha1(self, database_name, username, password):
        # Totally stolen from pymongo.auth
        user = username.replace('=', "=3D").replace(',', "=2C")
        nonce = base64.standard_b64encode(
            str(SystemRandom().random()).encode('ascii'))[2:]
        first_bare = "n={0},r={1}".format(user, nonce.decode()).encode('ascii')

        cmd = SON([("saslStart", 1), ("mechanism", "SCRAM-SHA-1"),
                   ("autoAuthorize", 1),
                   ("payload", Binary(b"n,," + first_bare))])
        result = yield self.__run_command(database_name, cmd)

        server_first = result["payload"]
        parsed = auth._parse_scram_response(server_first)
        iterations = int(parsed[b'i'])
        salt = parsed[b's']
        rnonce = parsed[b'r']
        if not rnonce.startswith(nonce):
            raise MongoAuthenticationError(
                "TxMongo: server returned an invalid nonce.")

        without_proof = b"c=biws,r=" + rnonce
        salted_pass = auth._hi(
            auth._password_digest(username, password).encode("utf-8"),
            base64.standard_b64decode(salt), iterations)
        client_key = hmac.HMAC(salted_pass, b"Client Key", sha1).digest()
        stored_key = sha1(client_key).digest()
        auth_msg = b','.join((first_bare, server_first, without_proof))
        client_sig = hmac.HMAC(stored_key, auth_msg, sha1).digest()
        client_proof = b"p=" + base64.standard_b64encode(
            auth._xor(client_key, client_sig))
        client_final = b','.join((without_proof, client_proof))

        server_key = hmac.HMAC(salted_pass, b"Server Key", sha1).digest()
        server_sig = base64.standard_b64encode(
            hmac.HMAC(server_key, auth_msg, sha1).digest())

        cmd = SON([("saslContinue", 1),
                   ("conversationId", result["conversationId"]),
                   ("payload", Binary(client_final))])
        result = yield self.__run_command(database_name, cmd)

        if not result["ok"]:
            raise MongoAuthenticationError("TxMongo: authentication failed.")

        parsed = auth._parse_scram_response(result["payload"])
        if parsed[b'v'] != server_sig:
            raise MongoAuthenticationError(
                "TxMongo: server returned an invalid signature.")

        # Depending on how it's configured, Cyrus SASL (which the server uses)
        # requires a third empty challenge.
        if not result["done"]:
            cmd = SON([("saslContinue", 1),
                       ("conversationId", result["conversationId"]),
                       ("payload", Binary(b''))])
            result = yield self.__run_command(database_name, cmd)
            if not result["done"]:
                raise MongoAuthenticationError(
                    "TxMongo: SASL conversation failed to complete.")
Exemple #2
0
async def _authenticate_scram_sha1(credentials: MongoCredential,
                                   connection: 'aiomongo.Connection') -> None:
    """Authenticate using SCRAM-SHA-1."""
    username = credentials.username
    password = credentials.password
    source = credentials.source

    # Make local
    _hmac = hmac.HMAC
    _sha1 = sha1

    user = username.encode('utf-8').replace(b'=', b'=3D').replace(b',', b'=2C')
    nonce = standard_b64encode(
        (('{}'.format(SystemRandom().random(), ))[2:]).encode('utf-8'))
    first_bare = b'n=' + user + b',r=' + nonce

    cmd = SON([('saslStart', 1), ('mechanism', 'SCRAM-SHA-1'),
               ('payload', Binary(b'n,,' + first_bare)), ('autoAuthorize', 1)])
    res = await connection.command(source, cmd)

    server_first = res['payload']
    parsed = _parse_scram_response(server_first)
    iterations = int(parsed[b'i'])
    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
    salted_pass = _hi(
        _password_digest(username, password).encode('utf-8'),
        standard_b64decode(salt), iterations)
    client_key = _hmac(salted_pass, b'Client Key', _sha1).digest()
    stored_key = _sha1(client_key).digest()
    auth_msg = b','.join((first_bare, server_first, without_proof))
    client_sig = _hmac(stored_key, auth_msg, _sha1).digest()
    client_proof = b'p=' + standard_b64encode(_xor(client_key, client_sig))
    client_final = b','.join((without_proof, client_proof))

    server_key = _hmac(salted_pass, b'Server Key', _sha1).digest()
    server_sig = standard_b64encode(
        _hmac(server_key, auth_msg, _sha1).digest())

    cmd = SON([('saslContinue', 1), ('conversationId', res['conversationId']),
               ('payload', Binary(client_final))])
    res = await connection.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 = await connection.command(source, cmd)
        if not res['done']:
            raise OperationFailure('SASL conversation failed to complete.')
Exemple #3
0
    def authenticate_scram_sha1(self, database_name, username, password):
        # Totally stolen from pymongo.auth

        user = username.encode("utf-8").replace('=', "=3D").replace(',', "=2C")
        nonce = base64.standard_b64encode(str(SystemRandom().random())[2:].encode("utf-8"))
        first_bare = "n={0},r={1}".format(user, nonce)

        cmd = SON([("saslStart", 1),
                        ("mechanism", "SCRAM-SHA-1"),
                        ("autoAuthorize", 1),
                        ("payload", Binary("n,," + first_bare))])
        result = yield self.__run_command(database_name, cmd)

        server_first = result["payload"]
        parsed = auth._parse_scram_response(server_first)
        iterations = int(parsed['i'])
        salt = parsed['s']
        rnonce = parsed['r']
        if not rnonce.startswith(nonce):
            raise MongoAuthenticationError("Server returned an invalid nonce.")

        without_proof = "c=biws,r=" + rnonce
        salted_pass = auth._hi(auth._password_digest(username, password).encode("utf-8"),
                               base64.standard_b64decode(salt),
                               iterations)
        client_key = hmac.HMAC(salted_pass, "Client Key", sha1).digest()
        stored_key = sha1(client_key).digest()
        auth_msg = ','.join((first_bare, server_first, without_proof))
        client_sig = hmac.HMAC(stored_key, auth_msg, sha1).digest()
        client_proof = "p=" + base64.standard_b64encode(auth._xor(client_key, client_sig))
        client_final = ','.join((without_proof, client_proof))

        server_key = hmac.HMAC(salted_pass, "Server Key", sha1).digest()
        server_sig = base64.standard_b64encode(
            hmac.HMAC(server_key, auth_msg, sha1).digest())

        cmd = SON([("saslContinue", 1),
                        ("conversationId", result["conversationId"]),
                        ("payload", Binary(client_final))])
        result = yield self.__run_command(database_name, cmd)

        if not result["ok"]:
            raise MongoAuthenticationError("Authentication failed")

        parsed = auth._parse_scram_response(result["payload"])
        if parsed['v'] != server_sig:
            raise MongoAuthenticationError("Server returned an invalid signature.")

        # Depending on how it's configured, Cyrus SASL (which the server uses)
        # requires a third empty challenge.
        if not result["done"]:
            cmd = SON([("saslContinue", 1),
                            ("conversationId", result["conversationId"]),
                            ("payload", Binary(''))])
            result = yield self.__run_command(database_name, cmd)
            if not result["done"]:
                raise MongoAuthenticationError("SASL conversation failed to complete.")