From 979c837286d3e5fa41322d2f97c4a31494cb5da5 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 15 Feb 2024 20:32:41 +0100 Subject: [PATCH] Dynamically determine maximum CMD data length depending on SCP If we're using a Secure Channel Protocol, this will add overhead in terms of the C-MAC appended to the C-APDU. This means in turn that the useable length of the data field shrinks by a certain number of bytes. Let's make sure the SCP instances expose an 'overhead' property of how much overhead they add - and that other commands use this to determine the maximum command data field length. Change-Id: I0a081a23efe20c77557600e62b52ba90a401058d --- pySim/commands.py | 16 ++++++++++++---- pySim/global_platform/__init__.py | 5 +++-- pySim/global_platform/scp.py | 5 +++++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/pySim/commands.py b/pySim/commands.py index 4e439e65..9a491249 100644 --- a/pySim/commands.py +++ b/pySim/commands.py @@ -84,6 +84,14 @@ class SimCardCommands: """Return the (cached) patched default CLA byte for this card.""" return self._cla4lchan + @property + def max_cmd_len(self) -> int: + """Maximum length of the command apdu data section. Depends on secure channel protocol used.""" + if self.scp: + return 255 - self.scp.overhead + else: + return 255 + @cla_byte.setter def cla_byte(self, new_val: Hexstr): """Set the (raw, without lchan) default CLA value for this card.""" @@ -318,7 +326,7 @@ class SimCardCommands: total_data = '' chunk_offset = 0 while chunk_offset < length: - chunk_len = min(255, length-chunk_offset) + chunk_len = min(self.max_cmd_len, length-chunk_offset) pdu = self.cla_byte + \ 'b0%04x%02x' % (offset + chunk_offset, chunk_len) try: @@ -376,7 +384,7 @@ class SimCardCommands: total_data = '' chunk_offset = 0 while chunk_offset < data_length: - chunk_len = min(255, data_length - chunk_offset) + chunk_len = min(self.max_cmd_len, data_length - chunk_offset) # chunk_offset is bytes, but data slicing is hex chars, so we need to multiply by 2 pdu = self.cla_byte + \ 'd6%04x%02x' % (offset + chunk_offset, chunk_len) + \ @@ -560,10 +568,10 @@ class SimCardCommands: total_len = len(tlv_bin) remaining = tlv_bin while len(remaining) > 0: - fragment = remaining[:255] + fragment = remaining[:self.max_cmd_len] rdata, sw = self._set_data(fragment, first=first) first = False - remaining = remaining[255:] + remaining = remaining[self.max_cmd_len:] return rdata, sw def run_gsm(self, rand: Hexstr) -> ResTuple: diff --git a/pySim/global_platform/__init__.py b/pySim/global_platform/__init__.py index 4d553a7b..9960560d 100644 --- a/pySim/global_platform/__init__.py +++ b/pySim/global_platform/__init__.py @@ -492,13 +492,14 @@ class ADF_SD(CardADF): def store_data(self, data: bytes, structure:str = 'none', encryption:str = 'none', response_permitted: bool = False) -> bytes: """Perform the GlobalPlatform GET DATA command in order to store some card-specific data. See GlobalPlatform CardSpecification v2.3Section 11.11 for details.""" + max_cmd_len = self._cmd.lchan.scc.max_cmd_len # Table 11-89 of GP Card Specification v2.3 remainder = data block_nr = 0 response = '' while len(remainder): - chunk = remainder[:255] - remainder = remainder[255:] + chunk = remainder[:max_cmd_len] + remainder = remainder[max_cmd_len:] p1b = build_construct(ADF_SD.StoreData, {'last_block': len(remainder) == 0, 'encryption': encryption, 'structure': structure, 'response': response_permitted}) diff --git a/pySim/global_platform/scp.py b/pySim/global_platform/scp.py index 08044921..967a582d 100644 --- a/pySim/global_platform/scp.py +++ b/pySim/global_platform/scp.py @@ -218,6 +218,10 @@ class SCP02(SCP): 'seq_counter'/Int16ub, 'card_challenge'/Bytes(6), 'card_cryptogram'/Bytes(8)) kvn_range = [0x20, 0x2f] + def __init__(self, *args, **kwargs): + self.overhead = 8 + super().__init__(*args, **kwargs) + def dek_encrypt(self, plaintext:bytes) -> bytes: cipher = DES.new(self.card_keys.dek, DES.MODE_ECB) return cipher.encrypt(plaintext) @@ -410,6 +414,7 @@ class SCP03(SCP): def __init__(self, *args, **kwargs): self.s_mode = kwargs.pop('s_mode', 8) + self.overhead = self.s_mode super().__init__(*args, **kwargs) def dek_encrypt(self, plaintext:bytes) -> bytes: