9 Commits

Author SHA1 Message Date
Alexander Couzens
369bdb98c6 working without the step 0 hack
Change-Id: I52a8799be88a8cc5d6113466cd6f4d0d42f37adc
2025-12-16 14:49:21 +01:00
Alexander Couzens
4f30a9c2f7 osmo-smdpp: ignore EID check in EUM cert
Change-Id: Ia5baf42878f0e83b0d24350c5b265a8aadbe1a34
2025-12-16 14:21:13 +01:00
Alexander Couzens
51d91896ff Hack: make the congress profile working
Change-Id: I121a4a1a0c2279b7a44417a1f805b29dff1adc8e
2025-12-15 22:34:36 +01:00
Alexander Couzens
125449126d ts_31_102: EF SUCI_Calc_Info: fix decoding empty files
When trying to use `edit_binary_decoded` with an empty file, pysim
runs into a len(None) exception, because hpkl.to_dict()['hnet_pubkey_list'] returns
None.

Can reproduced with a CCC Camp 2023 usim and editing the file.

Change-Id: Ib8e322e65dd768bfd49e7a5620a2163f12a74ec7
2025-12-14 23:51:49 +01:00
Harald Welte
572a81f2af pySim.runtime: Fix file selection by upper case hex FID
When trying to remove a file (e.g. DF.5G_ProSe, 5FF0),
there seems to be a case sensitive check when checking for the dict:
pySim/runtime.py: get_file_for_filename():

478          def get_file_for_filename(self, name: str):
479              """Get the related CardFile object for a specified filename."""
480              sels = self.selected_file.get_selectables()
481              return sels[name]

The dict sels contains 5ff0, but not 5FF0.
The type of argument name is str. So a case sensitive check will be used.

Change-Id: Idd0db1f4bbd3ee9eec20f5fd0f4371c2882950cd
Closes: OS#6898
2025-12-10 13:34:27 +00:00
Neels Hofmeyr
ff4f2491b8 fix downstream error: ImportError: cannot import name 'style' from 'cmd2'
cmd2 version 3.0 was released, with significant API changes. Limit the
dependency to below 3.0, as already reflected in requirements.txt.

Seeing but not changing the discrepancy in minimum version:
requirements.txt has >2.6.2 while setup.py has >= 1.5.0.

Related: SYS#7775 SYS#7777
Change-Id: I5186f242dbc1b770e3ab8cdca7f27d2a1029fff6
2025-12-10 04:06:43 +01:00
Harald Welte
05fd870d1b contrib/saip-tool: Use repr() on security domain keys
Let's not reinvent the wheel of printing such data structures and use
the repr method provided by the respective class instead.  This also
adds the missing key_usage_qualifier information to the print-out,
as well as the mac_len of the key components.

Change-Id: Iaead4a02f07130fd00bcecc43e1c843f1c221e63
2025-12-09 16:23:49 +00:00
Harald Welte
c07ecbae52 pySim.esim.saip: Hex representation of SecurityDomainKey
Let's print the key_usage_qualifier in hexadecimal notation (more compact)

Change-Id: Ic9a92d53d73378eafca1760dd8351215bce1157a
2025-12-09 16:23:49 +00:00
Alexander Couzens
e20f9e6cdf ts_102_221: EF.ARR: fix read_arr_record
`read_arr_record 1` failed with an AttributeError exception
because RECORD_NR must be all caps.

Change-Id: If44f9f2271293d3063f6c527e5a68dcfaeb5942e
2025-12-04 15:32:16 +01:00
8 changed files with 39 additions and 11 deletions

View File

@@ -329,7 +329,7 @@ def do_info(pes: ProfileElementSequence, opts):
print("Security domain Instance AID: %s" % b2h(sd.decoded['instance']['instanceAID'])) print("Security domain Instance AID: %s" % b2h(sd.decoded['instance']['instanceAID']))
# FIXME: 'applicationSpecificParametersC9' parsing to figure out enabled SCP # FIXME: 'applicationSpecificParametersC9' parsing to figure out enabled SCP
for key in sd.keys: for key in sd.keys:
print("\tKVN=0x%02x, KID=0x%02x, %s" % (key.key_version_number, key.key_identifier, key.key_components)) print("\t%s" % repr(key))
# RFM # RFM
print() print()

View File

@@ -618,7 +618,7 @@ class SmDppHttpServer:
# Verify EID is within permitted range of EUM certificate # Verify EID is within permitted range of EUM certificate
if not validate_eid_range(ss.eid, eum_cert): if not validate_eid_range(ss.eid, eum_cert):
raise ApiError('8.1.4', '6.1', 'EID is not within the permitted range of the EUM certificate') logger.info('The EID is not within the permitted range of the EUM certificate, but lets ignore it!')
# Verify that the serverChallenge attached to the ongoing RSP session matches the # Verify that the serverChallenge attached to the ongoing RSP session matches the
# serverChallenge returned by the eUICC. Otherwise, the SM-DP+ SHALL return a status code "eUICC - # serverChallenge returned by the eUICC. Otherwise, the SM-DP+ SHALL return a status code "eUICC -

View File

@@ -144,6 +144,9 @@ class File:
def file_size(self) -> Optional[int]: def file_size(self) -> Optional[int]:
"""Return the size of the file in bytes.""" """Return the size of the file in bytes."""
if self.file_type in ['LF', 'CY']: if self.file_type in ['LF', 'CY']:
if self._file_size and self.nb_rec is None and self.rec_len:
self.nb_rec = self._file_size // self.rec_len
return self.nb_rec * self.rec_len return self.nb_rec * self.rec_len
elif self.file_type in ['TR', 'BT']: elif self.file_type in ['TR', 'BT']:
return self._file_size return self._file_size
@@ -291,6 +294,13 @@ class File:
dfName = fileDescriptor.get('dfName', None) dfName = fileDescriptor.get('dfName', None)
if dfName: if dfName:
self.df_name = dfName self.df_name = dfName
dfName = fileDescriptor.get('dfName', None)
if dfName:
self.df_name = dfName
efFileSize = fileDescriptor.get('efFileSize', None)
if efFileSize:
self._file_size = self._decode_file_size(efFileSize)
pefi = fileDescriptor.get('proprietaryEFInfo', {}) pefi = fileDescriptor.get('proprietaryEFInfo', {})
securityAttributesReferenced = fileDescriptor.get('securityAttributesReferenced', None) securityAttributesReferenced = fileDescriptor.get('securityAttributesReferenced', None)
if securityAttributesReferenced: if securityAttributesReferenced:
@@ -300,13 +310,11 @@ class File:
fdb_dec = fd_dec['file_descriptor_byte'] fdb_dec = fd_dec['file_descriptor_byte']
self.shareable = fdb_dec['shareable'] self.shareable = fdb_dec['shareable']
if fdb_dec['file_type'] == 'working_ef': if fdb_dec['file_type'] == 'working_ef':
efFileSize = fileDescriptor.get('efFileSize', None)
if fd_dec['num_of_rec']: if fd_dec['num_of_rec']:
self.nb_rec = fd_dec['num_of_rec'] self.nb_rec = fd_dec['num_of_rec']
if fd_dec['record_len']: if fd_dec['record_len']:
self.rec_len = fd_dec['record_len'] self.rec_len = fd_dec['record_len']
if efFileSize: if efFileSize:
self._file_size = self._decode_file_size(efFileSize)
if self.rec_len and self.nb_rec == None: if self.rec_len and self.nb_rec == None:
# compute the number of records from file size and record length # compute the number of records from file size and record length
self.nb_rec = self._file_size // self.rec_len self.nb_rec = self._file_size // self.rec_len
@@ -352,6 +360,9 @@ class File:
fd = self.get_tuplelist_item(l, 'fileDescriptor') fd = self.get_tuplelist_item(l, 'fileDescriptor')
if not fd and not self._template_derived: if not fd and not self._template_derived:
raise ValueError("%s: No fileDescriptor found in tuple, and none set by template before" % self) raise ValueError("%s: No fileDescriptor found in tuple, and none set by template before" % self)
if self.name == "EF.SUCI_Calc_Info":
breakpoint()
#breakpoint()
if fd: if fd:
self.from_fileDescriptor(dict(fd)) self.from_fileDescriptor(dict(fd))
# BODY # BODY
@@ -386,6 +397,16 @@ class File:
stream = io.BytesIO() stream = io.BytesIO()
# Providing file content within "fillFileContent" / "fillFileOffset" shall have the same effect as # Providing file content within "fillFileContent" / "fillFileOffset" shall have the same effect as
# creating a file with a fill/repeat pattern and thereafter updating the content via Update. # creating a file with a fill/repeat pattern and thereafter updating the content via Update.
# Step 0: determine file size
#file_size = self._file_size
#for k, v in l:
# if k != 'fileDescriptor':
# continue
# file_desc = v
# if 'efFileSize' in file_desc:
# file_size = self._decode_file_size(file_desc['efFileSize'])
# Step 1: Fill with pattern from Fcp or Template # Step 1: Fill with pattern from Fcp or Template
if self.fill_pattern: if self.fill_pattern:
stream.write(self.expand_fill_pattern()) stream.write(self.expand_fill_pattern())
@@ -985,9 +1006,9 @@ class SecurityDomainKey:
self.key_components = key_components self.key_components = key_components
def __repr__(self) -> str: def __repr__(self) -> str:
return 'SdKey(KVN=0x%02x, ID=0x%02x, Usage=%s, Comp=%s)' % (self.key_version_number, return 'SdKey(KVN=0x%02x, ID=0x%02x, Usage=0x%x, Comp=%s)' % (self.key_version_number,
self.key_identifier, self.key_identifier,
self.key_usage_qualifier, build_construct(KeyUsageQualifier, self.key_usage_qualifier)[0],
repr(self.key_components)) repr(self.key_components))
@classmethod @classmethod

View File

@@ -673,7 +673,7 @@ class FilesUsimDf5GS(ProfileTemplate):
FileTemplate(0x4f06, 'EF.UAC_AIC', 'TR', None, 4, 2, 0x06, None, True, ass_serv=[126]), FileTemplate(0x4f06, 'EF.UAC_AIC', 'TR', None, 4, 2, 0x06, None, True, ass_serv=[126]),
FileTemplate(0x4f07, 'EF.SUCI_Calc_Info', 'TR', None, None, 2, 0x07, 'FF...FF', False, ass_serv=[124]), FileTemplate(0x4f07, 'EF.SUCI_Calc_Info', 'TR', None, None, 2, 0x07, 'FF...FF', False, ass_serv=[124]),
FileTemplate(0x4f08, 'EF.OPL5G', 'LF', None, 10, 10, 0x08, 'FF...FF', False, ['nb_rec'], ass_serv=[129]), FileTemplate(0x4f08, 'EF.OPL5G', 'LF', None, 10, 10, 0x08, 'FF...FF', False, ['nb_rec'], ass_serv=[129]),
FileTemplate(0x4f09, 'EF.SUPI_NAI', 'TR', None, None, 2, 0x09, None, True, ['size'], ass_serv=[130]), FileTemplate(0x4f09, 'EF.SUPI_NAI', 'TR', None, None, 2, 0x09, None, True, ['size'], ass_serv=[130], pe_name='ef-supinai'),
FileTemplate(0x4f0a, 'EF.Routing_Indicator', 'TR', None, 4, 2, 0x0a, 'F0FFFFFF', False, ass_serv=[124]), FileTemplate(0x4f0a, 'EF.Routing_Indicator', 'TR', None, 4, 2, 0x0a, 'F0FFFFFF', False, ass_serv=[124]),
] ]
@@ -818,7 +818,7 @@ class FilesIsimOptional(ProfileTemplate):
base_path = Path('ADF.ISIM') base_path = Path('ADF.ISIM')
extends = FilesIsimMandatory extends = FilesIsimMandatory
files = [ files = [
FileTemplate(0x6f09, 'EF.P-CSCF', 'LF', 1, None, 2, None, None, True, ['size'], ass_serv=[1,5]), FileTemplate(0x6f09, 'EF.P-CSCF', 'LF', 1, None, 2, None, None, True, ['size'], ass_serv=[1,5], pe_name='ef-pcscf'),
FileTemplate(0x6f3c, 'EF.SMS', 'LF', 10, 176, 5, None, '00FF...FF', False, ass_serv=[6,8]), FileTemplate(0x6f3c, 'EF.SMS', 'LF', 10, 176, 5, None, '00FF...FF', False, ass_serv=[6,8]),
FileTemplate(0x6f42, 'EF.SMSP', 'LF', 1, 38, 5, None, 'FF...FF', False, ass_serv=[8]), FileTemplate(0x6f42, 'EF.SMSP', 'LF', 1, 38, 5, None, 'FF...FF', False, ass_serv=[8]),
FileTemplate(0x6f43, 'EF.SMSS', 'TR', None, 2, 5, None, 'FFFF', False, ass_serv=[6,8]), FileTemplate(0x6f43, 'EF.SMSS', 'TR', None, 2, 5, None, 'FFFF', False, ass_serv=[6,8]),

View File

@@ -477,11 +477,15 @@ class RuntimeLchan:
def get_file_for_filename(self, name: str): def get_file_for_filename(self, name: str):
"""Get the related CardFile object for a specified filename.""" """Get the related CardFile object for a specified filename."""
if is_hex(name):
name = name.lower()
sels = self.selected_file.get_selectables() sels = self.selected_file.get_selectables()
return sels[name] return sels[name]
def activate_file(self, name: str): def activate_file(self, name: str):
"""Request ACTIVATE FILE of specified file.""" """Request ACTIVATE FILE of specified file."""
if is_hex(name):
name = name.lower()
sels = self.selected_file.get_selectables() sels = self.selected_file.get_selectables()
f = sels[name] f = sels[name]
data, sw = self.scc.activate_file(f.fid) data, sw = self.scc.activate_file(f.fid)

View File

@@ -750,7 +750,7 @@ class EF_ARR(LinFixedEF):
@cmd2.with_argparser(LinFixedEF.ShellCommands.read_rec_dec_parser) @cmd2.with_argparser(LinFixedEF.ShellCommands.read_rec_dec_parser)
def do_read_arr_record(self, opts): def do_read_arr_record(self, opts):
"""Read one EF.ARR record in flattened, human-friendly form.""" """Read one EF.ARR record in flattened, human-friendly form."""
(data, _sw) = self._cmd.lchan.read_record_dec(opts.record_nr) (data, _sw) = self._cmd.lchan.read_record_dec(opts.RECORD_NR)
data = self._cmd.lchan.selected_file.flatten(data) data = self._cmd.lchan.selected_file.flatten(data)
self._cmd.poutput_json(data, opts.oneline) self._cmd.poutput_json(data, opts.oneline)

View File

@@ -389,7 +389,10 @@ class EF_SUCI_Calc_Info(TransparentEF):
# remaining data holds Home Network Public Key Data Object # remaining data holds Home Network Public Key Data Object
hpkl = EF_SUCI_Calc_Info.HnetPubkeyList() hpkl = EF_SUCI_Calc_Info.HnetPubkeyList()
hpkl.from_tlv(in_bytes[pos:]) hpkl.from_tlv(in_bytes[pos:])
hnet_pubkey_list = self._compact_pubkey_list(hpkl.to_dict()['hnet_pubkey_list'])
hnet_pubkey_list = []
if hpkl.to_dict()['hnet_pubkey_list']:
hnet_pubkey_list = self._compact_pubkey_list(hpkl.to_dict()['hnet_pubkey_list'])
return { return {
'prot_scheme_id_list': prot_scheme_id_list, 'prot_scheme_id_list': prot_scheme_id_list,

View File

@@ -21,7 +21,7 @@ setup(
"pyscard", "pyscard",
"pyserial", "pyserial",
"pytlv", "pytlv",
"cmd2 >= 1.5.0", "cmd2 >= 1.5.0, < 3.0",
"jsonpath-ng", "jsonpath-ng",
"construct >= 2.10.70", "construct >= 2.10.70",
"bidict", "bidict",