The keyword argument should be `nested=`. As written `ApplicationAID` is silently ignored - `ApplicationTemplate` will not descend into its nested TLVs.
global_platform: fix typo in SupportedTlsCipherSuitesForScp81
The attribute name is misspelled. The BER-TLV infrastructure looks for `_construct`; this typo means `SupportedTlsCipherSuitesForScp81` will never decode its content.
global_platform: fix store_data() returning last chunk only
The loop builds up `response` across multiple STORE DATA blocks, but the function returns only `data` - the response from the *last* block. It should return the accumulated response instead.
Both `do_store_data` and `store_data` have identical docstrings that incorrectly describe the command as GET DATA. Should be "STORE DATA". Take a chance to fix missing space between `v2.3` and `Section`.
* field `tp_rp` appears at bit positions 7 and 5 ** bit 7 should be `tp_rp` (Reply Path) ** bit 5 should be `tp_sri` (Status Report Indication) * field `tp_lp` is completely missing ** should be at bit position 3
global_platform: install_cap_parser: argument groups cannot be nested
pySim-shell currently does not work on systems with Python 3.14+:
File ".../pysim/pySim/global_platform/__init__.py", line 868, in AddlShellCommands install_cap_parser_inst_prm_g_grp = install_cap_parser_inst_prm_g.add_argument_group() File "/usr/lib/python3.14/argparse.py", line 1794, in add_argument_group raise ValueError('argument groups cannot be nested') ValueError('argument groups cannot be nested')
The problem is that install_cap_parser creates a nested group inside of mutually exclusive group. argparse never supported group nesting properly, so it has been deprecated since Python 3.11, and eventually got removed in Python 3.14.
Remove group nesting, adjust the usage string, and implement the mutual exclusiveness/inclusiveness manually in do_install_cap().
gen_install_parameters() had contradictory logic: the outer guard required all three arguments to be non-None/non-empty (making them mutually inclusive), while the inner checks then treated each one as optional.
Make each parameter independently optional (defaulting to None) and remove the all-or-nothing check. Simplify the function body to a straightforward single-pass construction of system_specific_params.
The URL used when HTTP requests are performed is defined statically with the url_prefix passed to the constructor of JsonHttpApiClient together with the path property in JsonHttpApiFunction.
For applications that require dynamic URLs there is no way to rewrite the URL. Let's add a mechanism that allows API users to apply custom URL reqriting rules by adding a rewrite_url method to JsonHttpApiFunction. API users may then overload this method with a custom implementation as needed.
The URL used when HTTP requests are performed is defined statically with the url_prefix passed to the constructor of JsonHttpApiClient together with the path property in JsonHttpApiFunction.
For applications that require dynamic URLs there is no way to rewrite the URL. Let's add a mechanism that allows API users to apply custom URL reqriting rules by adding a rewrite_url method to JsonHttpApiFunction. API users may then overload this method with a custom implementation as needed.
The URL used when HTTP requests are performed is defined statically with the url_prefix passed to the constructor of JsonHttpApiClient together with the path property in JsonHttpApiFunction.
For applications that require dynamic URLs there is no way to rewrite the URL. Let's add a mechanism that allows API users to apply custom URL reqriting rules by adding a reqrite_url method to JsonHttpApiFunction. API users may then overload this method with a custom implementation as needed.
global_platform: fix typo in SupportedTlsCipherSuitesForScp81
The attribute name is misspelled. The BER-TLV infrastructure looks for `_construct`; this typo means `SupportedTlsCipherSuitesForScp81` will never decode its content.
The keyword argument should be `nested=`. As written `ApplicationAID` is silently ignored - `ApplicationTemplate` will not descend into its nested TLVs.
* field `tp_rp` appears at bit positions 7 and 5 ** bit 7 should be `tp_rp` (Reply Path) ** bit 5 should be `tp_sri` (Status Report Indication) * field `tp_lp` is completely missing ** should be at bit position 3
global_platform: fix store_data() returning last chunk only
The loop builds up `response` across multiple STORE DATA blocks, but the function returns only `data` - the response from the *last* block. It should return the accumulated response instead.
Both `do_store_data` and `store_data` have identical docstrings that incorrectly describe the command as GET DATA. Should be "STORE DATA". Take a chance to fix missing space between `v2.3` and `Section`.
global_platform: make --install-parameters-* independently optional
gen_install_parameters() had contradictory logic: the outer guard required all three arguments to be non-None/non-empty (making them mutually inclusive), while the inner checks then treated each one as optional.
Make each parameter independently optional (defaulting to None) and remove the all-or-nothing guard from both the function and its caller. Simplify the function body to a straightforward single-pass construction of system_specific_params.
Implement pySim.esim.saip.batch.BatchPersonalization, generating N eSIM profiles from a preset configuration.
Batch parameters can be fed by a constant, incrementing, random or from CSV rows: add pySim.esim.saip.param_source.* classes to feed such input to each of the BatchPersonalization's ConfigurableParameter instances.
personalization: indicate default ParamSource per ConfigurableParameter
Add default_source class members pointing to ParamSource classes to all ConfigurableParameter subclasses.
This is useful to automatically set up a default ParamSource for a given ConfigurableParameter subclass, during user interaction to produce a batch personalization.
For example, if the user selects a Pin1 parameter, a calling program can implicitly set this to a RandomDigitSource, which will magically make it work the way that most users need.
BTW, default_source and default_value can be combined to configure a matching ParamSource instance:
personalization: implement reading back values from a PES
Implement get_values_from_pes(), the reverse direction of apply_val(): read back and return values from a ProfileElementSequence. Implement for all ConfigurableParameter subclasses.
Future: SdKey.get_values_from_pes() is reading pe.decoded[], which works fine, but I07dfc378705eba1318e9e8652796cbde106c6a52 will change this implementation to use the higher level ProfileElementSD members.
Implementation detail:
Implement get_values_from_pes() as classmethod that returns a generator. Subclasses should yield all occurences of their parameter in a given PES.
For example, the ICCID can appear in multiple places. Iccid.get_values_from_pes() yields all of the individual values. A set() of the results quickly tells whether the PES is consistent.
Rationales for reading back values:
This allows auditing an eSIM profile, particularly for producing an output.csv from a batch personalization (that generated lots of random key material which now needs to be fed to an HLR...).
Reading back from a binary result is more reliable than storing the values that were fed into a personalization. By auditing final DER results with this code, I discovered: - "oh, there already was some key material in my UPP template." - "all IMSIs ended up the same, forgot to set up the parameter." - the SdKey.apply() implementations currently don't work, see I07dfc378705eba1318e9e8652796cbde106c6a52 for a fix.
personalization: indicate default ParamSource per ConfigurableParameter
Add default_source class members pointing to ParamSource classes to all ConfigurableParameter subclasses.
This is useful to automatically set up a default ParamSource for a given ConfigurableParameter subclass, during user interaction to produce a batch personalization.
For example, if the user selects a Pin1 parameter, a calling program can implicitly set this to a RandomDigitSource, which will magically make it work the way that most users need.
BTW, default_source and default_value can be combined to configure a matching ParamSource instance:
personalization: implement reading back values from a PES
Implement get_values_from_pes(), the reverse direction of apply_val(): read back and return values from a ProfileElementSequence. Implement for all ConfigurableParameter subclasses.
Future: SdKey.get_values_from_pes() is reading pe.decoded[], which works fine, but I07dfc378705eba1318e9e8652796cbde106c6a52 will change this implementation to use the higher level ProfileElementSD members.
Implementation detail:
Implement get_values_from_pes() as classmethod that returns a generator. Subclasses should yield all occurences of their parameter in a given PES.
For example, the ICCID can appear in multiple places. Iccid.get_values_from_pes() yields all of the individual values. A set() of the results quickly tells whether the PES is consistent.
Rationales for reading back values:
This allows auditing an eSIM profile, particularly for producing an output.csv from a batch personalization (that generated lots of random key material which now needs to be fed to an HLR...).
Reading back from a binary result is more reliable than storing the values that were fed into a personalization. By auditing final DER results with this code, I discovered: - "oh, there already was some key material in my UPP template." - "all IMSIs ended up the same, forgot to set up the parameter." - the SdKey.apply() implementations currently don't work, see I07dfc378705eba1318e9e8652796cbde106c6a52 for a fix.
Implement pySim.esim.saip.batch.BatchPersonalization, generating N eSIM profiles from a preset configuration.
Batch parameters can be fed by a constant, incrementing, random or from CSV rows: add pySim.esim.saip.param_source.* classes to feed such input to each of the BatchPersonalization's ConfigurableParameter instances.
* field `tp_rp` appears at bit positions 7 and 5 ** bit 7 should be `tp_rp` (Reply Path) ** bit 5 should be `tp_sri` (Status Report Indication) * field `tp_lp` is completely missing ** should be at bit position 3
Both `do_store_data` and `store_data` have identical docstrings that incorrectly describe the command as GET DATA. Should be "STORE DATA". Take a chance to fix missing space between `v2.3` and `Section`.
* field `tp_rp` appears at bit positions 7 and 5 ** bit 7 should be `tp_rp` (Reply Path) ** bit 5 should be `tp_sri` (Status Report Indication) * field `tp_lp` is completely missing ** should be at bit position 3
global_platform: fix store_data() returning last chunk only
The loop builds up `response` across multiple STORE DATA blocks, but the function returns only `data` - the response from the *last* block. It should return the accumulated response instead.
global_platform: fix typo in SupportedTlsCipherSuitesForScp81
The attribute name is misspelled. The BER-TLV infrastructure looks for `_construct`; this typo means `SupportedTlsCipherSuitesForScp81` will never decode its content.
global_platform: install_cap_parser: argument groups cannot be nested
pySim-shell currently does not work on systems with Python 3.14+:
File ".../pysim/pySim/global_platform/__init__.py", line 868, in AddlShellCommands install_cap_parser_inst_prm_g_grp = install_cap_parser_inst_prm_g.add_argument_group() File "/usr/lib/python3.14/argparse.py", line 1794, in add_argument_group raise ValueError('argument groups cannot be nested') ValueError('argument groups cannot be nested')
The problem is that install_cap_parser creates a nested group inside of mutually exclusive group. argparse never supported group nesting properly, so it has been deprecated since Python 3.11, and eventually got removed in Python 3.14.
Remove group nesting, adjust the usage string, and implement the mutual exclusiveness/inclusiveness manually in do_install_cap().
The keyword argument should be `nested=`. As written `ApplicationAID` is silently ignored - `ApplicationTemplate` will not descend into its nested TLVs.
docs/put_key: add tutorial that explains how to manage global platform keys
With the increased interest in using GlobalPlatform features of UICC and eUICCs (OTA-SMS, applets, etc.), also comes an increased interest in how the related GlobalPlatform keys can be managed (key rotation, adding/removing keysets from/to a Security Domain).
Unfortunately, many aspects of this topic are not immediately obvious for the average user. Let's add a tutorial that contains some practical examples to shine some light on the topic.
docs/put_key: add tutorial that explains how to manage global platform keys
With the increased interest in using GlobalPlatform features of UICC and eUICCs (OTA-SMS, applets, etc.), also comes an increased interest in how the related GlobalPlatform keys can be managed (key rotation, adding/removing keysets from/to a Security Domain).
Unfortunately, many aspects of this topic are not immediately obvious for the average user. Let's add a tutorial that contains some practical examples to shine some light on the topic.
pySim-prog/pySim-read: add pySimLogger and verbose cmdline argument
pySim-prog and pySim-read do not integrate the pySimLogger yet. As we may add more debug output that should not be visible on normal use, we should ensure that the pySimLogger is correctly set up.
PySimLogger: add parameter to set initial log-level/verbosity
When we initialize a new PySimLogger, we always call the setup method first and then use the set_verbose and set_level method to configure the initial log level and the initial log verbosity. However, we initialize the PySimLogger in all our programs the same way and we end up with the same boilerplate code every time. Let's add a keyword parameter to the setup method where we can pass our opts.verbose (bool) parameter so that the setup method can do the work for the main program.
In case the caller wants a different default configuration he still can call set_verbose and set_level methods as needed.
pySim-prog/pySim-read: add pySimLogger and verbose cmdline argument
pySim-prog and pySim-read do not integrate the pySimLogger yet. As we may add more debug output that should not be visible on normal use, we should ensure that the pySimLogger is correctly set up.
global_platform: install_cap_parser: argument groups cannot be nested
pySim-shell currently does not work on systems with Python 3.14+:
File ".../pysim/pySim/global_platform/__init__.py", line 868, in AddlShellCommands install_cap_parser_inst_prm_g_grp = install_cap_parser_inst_prm_g.add_argument_group() File "/usr/lib/python3.14/argparse.py", line 1794, in add_argument_group raise ValueError('argument groups cannot be nested') ValueError('argument groups cannot be nested')
The problem is that install_cap_parser creates a nested group inside of mutually exclusive group. argparse never supported group nesting properly, so it has been deprecated since Python 3.11, and eventually got removed in Python 3.14.
Remove group nesting, adjust the usage string, and implement the mutual exclusiveness/inclusiveness manually in do_install_cap().
The current behavior we implement in the method __send_apdu_T0 is incomplete. Some details discussed in ETSI TS 102 221, section 7.3.1.1.4, clause 4 seem to be not fully implemented. We may also end up sending a GET RESPONSE in other APDU cases than case 4 (the only case that uses the GET RESPONSE command).
contrib/csv-to_pgsl: explicitly set log level to INFO in non verbose mode
The current log level for PySimLogger currently is DEBUG. (The default for verbose is False). Since those defaults may change over time, we should conciously set what we want in verbose and non verbose mode, like we already do in pySim-shell.
pySim-prog/pySim-read: add pySimLogger and verbose cmdline argument
pySim-prog and pySim-read do not integrate the pySimLogger yet. As we may add more debug output that should not be visible on normal use, we should ensure that the pySimLogger is correctly set up.
The current behavior we implement in the method __send_apdu_T0 is incomplete. Some details discussed in ETSI TS 102 221, section 7.3.1.1.4, clause 4 seem to be not fully implemented. We may also end up sending a GET RESPONSE in other APDU cases than case 4 (the only case that uses the GET RESPONSE command).
The current behavior we implement in the method __send_apdu_T0 is incomplete. Some details discussed in ETSI TS 102 221, section 7.3.1.1.4, clause 4 seem to be not fully implemented. We may also end up sending a GET RESPONSE in other APDU cases than case 4 (the only case that uses the GET RESPONSE command).
The current behavior we implement in the method __send_apdu_T0 is incomplete. Some details discussed in ETSI TS 102 221, section 7.3.1.1.4, clause 4 seem to be not fully implemented. We may also end up sending a GET RESPONSE in other APDU cases than case 4 (the only case that uses the GET RESPONSE command).
global_platform/scp: fix dek_encrypt/dek_decrypt for SCP02
The methods dek_encrypt/dek_decrypt use the wrong algorithm and the wrong key material. The algorithm should be 3DES rather then single DES and the key must be the DEK session key instead of the static DEK key from which the DEK session key is derived.
contrib/smpp-ota-tool: define commandline arguments in global scope
The commandline arguments are currently defined under __main__ in a private scope. From there they are not reachable to the sphinx argparse module. We have to define the arguments globally at the top. (like in the other applications)
We already have documentation that explains how to run pySim-smpp2sim. With smpp-ota-tool we now have a counterpart for pySim-smpp2sim, so let's add documentation for this tool as well.
tests/pySim-smpp2sim_test/card_sanitizer: update card backup with new test keyset
In our test setup we run the card_sanitizer.py script regualary to ensure that we have consistent start conditions when running our tests. In case a testcase crashes for some reason and leaves messed up files on a test card. The card_sanitizer.py script will ensure that any problem like that is cleaned up over night.
For the testcases we are about to add in the patch following this one, we need to provision a new test keyset to one of our test cards. This has been already done manually. However since the card_sanitizer still has the old keys in its backup we will have to update that as well.
tests/pySim-smpp2sim_test: add testcases for AES128 and AES256
Extend the existing test script so that it can handle multiple testcases. Also add support for switching eUICC profiles. Finally, add a testcases to test OTA-SMS (RFM) with AES128 and AES256 encryption.
The URL used when HTTP requests are performed is defined statically with the url_prefix passed to the constructor of JsonHttpApiClient together with the path property in JsonHttpApiFunction.
For applications that require dynamic URLs there is no way to rewrite the URL. Let's add a mechanism that allows API users to apply custom URL reqriting rules by adding a reqrite_url method to JsonHttpApiFunction. API users may then overload this method with a custom implementation as needed.
personalization audit: optionally audit all (unknown) SD keys
By a flag, allow to audit also all Security Domain KVN that we have *not* created ConfigurableParameter subclasses for.
For example, SCP80 has reserved kvn 0x01..0x0f, but we offer only Scp80Kvn01, Scp80Kvn02, Scp80Kvn03. So we would not show kvn 0x03..0x0f in an audit.
This patch includes audits of all SD key kvn there may be in the UPP. This will help to spot SD keys that may already be present in a UPP template, with unexpected / unusual kvn.
To help existing applications transition to a common naming scheme for the SdKey classes, offer this intermediate result, where the SdKey classes' .name are still unchanged as before generating them.
saip SmspTpScAddr: safeguard against decoding error
Reading the TS48 V6.0 eSIM_GTP_SAIP2.1A_NoBERTLV profile results in an exception [1] in SmspTpScAddr. I have a caller that needs to skip erratic values instead of raising.
The underlying issue, I presume, is that either the data needs validation before decode_record_bin(), or decode_record_bin() needs well-defined error handling.
So far I know only of this IndexError, so, as a workaround, catch that.
[1] File "/pysim/pySim/esim/saip/personalization.py", line 617, in get_values_from_pes ef_smsp_dec = ef_smsp.decode_record_bin(f_smsp.body, 1) File "/pysim/pySim/filesystem.py", line 1047, in decode_record_bin return parse_construct(self._construct, raw_bin_data) File "/application/venv/lib/python3.13/site-packages/osmocom/construct.py", line 550, in parse_construct parsed = c.parse(raw_bin_data, total_len=length, **context) File "/application/venv/lib/python3.13/site-packages/construct/core.py", line 404, in parse return self.parse_stream(io.BytesIO(data), **contextkw) ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/application/venv/lib/python3.13/site-packages/construct/core.py", line 416, in parse_stream return self._parsereport(stream, context, "(parsing)") ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/application/venv/lib/python3.13/site-packages/construct/core.py", line 428, in _parsereport obj = self._parse(stream, context, path) File "/application/venv/lib/python3.13/site-packages/construct/core.py", line 2236, in _parse subobj = sc._parsereport(stream, context, path) File "/application/venv/lib/python3.13/site-packages/construct/core.py", line 428, in _parsereport obj = self._parse(stream, context, path) File "/application/venv/lib/python3.13/site-packages/construct/core.py", line 2770, in _parse return self.subcon._parsereport(stream, context, path) ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^ File "/application/venv/lib/python3.13/site-packages/construct/core.py", line 428, in _parsereport obj = self._parse(stream, context, path) File "/application/venv/lib/python3.13/site-packages/construct/core.py", line 2236, in _parse subobj = sc._parsereport(stream, context, path) File "/application/venv/lib/python3.13/site-packages/construct/core.py", line 428, in _parsereport obj = self._parse(stream, context, path) File "/application/venv/lib/python3.13/site-packages/construct/core.py", line 2770, in _parse return self.subcon._parsereport(stream, context, path) ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^ File "/application/venv/lib/python3.13/site-packages/construct/core.py", line 428, in _parsereport obj = self._parse(stream, context, path) File "/application/venv/lib/python3.13/site-packages/construct/core.py", line 820, in _parse return self._decode(obj, context, path) ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^ File "/application/venv/lib/python3.13/site-packages/osmocom/construct.py", line 268, in _decode if r[-1] == 'f': ~^^^^ File "/application/venv/lib/python3.13/site-packages/osmocom/utils.py", line 50, in __getitem__ return hexstr(super().__getitem__(val)) ~~~~~~~~~~~~~~~~~~~^^^^^ IndexError: string index out of range
saip: add numeric_base indicator to ConfigurableParameter and ParamSource
By default, numeric_base = None, to indicate that there are no explicit limitations on the number space.
For parameters that are definitely decimal, set numeric_base = 10. For definitely hexadecimal, set numeric_base = 16.
Do the same for ConfigurableParameter as well as ParamSource, so callers can match them up: if a parameter is numeric_base = 10, then omit sources that are numeric_base = 16, and vice versa.
The AlgorithmID has a few preset values, and hardly anyone knows which is which. So instead of entering '1', '2' or '3', make it work with prededined values 'Milenage', 'TUAK' and 'usim-test'.
Implement the enum value part abstractly in new EnumParam.
Make AlgorithmID a subclass of EnumParam and define the values as from pySim/esim/asn1/saip/PE_Definitions-3.3.1.asn
'securityDomain' elements are decoded to ProfileElementSD instances, which keep higher level representations of the key data apart from the decoded[] lists.
So far, apply_val() was dropping binary values in decoded[], which does not work, because ProfileElementSD._pre_encode() overwrites self.decoded[] from the higher level representation.
Implement using - ProfileElementSD.find_key() and SecurityDomainKeyComponent to modify an exsiting entry, or - ProfileElementSD.add_key() to create a new entry.
Before this patch, SdKey parameters seemed to patch PES successfully, but their modifications did not end up in the encoded DER.
(BTW, this does not fix any other errors that may still be present in the various SdKey subclasses, patches coming up.)
param_source: allow input val expansion like '0 * 32'
Working with keys, we often generate 4, 8, 16, 32 digit wide random values. Those then typically have default input values like
00000000000000000000000000000000
it is hard for humans to count the number of digits. Much easier:
00*16
Teach the ParamSource subclasses dealing with random values to understand an expansion like this. Any expansion is carried out before all other input value handling.
Use this expansion also in the default_value of ConfigurableParameter subclasses that have a default_source pointing at a ParamSource that now understand this expansion.
personalization: add get_typical_input_len() to ConfigurableParameter
The aim is to tell a user interface how wide an input text field should be chosen to be convenient -- ideally showing the entire value in all cases, but not too huge for fields that have no sane size limit.
personalization: indicate default ParamSource per ConfigurableParameter
Add default_source class members pointing to ParamSource classes to all ConfigurableParameter subclasses.
This is useful to automatically set up a default ParamSource for a given ConfigurableParameter subclass, during user interaction to produce a batch personalization.
For example, if the user selects a Pin1 parameter, a calling program can implicitly set this to a RandomDigitSource, which will magically make it work the way that most users need.
BTW, default_source and default_value can be combined to configure a matching ParamSource instance:
personalization: implement reading back values from a PES
Implement get_values_from_pes(), the reverse direction of apply_val(): read back and return values from a ProfileElementSequence. Implement for all ConfigurableParameter subclasses.
Future: SdKey.get_values_from_pes() is reading pe.decoded[], which works fine, but I07dfc378705eba1318e9e8652796cbde106c6a52 will change this implementation to use the higher level ProfileElementSD members.
Implementation detail:
Implement get_values_from_pes() as classmethod that returns a generator. Subclasses should yield all occurences of their parameter in a given PES.
For example, the ICCID can appear in multiple places. Iccid.get_values_from_pes() yields all of the individual values. A set() of the results quickly tells whether the PES is consistent.
Rationales for reading back values:
This allows auditing an eSIM profile, particularly for producing an output.csv from a batch personalization (that generated lots of random key material which now needs to be fed to an HLR...).
Reading back from a binary result is more reliable than storing the values that were fed into a personalization. By auditing final DER results with this code, I discovered: - "oh, there already was some key material in my UPP template." - "all IMSIs ended up the same, forgot to set up the parameter." - the SdKey.apply() implementations currently don't work, see I07dfc378705eba1318e9e8652796cbde106c6a52 for a fix.
Implement pySim.esim.saip.batch.BatchPersonalization, generating N eSIM profiles from a preset configuration.
Batch parameters can be fed by a constant, incrementing, random or from CSV rows: add pySim.esim.saip.param_source.* classes to feed such input to each of the BatchPersonalization's ConfigurableParameter instances.
We already have documentation that explains how to run pySim-smpp2sim. With smpp-ota-tool we now have a counterpart for pySim-smpp2sim, so let's add documentation for this tool as well.
We already have documentation that explains how to run pySim-smpp2sim. With smpp-ota-tool we now have a counterpart for pySim-smpp2sim, so let's add documentation for this tool as well.
contrib/smpp-ota-tool: define commandline arguments in global scope
The commandline arguments are currently defined under __main__ in a private scope. From there they are not reachable to the sphinx argparse module. We have to define the arguments globally at the top. (like in the other applications)
tests/pySim-smpp2sim_test: add testcases for AES128 and AES256
Extend the existing test script so that it can handle multiple testcases. Also add support for switching eUICC profiles. Finally, add a testcases to test OTA-SMS (RFM) with AES128 and AES256 encryption.
tests/pySim-smpp2sim_test: add testcases for AES128 and AES256
Extend the existing test script so that it can handle multiple testcases. Also add support for switching eUICC profiles. Finally, add a testcases to test OTA-SMS (RFM) with AES128 and AES256 encryption.
tests/pySim-smpp2sim_test: add testcases for AES128 and AES256
Extend the existing test script so that it can handle multiple testcases. Also add support for switching eUICC profiles. Finally, add a testcases to test OTA-SMS (RFM) with AES128 and AES256 encryption.
tests/pySim-smpp2sim_test: add testcases for AES128 and AES256
Extend the existing test script so that it can handle multiple testcases. Also add support for switching eUICC profiles. Finally, add a testcases to test OTA-SMS (RFM) with AES128 and AES256 encryption.
contrib/smpp-ota-tool: warn about mixed up KIC/KIC indexes
Cards usually have multiple sets of KIC, KID (and KIK). The keys are selected through an index. However, mixing keys from different sets is concidered as a security violation and cards should reject such configurations.
Let's print a warning to make users aware that something is off.
tests/pySim-smpp2sim_test: add testcases for AES128 and AES256
Extend the existing test script so that it can handle multiple testcases. Also add support for switching eUICC profiles. Finally, add a testcases to test OTA-SMS (RFM) with AES128 and AES256 encryption.
'securityDomain' elements are decoded to ProfileElementSD instances, which keep higher level representations of the key data apart from the decoded[] lists.
So far, apply_val() was dropping binary values in decoded[], which does not work, because ProfileElementSD._pre_encode() overwrites self.decoded[] from the higher level representation.
Implement using - ProfileElementSD.find_key() and SecurityDomainKeyComponent to modify an exsiting entry, or - ProfileElementSD.add_key() to create a new entry.
Before this patch, SdKey parameters seemed to patch PES successfully, but their modifications did not end up in the encoded DER.
(BTW, this does not fix any other errors that may still be present in the various SdKey subclasses, patches coming up.)
personalization: implement reading back values from a PES
Implement get_values_from_pes(), the reverse direction of apply_val(): read back and return values from a ProfileElementSequence. Implement for all ConfigurableParameter subclasses.
Future: SdKey.get_values_from_pes() is reading pe.decoded[], which works fine, but I07dfc378705eba1318e9e8652796cbde106c6a52 will change this implementation to use the higher level ProfileElementSD members.
Implementation detail:
Implement get_values_from_pes() as classmethod that returns a generator. Subclasses should yield all occurences of their parameter in a given PES.
For example, the ICCID can appear in multiple places. Iccid.get_values_from_pes() yields all of the individual values. A set() of the results quickly tells whether the PES is consistent.
Rationales for reading back values:
This allows auditing an eSIM profile, particularly for producing an output.csv from a batch personalization (that generated lots of random key material which now needs to be fed to an HLR...).
Reading back from a binary result is more reliable than storing the values that were fed into a personalization. By auditing final DER results with this code, I discovered: - "oh, there already was some key material in my UPP template." - "all IMSIs ended up the same, forgot to set up the parameter." - the SdKey.apply() implementations currently don't work, see I07dfc378705eba1318e9e8652796cbde106c6a52 for a fix.
The AlgorithmID has a few preset values, and hardly anyone knows which is which. So instead of entering '1', '2' or '3', make it work with prededined values 'Milenage', 'TUAK' and 'usim-test'.
Implement the enum value part abstractly in new EnumParam.
Make AlgorithmID a subclass of EnumParam and define the values as from pySim/esim/asn1/saip/PE_Definitions-3.3.1.asn
personalization: add get_typical_input_len() to ConfigurableParameter
The aim is to tell a user interface how wide an input text field should be chosen to be convenient -- ideally showing the entire value in all cases, but not too huge for fields that have no sane size limit.
Implement pySim.esim.saip.batch.BatchPersonalization, generating N eSIM profiles from a preset configuration.
Batch parameters can be fed by a constant, incrementing, random or from CSV rows: add pySim.esim.saip.param_source.* classes to feed such input to each of the BatchPersonalization's ConfigurableParameter instances.
personalization: indicate default ParamSource per ConfigurableParameter
Add default_source class members pointing to ParamSource classes to all ConfigurableParameter subclasses.
This is useful to automatically set up a default ParamSource for a given ConfigurableParameter subclass, during user interaction to produce a batch personalization.
For example, if the user selects a Pin1 parameter, a calling program can implicitly set this to a RandomDigitSource, which will magically make it work the way that most users need.
BTW, default_source and default_value can be combined to configure a matching ParamSource instance:
contrib/smpp-ota-tool: warn about mixed up KIC/KIC indexes
Cards usually have multiple sets of KIC, KID (and KIK). The keys are selected through an index. However, mixing keys from different sets is concidered as a security violation and cards should reject such configurations.
Let's print a warning to make users aware that something is off.
(normally KID index and KIC index should be the same since mixing keys is a concidered as a security violation. However, in this tool we want to allow users to specify different indexes for KIC and KIC so that they can make tests to make sure their cards correctly reject mixed up key indexes)
tests/pySim-smpp2sim_test: add testcases for AES128 and AES256
Extend the existing test script so that it can handle multiple testcases. Also add support for switching eUICC profiles. Finally, add a testcases to test OTA-SMS (RFM) with AES128 and AES256 encryption.
esim/http_json_api.py: support text/plain response Content-Type
Allow returning text/plain Content-Types as 'data' output argument.
So far, all the esim/http_json_api functions require a JSON response. However, a specific vendor has a list function where the request is JSON but the response is text/plain CSV data. Allow and return in a dict.
Main points/rationales of the refactoring, details below: 1) common validation implementation 2) offer classmethods
The new features are optional, and will be heavily used by batch personalization patches coming soon.
Implement Iccid and Imsi to use the new way, with a common abstract DecimalParam implementation.
So far leave the other parameter classes working as they always did, to follow suit in subsequent commits.
Details:
1) common validation implementation: There are very common validation steps in the various parameter implementations. It is more convenient and much more readable to implement those once and set simple validation parameters per subclass. So there now is a validate_val() classmethod, which subclasses can use as-is to apply the validation parameters -- or subclasses can override their cls.validate_val() for specialized validation. (Those subclasses that this patch doesn't touch still override the self.validate() instance method. Hence they still work as before this patch, but don't use the new common features yet.)
2) offer stateless classmethods: It is useful for... - batch processing of multiple profiles (in upcoming patches) and - user input validation to be able to have classmethods that do what self.validate() and self.apply() do, but do not modify any self.* members. So far the paradigm was to create a class instance to keep state about the value. This remains available, but in addition we make available the paradigm of a singleton that is stateless (the classmethods). Using self.validate() and self.apply() still work the same as before this patch, i.e. via self.input_value and self.value -- but in addition, there are now classmethods that don't touch self.* members.