def address_and_version_check(self, pck: CP1Package) -> bool: """ Checks whether a pck is meant for this client: Does the address and version number match? :param pck: The package to check :return: True in case it is meant for this client, False otherwise. """ init_field = pck.get_field(self.init_pck_field) init_field = init_field[self.init_pck_field.length() - 8:self.init_pck_field.length()] received_address_bits_hashed = init_field[0:6] own_address_bits_hashed = generate_address_hash( pck.hash_nonce(), self._address) if received_address_bits_hashed != own_address_bits_hashed: self.log.debug('The received address hash did not match: ' + received_address_bits_hashed) return False received_version_bits_hashed = init_field[6:8] own_version_bits_hashed = generate_version_hash( pck.hash_nonce(), _PROTOCOL_VERSION) if received_version_bits_hashed != own_version_bits_hashed: self.log.debug( 'The received version hash did not match the expected value: ' + str(received_version_bits_hashed)) return False return True
def test_add_next_pck_successfully_added(self): # Arrange session = CP1ClientSession(KEY_BITS_192, CP1Package()) payload_pck = CP1Package() payload = '1010101010111011' payload_pck.add_payload(payload) # Act session.add_next_pck(payload_pck) # Assert self.assertEqual(session.secret_received_in_bits, payload)
def next_pck(self, cp1_pck: CP1Package): """ Handles the next NTP package, extracts the payload and adds it to the session storage. :param cp1_pck: :return: """ self.secret_received += cp1_pck.extract_payload()
def generate_init_pck(self, address: str) -> NTP: """ Creates a new init package containing the hashed version number and address information. Also stores the AES nonce in the session. :param address: The address to hash and insert into the package. :return: the NTP package filled with address and version information. """ ntp = init_ntp_pck() raw_ntp = CP1Package(ntp) self.log.debug("Init pck field: " + str(self.init_pck_field)) self.log.debug('Value of init-package-field before transformation: ' + str(raw_ntp.get_field(self.init_pck_field))) self.aes_nonce = raw_ntp.aes_nonce_bits() address_hashed = generate_address_hash(raw_ntp.hash_nonce(), address) self.log.debug('Address hash: ' + str(address_hashed)) version_hashed = generate_version_hash(raw_ntp.hash_nonce(), _PROTOCOL_VERSION) field_value = raw_ntp.get_field(self.init_pck_field) field_value = field_value[:len(field_value) - 8] + address_hashed + version_hashed assert len(field_value) == 64 raw_ntp.set_field(field_value, self.init_pck_field) self.log.debug('Value of init-package-field after transformation: ' + str(raw_ntp.get_field(self.init_pck_field))) ntp = raw_ntp.ntp() return ntp
def send_next_pck(self, ip_address, ntp_mode: NTPMode = NTPMode.CLIENT) -> Packet: """ Sends the next chunk of payload bits to the destination. :param ip_address: :param ntp_mode: the mode of the ntp package to send. :return: the bits just send. """ next_bits_to_send = self.send_session.secret_to_send.next_bits(self.payload_size) self.log.debug("Next payload bits to send: " + str(next_bits_to_send)) ntp_pck = CP1Package(ntp_pck=init_ntp_client_pck()) ntp_pck.add_payload(next_bits_to_send) ntp_pck_ntp = ntp_pck.ntp() ntp_pck_ntp.orig = None ntp_pck_ntp.recv = None ntp_pck_ntp.mode = 5 pck_to_send = IP(dst=ip_address) / UDP() / ntp_pck_ntp send(pck_to_send) self.log.debug("Payload package successfully send to " + str(ip_address)) if not self.send_session.secret_to_send.has_next_bits(): self.log.debug("Sending complete. Terminating sending session.") return pck_to_send
def handle_incoming_ntp_pck(self, ntp_pck: NTP): cp1_pck = CP1Package(ntp_pck) self.log.info('Received pck bits: ' + str(cp1_pck._raw)) if self.listen_session is None: if not self.address_and_version_check(cp1_pck): self.log.info( 'Package did not contain matching address or version') else: self.log.info("Init pck. Creating new session") self.listen_session = CP1ClientSession(self.static_key_bits, cp1_pck) return self.listen_session.add_next_pck(cp1_pck) if self.listen_session.is_complete(): self.log.info("Payload completely received: " + str(self.listen_session.secret_received_in_bits)) self.log.info("Using key to decrypt: " + str(self.listen_session.get_decryption_key_bytes())) decoded_bits = decrypt_bits_raw( encrypted_bits=self.listen_session.secret_received_in_bits, decryption_key_bytes=self.listen_session. get_decryption_key_bytes()) self.log.info("DECRYPTED Payload: " + str(decoded_bits)) self.listen_session = None
def handle_incoming_pck(self, pck: Packet): ntp_pck = pck[NTP] cp1_pck = CP1Package(ntp_pck) self.log.info('Received pck bits: ' + str(cp1_pck._raw)) if self.send_session is None: self.log.debug("Init new session (1).") self.send_session = CP1Session() next_pck = self.send_session.generate_init_pck(self.client_address) self.add_secret_payload(self.payload, self.static_key) else: next_bits_to_send = self.send_session.secret_to_send.next_bits( self.payload_size) self.log.debug("Next payload bits to send: " + str(next_bits_to_send)) new_cp1_pck = CP1Package(ntp_pck=init_ntp_client_pck()) new_cp1_pck.add_payload(next_bits_to_send) next_pck = new_cp1_pck.ntp() upstream_pck = self.scapy_wrapper.get_upstream_ntp() upstream_pck[NTP].mode = 4 # upstream_pck[NTP].orig = ntp_pck.sent upstream_pck[NTP].sent = next_pck[NTP].sent upstream_pck[NTP].ref = next_pck[NTP].ref upstream_pck[IP].src = pck[IP].dst upstream_pck[IP].dst = pck[IP].src upstream_pck[UDP].sport = pck[UDP].dport upstream_pck[UDP].dport = pck[UDP].sport up_raw = RawNTP(upstream_pck[NTP]) pck_raw = RawNTP(ntp_pck) up_raw.set_origin_timestamp(pck_raw.transmit_timestamp()) upstream_pck[NTP] = up_raw.ntp() self.log.debug("Created new CP1 packet to send...") upstream_pck.show() send(upstream_pck) if not self.has_next_pck(): self.log.debug("Init new session (2).") self.send_session = None
def test_add_payload_in_default_correctly_added(self): # Arrange payload = '1000101011010011' cp1_package = CP1Package() # Act cp1_package.add_payload(payload) # Assert self.assertTrue(cp1_package.transmit_timestamp()[40:56] == payload)
def test_is_complete_payload_not_complete_false_returned(self): # Arrange session = CP1Session() pck = CP1Package() pck.add_payload('1010101010101010') session.next_pck(pck) # Act result = session.is_complete() # Assert self.assertFalse(result)
def test_extract_payload_default_correctlyExtracted(self): # Arrange logging.basicConfig(level=logging.DEBUG) payload = '1000101011010011' cp1_package = CP1Package() cp1_package.add_payload(payload) # Act result = cp1_package.extract_payload() # Assert self.assertEqual(result, payload)
def test_get_decryption_key_bytes(self): # Arrange cp1_pck = CP1Package() session = CP1ClientSession(KEY_BITS_192, cp1_pck) comparing_result = NTPCrypto().generate_aes_key_bytes( KEY_BITS_192, cp1_pck.aes_nonce_bits()) # Act result = session.get_decryption_key_bytes() # Assert self.assertEqual(result, comparing_result)
def test_is_complete_payload_is_complete_true_returned(self): # Arrange session = CP1Session() pck = CP1Package() pck.add_payload('1010101010101010') for x in range(8): session.next_pck(pck) # Act result = session.is_complete() # Assert self.assertTrue(result)
def test_add_payload_different_field_and_length_correctly_added(self): # Arrange payload = '10001010100110111111' cp1_package = CP1Package() # Act cp1_package.add_payload(payload, pos=5, field=NTPField.RECEIVE_TIMESTAMP) # Assert self.assertTrue( cp1_package.receive_timestamp()[5:5 + len(payload)] == payload)
def __init__( self, static_key_bits, init_pck: CP1Package, log: logging.Logger = logging.getLogger('CP1ClientSession-logger')): """ A data container which stores session data of one CP1 Session from client perspective. :param static_key_bits: The static key to decrypt a received message. :param init_pck: The first package send to the client which holds the nonce for the aes key. :param log: """ self.log = log self.crypto_tools = NTPCrypto() self._decryption_key_bytes = self.crypto_tools.generate_aes_key_bytes( static_key_bits, init_pck.aes_nonce_bits()) self.secret_received_in_bits = ''