mirror of
https://gitea.osmocom.org/sim-card/pysim.git
synced 2026-03-17 02:48:34 +03:00
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
This commit is contained in:
@@ -84,6 +84,14 @@ class SimCardCommands:
|
|||||||
"""Return the (cached) patched default CLA byte for this card."""
|
"""Return the (cached) patched default CLA byte for this card."""
|
||||||
return self._cla4lchan
|
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
|
@cla_byte.setter
|
||||||
def cla_byte(self, new_val: Hexstr):
|
def cla_byte(self, new_val: Hexstr):
|
||||||
"""Set the (raw, without lchan) default CLA value for this card."""
|
"""Set the (raw, without lchan) default CLA value for this card."""
|
||||||
@@ -318,7 +326,7 @@ class SimCardCommands:
|
|||||||
total_data = ''
|
total_data = ''
|
||||||
chunk_offset = 0
|
chunk_offset = 0
|
||||||
while chunk_offset < length:
|
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 + \
|
pdu = self.cla_byte + \
|
||||||
'b0%04x%02x' % (offset + chunk_offset, chunk_len)
|
'b0%04x%02x' % (offset + chunk_offset, chunk_len)
|
||||||
try:
|
try:
|
||||||
@@ -376,7 +384,7 @@ class SimCardCommands:
|
|||||||
total_data = ''
|
total_data = ''
|
||||||
chunk_offset = 0
|
chunk_offset = 0
|
||||||
while chunk_offset < data_length:
|
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
|
# chunk_offset is bytes, but data slicing is hex chars, so we need to multiply by 2
|
||||||
pdu = self.cla_byte + \
|
pdu = self.cla_byte + \
|
||||||
'd6%04x%02x' % (offset + chunk_offset, chunk_len) + \
|
'd6%04x%02x' % (offset + chunk_offset, chunk_len) + \
|
||||||
@@ -560,10 +568,10 @@ class SimCardCommands:
|
|||||||
total_len = len(tlv_bin)
|
total_len = len(tlv_bin)
|
||||||
remaining = tlv_bin
|
remaining = tlv_bin
|
||||||
while len(remaining) > 0:
|
while len(remaining) > 0:
|
||||||
fragment = remaining[:255]
|
fragment = remaining[:self.max_cmd_len]
|
||||||
rdata, sw = self._set_data(fragment, first=first)
|
rdata, sw = self._set_data(fragment, first=first)
|
||||||
first = False
|
first = False
|
||||||
remaining = remaining[255:]
|
remaining = remaining[self.max_cmd_len:]
|
||||||
return rdata, sw
|
return rdata, sw
|
||||||
|
|
||||||
def run_gsm(self, rand: Hexstr) -> ResTuple:
|
def run_gsm(self, rand: Hexstr) -> ResTuple:
|
||||||
|
|||||||
@@ -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:
|
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.
|
"""Perform the GlobalPlatform GET DATA command in order to store some card-specific data.
|
||||||
See GlobalPlatform CardSpecification v2.3Section 11.11 for details."""
|
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
|
# Table 11-89 of GP Card Specification v2.3
|
||||||
remainder = data
|
remainder = data
|
||||||
block_nr = 0
|
block_nr = 0
|
||||||
response = ''
|
response = ''
|
||||||
while len(remainder):
|
while len(remainder):
|
||||||
chunk = remainder[:255]
|
chunk = remainder[:max_cmd_len]
|
||||||
remainder = remainder[255:]
|
remainder = remainder[max_cmd_len:]
|
||||||
p1b = build_construct(ADF_SD.StoreData,
|
p1b = build_construct(ADF_SD.StoreData,
|
||||||
{'last_block': len(remainder) == 0, 'encryption': encryption,
|
{'last_block': len(remainder) == 0, 'encryption': encryption,
|
||||||
'structure': structure, 'response': response_permitted})
|
'structure': structure, 'response': response_permitted})
|
||||||
|
|||||||
@@ -218,6 +218,10 @@ class SCP02(SCP):
|
|||||||
'seq_counter'/Int16ub, 'card_challenge'/Bytes(6), 'card_cryptogram'/Bytes(8))
|
'seq_counter'/Int16ub, 'card_challenge'/Bytes(6), 'card_cryptogram'/Bytes(8))
|
||||||
kvn_range = [0x20, 0x2f]
|
kvn_range = [0x20, 0x2f]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.overhead = 8
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def dek_encrypt(self, plaintext:bytes) -> bytes:
|
def dek_encrypt(self, plaintext:bytes) -> bytes:
|
||||||
cipher = DES.new(self.card_keys.dek, DES.MODE_ECB)
|
cipher = DES.new(self.card_keys.dek, DES.MODE_ECB)
|
||||||
return cipher.encrypt(plaintext)
|
return cipher.encrypt(plaintext)
|
||||||
@@ -410,6 +414,7 @@ class SCP03(SCP):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.s_mode = kwargs.pop('s_mode', 8)
|
self.s_mode = kwargs.pop('s_mode', 8)
|
||||||
|
self.overhead = self.s_mode
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def dek_encrypt(self, plaintext:bytes) -> bytes:
|
def dek_encrypt(self, plaintext:bytes) -> bytes:
|
||||||
|
|||||||
Reference in New Issue
Block a user