moved decryption logic and simplified it a bit
This commit is contained in:
		
							parent
							
								
									eb0d3d7353
								
							
						
					
					
						commit
						bd2e64b0cd
					
				| @ -32,6 +32,7 @@ class TelegramParser(object): | |||||||
|             object["obis_reference"]: re.compile(object["obis_reference"], re.DOTALL | re.MULTILINE) |             object["obis_reference"]: re.compile(object["obis_reference"], re.DOTALL | re.MULTILINE) | ||||||
|             for object in self.telegram_specification['objects'] |             for object in self.telegram_specification['objects'] | ||||||
|         } |         } | ||||||
|  |         self._telegram_encryption_active = None | ||||||
| 
 | 
 | ||||||
|     def parse(self, telegram_data, encryption_key="", authentication_key="", throw_ex=False):  # noqa: C901 |     def parse(self, telegram_data, encryption_key="", authentication_key="", throw_ex=False):  # noqa: C901 | ||||||
|         """ |         """ | ||||||
| @ -46,38 +47,11 @@ class TelegramParser(object): | |||||||
|         :raises ParseError: |         :raises ParseError: | ||||||
|         :raises InvalidChecksumError: |         :raises InvalidChecksumError: | ||||||
|         """ |         """ | ||||||
| 
 |         telegram_data = self.decrypt_telegram_data( | ||||||
|         if "general_global_cipher" in self.telegram_specification: |             telegram_data=telegram_data, | ||||||
|             if self.telegram_specification["general_global_cipher"]: |             encryption_key=encryption_key, | ||||||
|                 enc_key = unhexlify(encryption_key) |             authentication_key=authentication_key | ||||||
|                 auth_key = unhexlify(authentication_key) |         ) | ||||||
|                 telegram_data = unhexlify(telegram_data) |  | ||||||
|                 apdu = XDlmsApduFactory.apdu_from_bytes(apdu_bytes=telegram_data) |  | ||||||
|                 if apdu.security_control.security_suite != 0: |  | ||||||
|                     logger.warning("Untested security suite") |  | ||||||
|                 if apdu.security_control.authenticated and not apdu.security_control.encrypted: |  | ||||||
|                     logger.warning("Untested authentication only") |  | ||||||
|                 if not apdu.security_control.authenticated and not apdu.security_control.encrypted: |  | ||||||
|                     logger.warning("Untested not encrypted or authenticated") |  | ||||||
|                 if apdu.security_control.compressed: |  | ||||||
|                     logger.warning("Untested compression") |  | ||||||
|                 if apdu.security_control.broadcast_key: |  | ||||||
|                     logger.warning("Untested broadcast key") |  | ||||||
|                 telegram_data = apdu.to_plain_apdu(enc_key, auth_key).decode("ascii") |  | ||||||
|             else: |  | ||||||
|                 try: |  | ||||||
|                     if unhexlify(telegram_data[0:2])[0] == GeneralGlobalCipher.TAG: |  | ||||||
|                         raise RuntimeError("Looks like a general_global_cipher frame " |  | ||||||
|                                            "but telegram specification is not matching!") |  | ||||||
|                 except Exception: |  | ||||||
|                     pass |  | ||||||
|         else: |  | ||||||
|             try: |  | ||||||
|                 if unhexlify(telegram_data[0:2])[0] == GeneralGlobalCipher.TAG: |  | ||||||
|                     raise RuntimeError( |  | ||||||
|                         "Looks like a general_global_cipher frame but telegram specification is not matching!") |  | ||||||
|             except Exception: |  | ||||||
|                 pass |  | ||||||
| 
 | 
 | ||||||
|         if self.apply_checksum_validation and self.telegram_specification['checksum_support']: |         if self.apply_checksum_validation and self.telegram_specification['checksum_support']: | ||||||
|             self.validate_checksum(telegram_data) |             self.validate_checksum(telegram_data) | ||||||
| @ -112,6 +86,42 @@ class TelegramParser(object): | |||||||
| 
 | 
 | ||||||
|         return telegram |         return telegram | ||||||
| 
 | 
 | ||||||
|  |     def decrypt_telegram_data(self, encryption_key, authentication_key, telegram_data): | ||||||
|  |         """ | ||||||
|  |         Check if telegram data is encrypted and decrypt if applicable. | ||||||
|  |         """ | ||||||
|  |         # if self._telegram_encryption_active is False: | ||||||
|  |         #     # If encryption is not working, stop trying and logging warnings. | ||||||
|  |         #     return telegram_data | ||||||
|  | 
 | ||||||
|  |         if self.telegram_specification.get("general_global_cipher"): | ||||||
|  |             enc_key = unhexlify(encryption_key) | ||||||
|  |             auth_key = unhexlify(authentication_key) | ||||||
|  |             telegram_data = unhexlify(telegram_data) | ||||||
|  |             apdu = XDlmsApduFactory.apdu_from_bytes(apdu_bytes=telegram_data) | ||||||
|  |             if apdu.security_control.security_suite != 0: | ||||||
|  |                 logger.warning("Untested security suite") | ||||||
|  |             if apdu.security_control.authenticated and not apdu.security_control.encrypted: | ||||||
|  |                 logger.warning("Untested authentication only") | ||||||
|  |             if not apdu.security_control.authenticated and not apdu.security_control.encrypted: | ||||||
|  |                 logger.warning("Untested not encrypted or authenticated") | ||||||
|  |             if apdu.security_control.compressed: | ||||||
|  |                 logger.warning("Untested compression") | ||||||
|  |             if apdu.security_control.broadcast_key: | ||||||
|  |                 logger.warning("Untested broadcast key") | ||||||
|  |             telegram_data = apdu.to_plain_apdu(enc_key, auth_key).decode("ascii") | ||||||
|  |             self._telegram_encryption_active = True | ||||||
|  |         else: | ||||||
|  |             try: | ||||||
|  |                 if unhexlify(telegram_data[0:2])[0] == GeneralGlobalCipher.TAG: | ||||||
|  |                     logger.warning("Looks like a general_global_cipher frame " | ||||||
|  |                                    "but telegram specification is not matching!") | ||||||
|  |             except Exception: | ||||||
|  |                 pass | ||||||
|  |             self._telegram_encryption_active = False | ||||||
|  | 
 | ||||||
|  |         return telegram_data | ||||||
|  | 
 | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def validate_checksum(telegram): |     def validate_checksum(telegram): | ||||||
|         """ |         """ | ||||||
|  | |||||||
| @ -1,29 +0,0 @@ | |||||||
| import unittest |  | ||||||
| import tempfile |  | ||||||
| from unittest import mock |  | ||||||
| 
 |  | ||||||
| from dsmr_parser import telegram_specifications |  | ||||||
| from dsmr_parser.clients.filereader import FileReader |  | ||||||
| from dsmr_parser.clients.serial_ import SerialReader |  | ||||||
| from dsmr_parser.clients.settings import SERIAL_SETTINGS_V5 |  | ||||||
| 
 |  | ||||||
| from test.example_telegrams import TELEGRAM_V5 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class SerialReaderTest(unittest.TestCase): |  | ||||||
| 
 |  | ||||||
|     @mock.patch('dsmr_parser.clients.serial_.serial.Serial') |  | ||||||
|     def test_read_as_object(self, mock_serial): |  | ||||||
|         serial_handle_mock = mock_serial.return_value |  | ||||||
|         # mock_serial.return_value.in_waiting = 1024 |  | ||||||
|         mock_serial.return_value.read.return_value = [b'Telegram data...', b'']  # Return data, then empty bytes |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         serial_reader = SerialReader( |  | ||||||
|             device='/dev/ttyUSB0', |  | ||||||
|             serial_settings=SERIAL_SETTINGS_V5, |  | ||||||
|             telegram_specification=telegram_specifications.V5 |  | ||||||
|         ) |  | ||||||
|          |  | ||||||
|         for telegram in serial_reader.read(): |  | ||||||
|             print(telegram)  # see 'Telegram object' docs below |  | ||||||
| @ -21,7 +21,7 @@ class TelegramParserV5Test(unittest.TestCase): | |||||||
|             telegram = parser.parse(TELEGRAM_V5, throw_ex=True) |             telegram = parser.parse(TELEGRAM_V5, throw_ex=True) | ||||||
|         except Exception as ex: |         except Exception as ex: | ||||||
|             assert False, f"parse trigged an exception {ex}" |             assert False, f"parse trigged an exception {ex}" | ||||||
|         print('test: ', type(telegram.P1_MESSAGE_HEADER), telegram.P1_MESSAGE_HEADER.__dict__) | 
 | ||||||
|         # P1_MESSAGE_HEADER (1-3:0.2.8) |         # P1_MESSAGE_HEADER (1-3:0.2.8) | ||||||
|         assert isinstance(telegram.P1_MESSAGE_HEADER, CosemObject) |         assert isinstance(telegram.P1_MESSAGE_HEADER, CosemObject) | ||||||
|         assert telegram.P1_MESSAGE_HEADER.unit is None |         assert telegram.P1_MESSAGE_HEADER.unit is None | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user