def _init_connection(self, response): """ Prepare initial connection message. We send the version as well as the initial JSON as an optimization. :param response: Response from the database :raises: ReqlDriverError :return: Initial message which will be sent to the DB """ if response is not None: raise ReqlDriverError('Unexpected response') self._random_nonce = base64.standard_b64encode( bytes(bytearray(SystemRandom().getrandbits(8) for i in range(18)))) self._first_client_message = chain_to_bytes('n=', self._username, ',r=', self._random_nonce) initial_message = chain_to_bytes( struct.pack('<L', self.VERSION), self._json_encoder.encode({ 'protocol_version': self._protocol_version, 'authentication_method': 'SCRAM-SHA-256', 'authentication': chain_to_bytes('n,,', self._first_client_message).decode('ascii') }).encode('utf-8'), b'\0') self._next_state() return initial_message
def test_init_connection(self, mock_base64): self.handshake._next_state = Mock() encoded_string = "test" mock_base64.standard_b64encode.return_value = encoded_string first_client_message = chain_to_bytes( "n=", self.handshake._username, ",r=", encoded_string ) expected_result = chain_to_bytes( struct.pack("<L", self.handshake.VERSION), self.handshake._json_encoder.encode( { "protocol_version": self.handshake._protocol_version, "authentication_method": "SCRAM-SHA-256", "authentication": chain_to_bytes( "n,,", first_client_message ).decode("ascii"), } ).encode("utf-8"), b"\0", ) result = self.handshake._init_connection(response=None) assert result == expected_result assert self.handshake._next_state.called is True
def _prepare_auth_request(self, response): """ Put tohether the authentication request based on the response of the database. :param response: Response from the database :raises: ReqlDriverError | ReqlAuthError :return: An empty string """ json_response = self._decode_json_response(response, with_utf8=True) first_client_message, authentication = self._get_authentication_and_first_client_message( json_response) random_nonce = authentication[b'r'] if not random_nonce.startswith(self._random_nonce): raise ReqlAuthError('Invalid nonce from server', self._host, self._port) salted_password = self._pbkdf2_hmac( 'sha256', self._password, base64.standard_b64decode(authentication[b's']), int(authentication[b'i'])) message_without_proof = chain_to_bytes('c=biws,r=', random_nonce) auth_message = b','.join((self._first_client_message, first_client_message, message_without_proof)) self._server_signature = hmac.new( hmac.new(salted_password, b'Server Key', hashlib.sha256).digest(), auth_message, hashlib.sha256).digest() client_key = hmac.new(salted_password, b'Client Key', hashlib.sha256).digest() client_signature = hmac.new( hashlib.sha256(client_key).digest(), auth_message, hashlib.sha256).digest() client_proof = struct.pack( '32B', *(l ^ random_nonce for l, random_nonce in zip( struct.unpack('32B', client_key), struct.unpack('32B', client_signature)))) authentication_request = chain_to_bytes( self._json_encoder.encode({ 'authentication': chain_to_bytes( message_without_proof, ',p=', base64.standard_b64encode(client_proof)).decode('ascii') }), b'\0') self._next_state() return authentication_request
def test_init_connection(self, mock_base64): self.handshake._next_state = Mock() encoded_string = 'test' mock_base64.standard_b64encode.return_value = encoded_string first_client_message = chain_to_bytes('n=', self.handshake._username, ',r=', encoded_string) expected_result = chain_to_bytes( struct.pack('<L', self.handshake.VERSION), self.handshake._json_encoder.encode({ 'protocol_version': self.handshake._protocol_version, 'authentication_method': 'SCRAM-SHA-256', 'authentication': chain_to_bytes('n,,', first_client_message).decode('ascii') }).encode('utf-8'), b'\0') result = self.handshake._init_connection(response=None) assert result == expected_result assert self.handshake._next_state.called is True
def _init_connection(self, response): """ Prepare initial connection message. We send the version as well as the initial JSON as an optimization. :param response: Response from the database :raises: ReqlDriverError :return: Initial message which will be sent to the DB """ if response is not None: raise ReqlDriverError("Unexpected response") self._random_nonce = base64.standard_b64encode( bytes(bytearray(SystemRandom().getrandbits(8) for i in range(18))) ) self._first_client_message = chain_to_bytes( "n=", self._username, ",r=", self._random_nonce ) initial_message = chain_to_bytes( struct.pack("<L", self.VERSION), self._json_encoder.encode( { "protocol_version": self._protocol_version, "authentication_method": "SCRAM-SHA-256", "authentication": chain_to_bytes( "n,,", self._first_client_message ).decode("ascii"), } ).encode("utf-8"), b"\0", ) self._next_state() return initial_message
def test_prepare_auth_request(self): self.handshake._next_state = Mock() self.handshake._random_nonce = base64.encodebytes( b'random_nonce') if six.PY3 else base64.b64encode(b'random_nonce') self.handshake._first_client_message = chain_to_bytes( 'n=', self.handshake._username, ',r=', self.handshake._random_nonce) response = { 'success': True, 'authentication': 's=cmFuZG9tX25vbmNl\n,i=2,r=cmFuZG9tX25vbmNl\n' } if six.PY3: expected_result = b'{"authentication": "c=biws,r=cmFuZG9tX25vbmNl\\n,p=2Tpd60LM4Tkhe7VATTPj/lh4yunl07Sm4A+m3ukC774="}\x00' else: expected_result = b'{"authentication": "c=biws,r=cmFuZG9tX25vbmNl\\n,p=JqVP98bzu3yye/3SLopNJvCRimBx34uKI/EY8UI41gM="}\x00' result = self.handshake._prepare_auth_request(json.dumps(response)) assert isinstance(result, six.binary_type) assert result == expected_result assert self.handshake._next_state.called is True
def test_mixed_chaining(self): expected_string = b"iron man" result = chain_to_bytes("iron", " ", b"man") assert result == expected_string
def test_mixed_chaining(self): expected_string = b'iron man' result = chain_to_bytes('iron', ' ', b'man') assert result == expected_string
def _prepare_auth_request(self, response): """ Put tohether the authentication request based on the response of the database. :param response: Response from the database :raises: ReqlDriverError | ReqlAuthError :return: An empty string """ json_response = self._decode_json_response(response, with_utf8=True) ( first_client_message, authentication, ) = self._get_authentication_and_first_client_message(json_response) random_nonce = authentication[b"r"] if not random_nonce.startswith(self._random_nonce): raise ReqlAuthError("Invalid nonce from server", self._host, self._port) salted_password = self._pbkdf2_hmac( "sha256", self._password, base64.standard_b64decode(authentication[b"s"]), int(authentication[b"i"]), ) message_without_proof = chain_to_bytes("c=biws,r=", random_nonce) auth_message = b",".join( (self._first_client_message, first_client_message, message_without_proof) ) self._server_signature = hmac.new( hmac.new(salted_password, b"Server Key", hashlib.sha256).digest(), auth_message, hashlib.sha256, ).digest() client_key = hmac.new(salted_password, b"Client Key", hashlib.sha256).digest() client_signature = hmac.new( hashlib.sha256(client_key).digest(), auth_message, hashlib.sha256 ).digest() client_proof = struct.pack( "32B", *( l ^ random_nonce for l, random_nonce in zip( struct.unpack("32B", client_key), struct.unpack("32B", client_signature), ) ) ) authentication_request = chain_to_bytes( self._json_encoder.encode( { "authentication": chain_to_bytes( message_without_proof, ",p=", base64.standard_b64encode(client_proof), ).decode("ascii") } ), b"\0", ) self._next_state() return authentication_request