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.")
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.')
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.")