Skip to content

Loading builds...

Changes

#2813 (Feb 15, 2026, 6:11:29 PM)

personalization: add param_source.py, add batch.py

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.

Related: SYS#6768
Change-Id: I497c60c101ea0eea980e8b1a4b1f36c0eda39002

rather move BatchPersonalization to separate module

Change-Id: I01ae40a06605eb205bfb409189fcd2b3a128855a
Neels Hofmeyr at

#2812 (Feb 15, 2026, 5:56:13 PM)

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:

  my_source = MyParam.default_source.from_str( MyParam.default_value )

Change-Id: Ie58d13bce3fa1aa2547cf3cee918c2f5b30a8b32
Neels Hofmeyr at

#2811 (Feb 15, 2026, 5:40:59 PM)

personalization: allow reading back multiple values from PES

Change-Id: Iecb68af7c216c6b9dc3add469564416b6f37f7b2
Neels Hofmeyr at

#2810 (Feb 15, 2026, 5:25:41 PM)

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.

Change-Id: I234fc4317f0bdc1a486f0cee4fa432c1dce9b463
Neels Hofmeyr at

#2809 (Feb 15, 2026, 5:09:58 PM)

MilenageRotationConstants: set example_input to 3GPP default

Change-Id: I36a9434b2f96d26d710f489d5afce1f0ef05bba1
Neels Hofmeyr at

#2808 (Feb 13, 2026, 2:44:57 PM)

pySim.esim.saip.File: Support pinStatusTemplateDO + lcsi

The tuples defining a DF or ADF in an eSIM template must contain a
pinStatusTemplateDO.  When parsing tuples into a File() instance, we
must save it, and re-create it at the time we re-encode that file.

Same applies to the lcsi (life cycle state indicator), which may
optionally exist for any file.

Change-Id: I073aa4374f2cd664d07fa0224bf0d4c809cdf4aa
Closes: OS#6955
laforge at

#2807 (Feb 10, 2026, 1:11:18 PM)

compile_asn1_subdir: filter compiled files by .asn suffix

When I open the .asn file in vim, pySim should not attempt to read the
vim .swp file as asn.1.

  File "/home/moi/osmo-dev/src/pysim/pySim/esim/saip/__init__.py", line 45, in <module>
    asn1 = compile_asn1_subdir('saip')
[...]
  File "<frozen codecs>", line 325, in decode
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xad in position 21: invalid start byte

Related: OS#6937
Change-Id: I37df3fc081e51e2ed2198876c63f6e68ecc8fcd8
Neels Hofmeyr at

#2806 (Feb 10, 2026, 12:43:28 PM)

esim/http_json_api: add alternative API interface (follow up)

This is a follow up patch to change:
I2a5d4b59b12e08d5eae7a1215814d3a69c8921f6

- do not ignore length of kwargs
- fix role parameter (roles other than 'legacy_client' can be used now)
- use startswith instead of match

Related: SYS#7866
Change-Id: Ifae13e82d671ff09bddf771f063a388d2ab283eb
pmaier@sysmocom.de at

#2805 (Feb 10, 2026, 10:36:26 AM)

pySim-shell_test/euicc: ensure test-profile is enabled

When testing commands like get_profile_info, enable_profile,
disable_profile or the commands to manage notifications, we
should ensure that the correct profile is enabled before
executing the actual testcase.

Change-Id: Ie57b0305876bc5001ab3a9c3a3b5711408161b74
pmaier@sysmocom.de at

#2804 (Feb 9, 2026, 1:36:02 PM)

pySim/euicc: fix encoding/decoding of Iccid

The class Iccid uses a BcdAdapter to encoded/decode the ICCID. This
works fine for ICCIDs that have an even (20) number of digits. In case
the digit count is odd (19), the ICCID the last digit requires padding.

Let's switch to PaddedBcdAdapter for encoding/decoding, to ensure that
odd-length ICCIDs are padded automatically.

Change-Id: I527a44ba454656a0d682ceb590eec6d9d0ac883a
Related: OS#6868
pmaier@sysmocom.de at

#2803 (Feb 9, 2026, 1:05:39 PM)

pySim-shell_test/euicc: ensure test-profile is enabled

When testing commands like get_profile_info, enable_profile,
disable_profile or the commands to manage notifications, we
should ensure that the correct profile is enabled before
executing the actual testcase.

Change-Id: Ie57b0305876bc5001ab3a9c3a3b5711408161b74
pmaier@sysmocom.de at

#2802 (Feb 9, 2026, 12:35:55 PM)

pySim/global_platform: replace deprecated argument group() creation

The global platform code used install_cap_parser.add_mutually_exclusive_group().add_argument_group()
But calling add_argument_group() on an exclusive group was never an intended feature nor
according to python docs was a working feature.

Change-Id: I4f73d3417a12b7fe94e33a265cdae244f3c9a1e9
lynxis at

#2801 (Feb 9, 2026, 12:15:08 PM)

pysim.utils.decomposeATR: Fix docutils warning

pySim/utils.py:docstring of pySim.utils.decomposeATR:9: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]

Change-Id: Ifda4ba15014ba97634fd5bd5c9b19d9110f4670e
laforge at

#2800 (Feb 9, 2026, 11:59:53 AM)

pySim.esim.saip: Fix docstring warnings:

this fixes the following two warnings:

pySim/esim/saip/__init__.py:docstring of pySim.esim.saip.FsNode.walk:1: WARNING: Inline strong start-string without end-string. [docutils]
pySim/esim/saip/__init__.py:docstring of pySim.esim.saip.FsNodeDF.walk:1: WARNING: Inline strong start-string without end-string. [docutils]

Change-Id: Id7debf9296923b735f76623808cee68967a1ece7
laforge at

#2799 (Feb 9, 2026, 11:44:37 AM)

pySim.esim.saip.personalization: Fix docstring errors + warnings

pysim/pySim/esim/saip/personalization.py:docstring of pySim.esim.saip.personalization.ConfigurableParameter:27: ERROR: Unexpected indentation. [docutils]
pysim/pySim/esim/saip/personalization.py:docstring of pySim.esim.saip.personalization.ConfigurableParameter:29: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]
pysim/pySim/esim/saip/personalization.py:docstring of pySim.esim.saip.personalization.ConfigurableParameter:34: ERROR: Unexpected indentation. [docutils]
pysim/pySim/esim/saip/personalization.py:docstring of pySim.esim.saip.personalization.ConfigurableParameter:35: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]
pysim/pySim/esim/saip/personalization.py:docstring of pySim.esim.saip.personalization.ConfigurableParameter:52: ERROR: Unexpected indentation. [docutils]
pysim/pySim/esim/saip/personalization.py:docstring of pySim.esim.saip.personalization.ConfigurableParameter:53: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]

Change-Id: I3918308856c3a1a5e6e90561c3e2a6b88040670d
laforge at

#2798 (Feb 9, 2026, 11:29:30 AM)

pySim.esim.saip.personalization: Fix docstring error

pySim/esim/saip/personalization.py:docstring of pySim.esim.saip.personalization.MilenageXoringConstants:4: ERROR: Unexpected indentation. [docutils]

Change-Id: If6ae360b7f74c095fa9075ae9aa988440496e6de
laforge at

#2797 (Feb 9, 2026, 11:00:48 AM)

pySim-shell_test/euicc: ensure test-profile is enabled

When testing commands like get_profile_info, enable_profile,
disable_profile or the commands to manage notifications, we
should ensure that the correct profile is enabled before
executing the actual testcase.

Change-Id: Ie57b0305876bc5001ab3a9c3a3b5711408161b74
pmaier@sysmocom.de at

#2796 (Feb 9, 2026, 10:43:58 AM)

pySim-shell_test/euicc: ensure test-profile is enabled

When testing commands like get_profile_info, enable_profile
or disable_profile, we should ensure that the correct profile
is enabled befor executing the actual testcase.

Change-Id: Ie57b0305876bc5001ab3a9c3a3b5711408161b74
pmaier@sysmocom.de at

#2795 (Feb 9, 2026, 10:20:43 AM)

pySim-shell_test/euicc: ensure test-profile is enabled

When testing the enable_profile / disable_profile commands we expect that
the profile we test with is enabled before the test begins. To be more
robust lets send an enable_profile command before the actual test begins.

Change-Id: Ie57b0305876bc5001ab3a9c3a3b5711408161b74
pmaier@sysmocom.de at

#2794 (Feb 9, 2026, 10:05:59 AM)

ModemATCommandLink: fix SyntaxWarning: invalid escape sequence '\+'

Change-Id: If8de5299a4dc5a8525ef6657213db95d30e3c83b
Fixes: OS#6948
Vadim Yanitskiy at

#2793 (Feb 9, 2026, 9:10:39 AM)

pySim-shell_test/euicc: fix testcase method name

We have two test_enable_disable_profile method, the second one should
be called test_set_nickname.

Change-Id: I5ff79218fdafc8c42c8b58cc00be3e56e09d808b
pmaier@sysmocom.de at

#2792 (Feb 7, 2026, 12:57:20 PM)

pySim/global_platform: replace deprecated argument group() creation

The global platform code used install_cap_parser.add_mutually_exclusive_group().add_argument_group()
But calling add_argument_group() on an exclusive group was never an intended feature nor
according to python docs was a working feature.

Change-Id: I4f73d3417a12b7fe94e33a265cdae244f3c9a1e9
lynxis at

#2791 (Feb 5, 2026, 5:13:52 PM)

pySim/global_platform: replace deprecated argument group() creation

The global platform code used install_cap_parser.add_mutually_exclusive_group().add_argument_group()
But calling add_argument_group() on an exclusive group was never an intended feature nor
according to python docs was a working feature.

Change-Id: I4f73d3417a12b7fe94e33a265cdae244f3c9a1e9
lynxis at

#2790 (Feb 5, 2026, 4:59:07 PM)

saip-tool: rename parser_tree correctly

parser_info is already defined and this seems to be a copy/paste
accident.

Change-Id: Icc30dbf02a266211fa4d3aee8e7cec14185e716c
lynxis at

#2789 (Feb 5, 2026, 1:31:04 PM)

euicc: extend get_profiles_info to retrieve all known tags

get_profiles_info only request for the default tag list, but
not all tags.
Add --all to the function to request for all known tags.

Change-Id: Ia6878519a480bd625bb1fa2567c1fd2e0e89b071
lynxis at

#2788 (Feb 5, 2026, 1:16:26 PM)

euicc: get_profiles_info: add additional tags

Add definitions for ProfileOwner (decoded),
Notification Configuration Info, SM-DP+ proprietary data,
Profile Policy Rules.

Change-Id: I727dbe34d87a42bb3b526bd7a8accd687d20a208
lynxis at

#2787 (Feb 4, 2026, 2:08:47 PM)

esim/http_json_api: add alternative API interface

unfortunately the API changes introduced in change

I277aa90fddb5171c4bf6c3436259aa371d30d092

broke the API interface of http_json_api.py. This was taken into
account and necessary to introduce add the server functionality next
to the already existing client functionality. The changes to the API
were minimal and all code locations that use http_json_api.py
were re-aligned.

Unfortunately it was not clear at this point in time that there are
out-of-tree projects that could be affected by API changes in
http_json_api.py

To mitigate the problem this patch introduces an alternative API
interface to the JsonHttpApiFunction base class. This alternative
API interface works like the old API interface when the class is
instantiated in the original way. To make use of the revised client
the API use has to pass an additional keyword argument that defines
the role.

Related: SYS#7866
Change-Id: I2a5d4b59b12e08d5eae7a1215814d3a69c8921f6
pmaier@sysmocom.de at

#2786 (Feb 4, 2026, 1:11:28 PM)

esim/http_json_api: add alternative API interface

unfortunately the API changes introduced in change

I277aa90fddb5171c4bf6c3436259aa371d30d092

broke the API interface of http_json_api.py. This was taken into
account and necessary to introduce add the server functionality next
to the already existing client functionality. The changes to the API
were minimal and all code locations that use http_json_api.py
were re-aligned.

Unfortunately it was not clear at this point in time that there are
out-of-tree projects that could be affected by API changes in
http_json_api.py

To mitigate the problem this patch introduces an alternative API
interface to the JsonHttpApiFunction base class. This alternative
API interface works like the old API interface when the class is
instantiated in the original way. To make use of the revised client
the API use has to pass an additional keyword argument that defines
the role.

Related: SYS#7866
Change-Id: I2a5d4b59b12e08d5eae7a1215814d3a69c8921f6
pmaier@sysmocom.de at

#2785 (Feb 4, 2026, 12:45:43 PM)

esim/http_json_api: add alternative API interface

unfortunately the API changes introduced in change

I277aa90fddb5171c4bf6c3436259aa371d30d092

broke the API interface of http_json_api.py. This was taken into
account and necessary to introduce add the server functionality next
to the already existing client functionality. The changes to the API
were minimal and all code locations that use http_json_api.py
were re-aligned.

Unfortunately it was not clear at this point in time that there are
out-of-tree projects that could be affected by API changes in
http_json_api.py

To mitigate the problem this patch introduces an alternative API
interface to the JsonHttpApiFunction base class. This alternative
API interface works like the old API interface when the class is
instantiated in the original way. To make use of the revised client
the API use has to pass an additional keyword argument that defines
the role.

Related: SYS#7866
Change-Id: I2a5d4b59b12e08d5eae7a1215814d3a69c8921f6
pmaier@sysmocom.de at

#2784 (Feb 4, 2026, 12:14:59 PM)

esim/http_json_api: add alternative API interface

unfortunately the API changes introduced in change

I277aa90fddb5171c4bf6c3436259aa371d30d092

broke the API interface of http_json_api.py. This was taken into
account and necessary to introduce add the server functionality next
to the already existing client functionality. The changes to the API
were minimal and all code locations that use http_json_api.py
were re-aligned.

Unfortunately it was not clear at this point in time that there are
out-of-tree projects that could be affected by API changes in
http_json_api.py

To mitigate the problem this patch introduces an alternative API
interface to the JsonHttpApiFunction base class. This alternative
API interface works like the old API interface when the class is
instantiated in the original way. To make use of the revised client
the API use has to pass an additional keyword argument that defines
the role.

Related: SYS#7866
Change-Id: I2a5d4b59b12e08d5eae7a1215814d3a69c8921f6
pmaier@sysmocom.de at

#2783 (Feb 4, 2026, 12:01:03 PM)

esim/http_json_api: add alternative API interface

unfortunately the API changes introduced in change

I277aa90fddb5171c4bf6c3436259aa371d30d092

broke the API interface of http_json_api.py. This was taken into
account and necessary to introduce add the server functionality next
to the already existing client functionality. The changes to the API
were minimal and all code locations that use http_json_api.py
were re-aligned.

Unfortunately it was not clear at this point in time that there are
out-of-tree projects that could be affected by API changes in
http_json_api.py

To mitigate the problem this patch introduces an alternative API
interface to the JsonHttpApiFunction base class. This alternative
API interface works like the old API interface when the class is
instantiated in the original way. To make use of the revised client
the API use has to pass an additional keyword argument that defines
the role.

Related: SYS#7866
Change-Id: I2a5d4b59b12e08d5eae7a1215814d3a69c8921f6
pmaier@sysmocom.de at

#2782 (Feb 4, 2026, 11:47:08 AM)

esim/http_json_api: add missing apidoc

Change-Id: Ibf9cf06197c9e3203c7a3ea5d77004f0ca41cd3f
pmaier@sysmocom.de at

#2781 (Feb 1, 2026, 5:35:17 PM)

compile_asn1_subdir: filter compiled files by .asn suffix

When I open the .asn file in vim, pySim should not attempt to read the
vim .swp file as asn.1.

  File "/home/moi/osmo-dev/src/pysim/pySim/esim/saip/__init__.py", line 45, in <module>
    asn1 = compile_asn1_subdir('saip')
[...]
  File "<frozen codecs>", line 325, in decode
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xad in position 21: invalid start byte

Related: OS#6937
Change-Id: I37df3fc081e51e2ed2198876c63f6e68ecc8fcd8
Neels Hofmeyr at

#2780 (Jan 31, 2026, 10:59:05 AM)

http_json_api: Only require Content-Type if response body is non-empty

If there is an empty body returned, such as in the case of the response
to an es9p notification, then it is of course also legal to not set the
content-type header.

This patch fixes an exception when talking to certain SM-DP+ with
es9p_client.py:

DEBUG:pySim.esim.http_json_api:HTTP RSP-STS: [204] hdr: {'X-Admin-Protocol': 'gsma/rsp/v2.5.0', 'Date': 'Wed, 28 Jan 2026 18:26:39 GMT', 'Server': 'REDACTED'}
DEBUG:pySim.esim.http_json_api:HTTP RSP: b''
{'X-Admin-Protocol': 'gsma/rsp/v2.5.0', 'Date': 'Wed, 28 Jan 2026 18:26:39 GMT', 'Server': 'REDACTED'}
<Response [204]>
Traceback (most recent call last):
  File "gprojects/git/pysim/es9p/../contrib/es9p_client.py", line 315, in <module>
    c.do_notification()
    ~~~~~~~~~~~~~~~~~^^
  File "projects/git/pysim/es9p/../contrib/es9p_client.py", line 159, in do_notification
    res = self.peer.call_handleNotification(data)
  File "projects/git/pysim/contrib/pySim/esim/es9p.py", line 174, in call_handleNotification
    return self.handleNotification.call(data)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "projects/git/pysim/contrib/pySim/esim/http_json_api.py", line 335, in call
    if not response.headers.get('Content-Type').startswith(req_headers['Content-Type']):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'startswith'

Change-Id: I99e8f167b7bb869c5ff6d908ba673dac87fef71a
laforge at

#2779 (Jan 31, 2026, 1:38:10 AM)

es9p_client: MAke install notification code execute at all

The caller specified 'install' but the do_notification() function
compared with 'download' :(

Change-Id: I2d441cfbc1457688eb163301d3d91a1f1fdc7a8c
laforge at

#2778 (Jan 31, 2026, 1:24:20 AM)

es9p_client: Fix type conversion in installation result notification

The asn.1 encoder expects bytes-like objects, we cannot simply pass
hex-strings to it without conversion

Change-Id: I83ad047e043dc6b3462b188ce6dd0b2cc0e52e87
laforge at

#2777 (Jan 29, 2026, 11:59:00 PM)

pySim-trace: pySim.apdu_source.stdin_hex

This introduces an "APDU source" for pySim-trace which enables the
decoding of APDUs that are copy+pasted from elsewhere, for example
APDU logs in text form created by proprietary tools, or to decode
personalization scripts or the like.

Change-Id: I5aacf13b7c27cea9efd42f01dacca61068c3aa33
laforge at

#2776 (Jan 29, 2026, 2:10:59 PM)

tests/pySim-smpp2sim_test: add integration test

At the moment pySim.ota codebase is not covered by any of the
integration tests (we have only normal unittests so far). To
increase the test coverage, let's add an integration test that
sends exchanges an RFM OTA-SMS with a real-world card.

However, there is no tool avaliable that can be used as an SMPP
client for pySim-smpp2sim yet. Let's use smpp_ota_apdu2.py on
laforge/ota to develop a tool that we can use to exchange SMS-TPDUs
that contain remote APDU scripts (RFM/RAM).

Finally let's use the tool we have created as a basis to create
an integration test that exchanges an SMS-TPDU with the RFM
application of a sysmoISIM-SJA5 card. The testcase shall pass
when we get the expected response from the card.

Related: OS#6868
Change-Id: If25e38be004cc1c7aeeb130431831377e78fe28d
pmaier@sysmocom.de at

#2775 (Jan 28, 2026, 11:03:29 PM)

es9p_client: Fix type conversion in installation result notification

The asn.1 encoder expects bytes-like objects, we cannot simply pass
hex-strings to it without conversion

Change-Id: I83ad047e043dc6b3462b188ce6dd0b2cc0e52e87
laforge at

#2774 (Jan 28, 2026, 10:49:39 PM)

es9p_client: MAke install notification code execute at all

The caller specified 'install' but the do_notification() function
compared with 'download' :(

Change-Id: I2d441cfbc1457688eb163301d3d91a1f1fdc7a8c
laforge at

#2773 (Jan 28, 2026, 10:35:48 PM)

es9p_client: smdpOid is optional in download/install notification

See the following definition in the ASN.1:

ProfileInstallationResultData ::= [39] SEQUENCE { -- Tag 'BF27'
    transactionId[0] TransactionId, -- The TransactionID generated by the SM-DP+
    notificationMetadata[47] NotificationMetadata,
    smdpOid OBJECT IDENTIFIER OPTIONAL, -- SM-DP+ OID (same value as in CERT.DPpb.ECDSA)
    finalResult [2] CHOICE {
        successResult SuccessResult,
        errorResult ErrorResult
    }
}

Change-Id: I5df54df2ddc0590a5eca31d4f37ae94053d20067
laforge at

#2772 (Jan 28, 2026, 4:07:30 PM)

pySim.esim.saip: Don't try to generate file contents for MF/DF/ADF

only EFs have data content

Change-Id: I02a54a3b2f73a0e9118db87f8b514d1dbf53971f
laforge at

#2771 (Jan 28, 2026, 3:53:35 PM)

contrib/csv-to-pgsql: add missing copyright header

Change-Id: Iad8b2c1abb6a80764d05c823fbd03a9eae0ec0ab
pmaier@sysmocom.de at

#2770 (Jan 28, 2026, 11:34:53 AM)

pySimLogger: user __name__ of the module when creating a new logger

At the moment we use random identifiers as names when we create a
new logger for pySimLogger. Let's switch to consistently use the
module name here. For the top level modules let's use the program
name so that it will show up in the log instead of __init__.

Change-Id: I49a9beb98845f66247edd42ed548980c97a7151a
pmaier@sysmocom.de at

#2769 (Jan 28, 2026, 11:20:55 AM)

transport/init: use PySimLogger to print messages

The module still uses print to output information. Let's replace
those print calls with the more modern PySimLogger method calls.

Change-Id: I2e2ec2b84f3b84dbd8a029ae9bb64b7a96ddbde3
pmaier@sysmocom.de at

#2768 (Jan 26, 2026, 9:01:40 PM)

pySim-trace: pySim.apdu_source.stdin_hex

This introduces an "APDU source" for pySim-trace which enables the
decoding of APDUs that are copy+pasted from elsewhere, for example
APDU logs in text form created by proprietary tools, or to decode
personalization scripts or the like.

Change-Id: I5aacf13b7c27cea9efd42f01dacca61068c3aa33
laforge at

#2767 (Jan 26, 2026, 8:47:46 PM)

pySim.esim.saip: Don't try to generate file contents for MF/DF/ADF

only EFs have data content

Change-Id: I02a54a3b2f73a0e9118db87f8b514d1dbf53971f
laforge at

#2766 (Jan 26, 2026, 8:33:52 PM)

pySim-trace: pySim.apdu_source.stdin_hex

This introduces an "APDU source" for pySim-trace which enables the
decoding of APDUs that are copy+pasted from elsewhere, for example
APDU logs in text form created by proprietary tools, or to decode
personalization scripts or the like.

Change-Id: I5aacf13b7c27cea9efd42f01dacca61068c3aa33
laforge at

#2765 (Jan 26, 2026, 8:20:04 PM)

pySim.esim.saip: Implement optimized file content encoding

Make sure we make use of the fill pattern when encoding file contents:
Only encode the differences to the fill pattern of the file, in order
to reduce the profile download size.

Change-Id: I61e4a5e04beba5c9092979fc546292d5ef3d7aad
laforge at

#2764 (Jan 26, 2026, 4:33:55 PM)

pySimLogger: user __name__ of the module when creating a new logger

At the moment we use random identifiers as names when we create a
new logger for pySimLogger. Let's switch to consistently use the
module name here. For the top level modules let's use the program
name so that it will show up in the log instead of __init__.

Change-Id: I49a9beb98845f66247edd42ed548980c97a7151a
pmaier@sysmocom.de at

#2763 (Jan 26, 2026, 4:20:08 PM)

transport/init: use PySimLogger to print messages

The module still uses print to output information. Let's replace
those print calls with the more modern PySimLogger method calls.

Change-Id: I2e2ec2b84f3b84dbd8a029ae9bb64b7a96ddbde3
pmaier@sysmocom.de at

#2762 (Jan 23, 2026, 6:37:45 AM)

personalization: refactor SmspTpScAddr

Refactor SmspTpScAddr to the new ConfigurableParameter implementation
style.

Change-Id: I2600369e195e9f5aed7f4e6ff99ae273ed3ab3bf
Neels Hofmeyr at

#2761 (Jan 23, 2026, 6:28:12 AM)

generate sdkey classes from a list

Change-Id: Ic92ddea6e1fad8167ea75baf78ffc3eb419838c4
Neels Hofmeyr at

#2760 (Jan 23, 2026, 6:14:25 AM)

personalization: add param_source.py, implement batch personalization

Implement pySim.esim.saip.personalization.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.

Related: SYS#6768
Change-Id: I497c60c101ea0eea980e8b1a4b1f36c0eda39002
Neels Hofmeyr at

#2759 (Jan 23, 2026, 6:05:03 AM)

transitional name mapping

Change-Id: I974cb6c393a2ed2248a6240c2722d157e9235c33
Neels Hofmeyr at

#2758 (Jan 23, 2026, 6:01:08 AM)

saip/param_source: try to not repeat random values

Change-Id: I4fa743ef5677580f94b9df16a5051d1d178edeb0
Neels Hofmeyr at

#2757 (Jan 23, 2026, 5:47:23 AM)

personalization: refactor SdKey

Refactor SdKey (and subclasses) to the new ConfigurableParameter
implementation style, keeping the same implementation.

But duly note that this implementation does not work!
It correctly patches pe.decoded[], but that gets overridden by
ProfileElementSD._pre_encode().

For a fix, see I07dfc378705eba1318e9e8652796cbde106c6a52.

Change-Id: I427ea851bfa28b2b045e70a19a9e35d361f0d393
Neels Hofmeyr at

#2756 (Jan 23, 2026, 5:37:53 AM)

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

Change-Id: Ic436e206776b81f24de126e8ee0ae8bf5f3e8d7a
Neels Hofmeyr at

#2755 (Jan 23, 2026, 5:22:30 AM)

use random.SystemRandom as random nr source (/dev/urandom)

/dev/urandom is somewhat better than python's PRNG

Change-Id: I6de38c14ac6dd55bc84d53974192509c18d02bfa
Neels Hofmeyr at

#2754 (Jan 23, 2026, 5:07:52 AM)

add test_configurable_parameters.py

Change-Id: Ia55f0d11f8197ca15a948a83a34b3488acf1a0b4
Neels Hofmeyr at

#2753 (Jan 23, 2026, 4:54:06 AM)

personalization: set some typical parameter names

These names better match what humans expect to read, for example "PIN1"
instead of "Pin1".

(We still fall back to the __class__.__name__ if a subclass omits a
specific name, see the ConfigurableParameter init.)

Change-Id: I31f390d634e58c384589c50a33ca45d6f86d4e10
Neels Hofmeyr at

#2752 (Jan 23, 2026, 4:40:10 AM)

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:

  my_source = MyParam.default_source.from_str( MyParam.default_value )

Change-Id: Ie58d13bce3fa1aa2547cf3cee918c2f5b30a8b32
Neels Hofmeyr at

#2751 (Jan 23, 2026, 4:26:15 AM)

personalization: fix SdKey.apply_val() implementation

'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.)

Related: SYS#6768
Change-Id: I07dfc378705eba1318e9e8652796cbde106c6a52
Neels Hofmeyr at

#2750 (Jan 23, 2026, 4:12:22 AM)

personalization: refactor ConfigurableParameter, Iccid, Imsi

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.

Related: SYS#6768
Change-Id: I6522be4c463e34897ca9bff2309b3706a88b3ce8
Neels Hofmeyr at

#2749 (Jan 23, 2026, 3:58:35 AM)

rather move BatchPersonalization to separate module

Change-Id: I01ae40a06605eb205bfb409189fcd2b3a128855a
Neels Hofmeyr at

#2748 (Jan 23, 2026, 3:44:49 AM)

personalization: make AlgorithmID a new EnumParam

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

Related: SYS#6768
Change-Id: I71c2ec1b753c66cb577436944634f32792353240
Neels Hofmeyr at

#2747 (Jan 23, 2026, 3:29:41 AM)

param_source: allow plugging a random implementation (for testing)

Change-Id: Idce2b18af70c17844d6f09f7704efc869456ac39
Neels Hofmeyr at

#2746 (Jan 23, 2026, 3:15:54 AM)

personalization: implement UppAudit and BatchAudit

Change-Id: Iaab336ca91b483ecdddd5c6c8e08dc475dc6bd0a
Neels Hofmeyr at

#2745 (Jan 23, 2026, 3:02:01 AM)

personalization: refactor Puk

Implement abstract DecimalHexParam, and use it to refactor Puk1 and Puk2
to the new ConfigurableParameter implementation style.

DecimalHexParam will also be used for Pin and Adm soon.

Change-Id: I271e6c030c890778ab7af9ab3bc7997e22018f6a
Neels Hofmeyr at

#2744 (Jan 23, 2026, 2:33:56 AM)

comment in uicc.py on Security Domain Keys: add SCP81

Change-Id: Ib0205880f58e78c07688b4637abd5f67ea0570d1
Neels Hofmeyr at

#2743 (Jan 23, 2026, 2:19:07 AM)

personalization.ConfigurableParameter: fix BytesIO() input

Change-Id: I0ad160eef9015e76eef10baee7c6b606fe249123
Neels Hofmeyr at

#2742 (Jan 23, 2026, 2:05:20 AM)

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.

Change-Id: I234fc4317f0bdc1a486f0cee4fa432c1dce9b463
Neels Hofmeyr at

#2741 (Jan 23, 2026, 1:51:37 AM)

improve Imsi,Iccid.get_values_from_pes()

Change-Id: I40c888e81fc3172d5abbd5fc49029e5f7cf720ab
Neels Hofmeyr at

#2740 (Jan 23, 2026, 1:36:23 AM)

RandomHexDigitSource: rather return in string format, not bytes

Change-Id: I4e86289f6fb72cbd4cf0c90b8b49538cfab69a7f
Neels Hofmeyr at

#2739 (Jan 23, 2026, 1:22:27 AM)

personalization: set example input values

For all ConfigurableParameter subclasses, provide an example_input.

This may be useful for downstream projects' user interaction, to suggest
a value or prefill an input field, as appropriate.

Related: SYS#6768
Change-Id: I2672fedcbc32cb7a6cb0c233a4a22112bd9aae03
Neels Hofmeyr at

#2738 (Jan 23, 2026, 1:07:08 AM)

use secrets.SystemRandom as secure random nr source

secrets.SystemRandom is defined as the most secure random source
available on the given operating system.

Change-Id: I8049cd1292674b3ced82b0926569128535af6efe
Neels Hofmeyr at

#2737 (Jan 23, 2026, 12:44:02 AM)

add test_param_src.py

Change-Id: I03087b84030fddae98b965e0075d44e04ec6ba5c
Neels Hofmeyr at

#2736 (Jan 23, 2026, 12:30:16 AM)

ConfigurableParameter: do not magically overwrite the 'name' attribute

Change-Id: I6f631444c6addeb7ccc5f6c55b9be3dc83409169
Neels Hofmeyr at

#2735 (Jan 23, 2026, 12:16:28 AM)

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.

Related: SYS#6768
Change-Id: Ie7171c152a7b478736f8825050305606b5af5735
Neels Hofmeyr at

#2734 (Jan 23, 2026, 12:02:47 AM)

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.

Change-Id: I2568a032167a10517d4d75d8076a747be6e21890
Neels Hofmeyr at

#2733 (Jan 22, 2026, 11:47:36 PM)

personalization: add int as input type for BinaryParameter

Change-Id: I31d8142cb0847a8b291f8dc614d57cb4734f0190
Neels Hofmeyr at

#2731 (Jan 22, 2026, 11:19:37 PM)

personalization: allow reading back multiple values from PES

Change-Id: Iecb68af7c216c6b9dc3add469564416b6f37f7b2
Neels Hofmeyr at

#2730 (Jan 22, 2026, 11:05:46 PM)

personalization: refactor Pin, Adm

Refactor Pin1, Pin2, Adm1 and Adm2 to the new ConfigurableParameter
implementation style.

Change-Id: I54aef10b6d4309398d4b779a3740a7d706d68603
Neels Hofmeyr at

#2729 (Jan 22, 2026, 10:51:54 PM)

personalization: refactor AlgorithmID, K, Opc

Refactor AlgorithmID, K, Opc to the new ConfigurableParameter
implementation style.

K and Opc use a common abstract BinaryParam.

Note from the future: AlgorithmID so far takes "raw" int values, but
will turn to be an "enum" parameter with predefined meaningful strings
in I71c2ec1b753c66cb577436944634f32792353240

Change-Id: I6296fdcfd5d2ed313c4aade57ff43cc362375848
Neels Hofmeyr at

#2728 (Jan 22, 2026, 9:56:07 PM)

gitignore: fix vim swp file pattern

Change-Id: I5a8351dc09f6ca7c8e9032ff8352e5cf1a4833a3
Neels Hofmeyr at

#2727 (Jan 22, 2026, 6:53:15 PM)

esim/http_json_api: extend JSON API with server functionality

At the moment http_json_api only supports the client role. Let's also add
support for the server role.

This patch refactors the existing client code. This in particular means
that the following preperations have to be made:

- To use the existing JsonHttpApiFunction definitions in the client and
  server the scheme has to be symetric. It already is for the most part,
  but it treads the header field differently. So let's just treat the
  header field like any other mandatory field and add it input_params.
  (this does not affect the es9p.py code since in ES9+ the requests have
   no header messages, see also SGP.22, section 6.5.1.1)

- The JsonHttpApiFunction class currently also has the code to perform
  the client requests. Let's seperate that code in a JsonHttpApiClient
  class to which we pass an JsonHttpApiFunction object.

- The code that does the encoding and decoding in the client role has
  lots of conditions the treat the header differently. Let's do the
  decisions about the header in the JsonHttpApiClient. The encoder
  and decoder function should do the generic encoding and decoding
  only. (however, some generic header specific conditions will remain).

The code for the server role logically mirrors the code for the client
role. We add a JsonHttpApiServer class that can be used to create
API endpoints. The API user has to pass in a call_handler through which
the application logic is defined. Above that we add an Es2pApiServer
class in es2p. In this class we implement the logic that runs the
HTTP server and receives the requests. The Es2pApiServer supports all
ES2+ functions defined by GSMA SGP.22. The user may use the provided
Es2pApiServerHandler base class to define the application logic for each
ES2+ function.

Related: SYS#7825
Change-Id: I277aa90fddb5171c4bf6c3436259aa371d30d092
pmaier@sysmocom.de at

#2726 (Jan 22, 2026, 6:39:24 PM)

contrib: add utility to receive ES2+handleDownloadProgressInfo calls

We already have a tool to work with the ES2+ API provided by an SMDP+
(es2p_client.py) With this tool we can only make API calls towards
an SMDP+. However, SGP.22 also defines a "reverse direction" ES2+
interface through wich the SMDP+ may make API calls towards the MNO.

At the moment the only possible MNO originated API call is
ES2+handleDownloadProgressInfo. Let's add a simple tool that runs a
HTTP server to receive and log the ES2+handleDownloadProgressInfo
requests.

Related: SYS#7825
Change-Id: I95af30cebae31f7dc682617b1866f4a2dc9b760c
pmaier@sysmocom.de at

#2725 (Jan 22, 2026, 6:22:11 PM)

contrib: add utility to receive ES2+handleDownloadProgressInfo calls

We already have a tool to work with the ES2+ API provided by an SMDP+
(es2p_client.py) With this tool we can only make API calls towards
an SMDP+. However, SGP.22 also defines a "reverse direction" ES2+
interface through wich the SMDP+ may make API calls towards the MNO.

At the moment the only possible MNO originated API call is
ES2+handleDownloadProgressInfo. Let's add a simple tool that runs a
HTTP server to receive and log the ES2+handleDownloadProgressInfo
requests.

Related: SYS#7825
Change-Id: I95af30cebae31f7dc682617b1866f4a2dc9b760c
pmaier@sysmocom.de at

#2724 (Jan 22, 2026, 6:08:13 PM)

esim/http_json_api: extend JSON API with server functionality

At the moment http_json_api only supports the client role. Let's also add
support for the server role.

This patch refactors the existing client code. This in particular means
that the following preperations have to be made:

- To use the existing JsonHttpApiFunction definitions in the client and
  server the scheme has to be symetric. It already is for the most part,
  but it treads the header field differently. So let's just treat the
  header field like any other mandatory field and add it input_params.
  (this does not affect the es9p.py code since in ES9+ the requests have
   no header messages, see also SGP.22, section 6.5.1.1)

- The JsonHttpApiFunction class currently also has the code to perform
  the client requests. Let's seperate that code in a JsonHttpApiClient
  class to which we pass an JsonHttpApiFunction object.

- The code that does the encoding and decoding in the client role has
  lots of conditions the treat the header differently. Let's do the
  decisions about the header in the JsonHttpApiClient. The encoder
  and decoder function should do the generic encoding and decoding
  only. (however, some generic header specific conditions will remain).

The code for the server role logically mirrors the code for the client
role. We add a JsonHttpApiServer class that can be used to create
API endpoints. The API user has to pass in a call_handler through which
the application logic is defined. Above that we add an Es2pApiServer
class in es2p. In this class we implement the logic that runs the
HTTP server and receives the requests. The Es2pApiServer supports all
ES2+ functions defined by GSMA SGP.22. The user may use the provided
Es2pApiServerHandler base class to define the application logic for each
ES2+ function.

Related: SYS#7825
Change-Id: I277aa90fddb5171c4bf6c3436259aa371d30d092
pmaier@sysmocom.de at

#2723 (Jan 22, 2026, 5:54:20 PM)

esim/http_json_api: add missing check

The line actual_sec = func_ex_status.get('statusCodeData', None) suggests
that 'statusCodeData' may be None under normal circumstances. So let's guard
sec.update(actual_sec) so that we won't run into an exception in case
'statusCodeData' is not in func_ex_status.

Related: SYS#7825
Change-Id: I8a1a3cd5e029dba4a3aec1a64702e19b0d694ae2
pmaier@sysmocom.de at

#2722 (Jan 21, 2026, 6:48:40 PM)

Decode GSM 7-bit packed EF.PNN data

Change-Id: I4558fd011592aeeae2389fe8a1f62f3d7e21d219
biofel at

#2721 (Jan 21, 2026, 1:07:09 PM)

pySim-trace: pySim.apdu_source.stdin_hex

This introduces an "APDU source" for pySim-trace which enables the
decoding of APDUs that are copy+pasted from elsewhere, for example
APDU logs in text form created by proprietary tools, or to decode
personalization scripts or the like.

Change-Id: I5aacf13b7c27cea9efd42f01dacca61068c3aa33
laforge at

#2720 (Jan 21, 2026, 7:52:36 AM)

pySim.apdu_source.stdin_hex

Change-Id: I5aacf13b7c27cea9efd42f01dacca61068c3aa33
laforge at

#2719 (Jan 21, 2026, 7:24:38 AM)

pySim.esim.saip: Don't try to generate file contents for MF/DF/ADF

only EFs have data content

Change-Id: I02a54a3b2f73a0e9118db87f8b514d1dbf53971f
laforge at

#2718 (Jan 21, 2026, 7:10:39 AM)

pySim.esim.saip: Implement optimized file content encoding

Make sure we make use of the fill pattern when encoding file contents:
Only encode the differences to the fill pattern of the file, in order
to reduce the profile download size.

Change-Id: I61e4a5e04beba5c9092979fc546292d5ef3d7aad
laforge at

#2717 (Jan 21, 2026, 6:56:44 AM)

Fix esim.saip.ProfileElementSequence.remove_naas_of_type

This method did not work at all at the moment, likely due to API churn
over time.  This change makes the following exception go away:

Traceback (most recent call last):
  File "projects/git/pysim/contrib/saip-tool.py", line 473, in <module>
    do_remove_naa(pes, opts)
    ~~~~~~~~~~~~~^^^^^^^^^^^
  File "projects/git/pysim/contrib/saip-tool.py", line 203, in do_remove_naa
    pes.remove_naas_of_type(naa)
    ~~~~~~~~~~~~~~~~~~~~~~~^^^^^
  File "projects/git/pysim/contrib/pySim/esim/saip/__init__.py", line 1748, in remove_naas_of_type
    if template in hdr.decoded['eUICC-Mandatory-GFSTEList']:
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "projects/git/pysim/contrib/pySim/esim/saip/oid.py", line 48, in __eq__
    return (self.intlist == other.intlist)
                            ^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'intlist'

A subsequent patch should introduce unit tests to avoid such breakage in
the future.

Change-Id: I88d862d751198c3d1648ab7f11d6e6a8fdbc41c9
laforge at

#2716 (Jan 21, 2026, 6:42:49 AM)

saip.validation: Verify unused mandatory services in header

This adds a new check method to the pySim.esim.saip.validation.CheckBasicStructure
class, which ensures that no unused authentication algorithm related mandatory
services are indicated in the ProfileHeader.

So if a profile e.g. states in the header it requires
usim-test-algorithm, but then the actual akaParameter instances do not
actually use that algorithm, it would raise an exception.

Change-Id: Id0e1988ae1936a321d04bc7c3c3a33262c767d30
Related: SYS#7826
laforge at

#2715 (Jan 21, 2026, 6:28:44 AM)

pySim.esim.saip: Implement optimized file content encoding

Make sure we make use of the fill pattern when encoding file contents:
Only encode the differences to the fill pattern of the file, in order
to reduce the profile download size.

Change-Id: I61e4a5e04beba5c9092979fc546292d5ef3d7aad
laforge at

#2714 (Jan 21, 2026, 2:36:41 AM)

personalization: refactor ConfigurableParameter, Iccid, Imsi

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.

Related: SYS#6768
Change-Id: I6522be4c463e34897ca9bff2309b3706a88b3ce8
Neels Hofmeyr at

#2713 (Jan 20, 2026, 1:22:00 PM)

transport/init: use PySimLogger to print messages

The module still uses print to output information. Let's replace
those print calls with the more modern PySimLogger method calls.

Change-Id: I2e2ec2b84f3b84dbd8a029ae9bb64b7a96ddbde3
pmaier@sysmocom.de at

#2712 (Jan 20, 2026, 1:07:51 PM)

pySimLogger: user __name__ of the module when creating a new logger

At the moment we use random identifiers as names when we create a
new logger for pySimLogger. Let's switch to consistently use the
module name here.

Change-Id: I49a9beb98845f66247edd42ed548980c97a7151a
pmaier@sysmocom.de at

#2711 (Jan 20, 2026, 9:24:37 AM)

setup.py: Fix missing pySim.esim.saip module

installing the pySim package was missing the pySim.esim.saip
sub-module[s].

Change-Id: I5fd30c0ac38003bbc336add9883fa1bbd4b737b2
laforge at

#2710 (Jan 19, 2026, 5:59:07 PM)

pySim/card_key_provider: fix typo in keys

Change-Id: Ie76f351ae221da2a0aab65c311fafe8ae6d63663
lynxis at

#2709 (Jan 19, 2026, 5:36:26 PM)

contrib: add utility to receive ES2+handleDownloadProgressInfo calls

We already have a tool to work with the ES2+ API provided by an SMDP+
(es2p_client.py) With this tool we can only make API calls towards
an SMDP+. However, SGP.22 also defines a "reverse direction" ES2+
interface through wich the SMDP+ may make API calls towards the MNO.

At the moment the only possible MNO originated API call is
ES2+handleDownloadProgressInfo. Let's add a simple tool that runs a
HTTP server to receive and log the ES2+handleDownloadProgressInfo
requests.

Related: SYS#7825
Change-Id: I95af30cebae31f7dc682617b1866f4a2dc9b760c
pmaier@sysmocom.de at

#2708 (Jan 19, 2026, 5:22:27 PM)

esim/http_json_api: extend JSON API with server functionality

At the moment http_json_api only supports the client role. Let's also add
support for the server role.

This patch refactors the existing client code. This in particular means
that the following preperations have to be made:

- To use the existing JsonHttpApiFunction definitions in the client and
  server the scheme has to be symetric. It already is for the most part,
  but it treads the header field differently. So let's just tread the
  header field like any other mandatory field and add it input_params.
  (this does not affect the es9p.py code since in ES9+ the requests have
   no header messages, see also SGP.22, section 6.5.1.1)

- The JsonHttpApiFunction class currently also has the code to perform
  the client requests. Let's seperate that code in a JsonHttpApiClient
  class to which we pass an JsonHttpApiFunction object.

- The code that does the encoding and decoding in the client role has
  lots of conditions the tread the header differently. Let's do the
  decisions about the header in the JsonHttpApiClient. The encoder
  and decoder function should do the generic encoding and decoding
  only. (however, some generic header specific conditions will remain).

The code for the server role logically mirrors the code for the client
role. We add a JsonHttpApiServer class that can be used to create
API endpoints. The API user has to pass in a call_handler through which
the application logic is defined. Above that we add an Es2pApiServer
class in es2p. In this class we implement the logic that runs the
HTTP server and receives the requests. The Es2pApiServer supports all
ES2+ functions defined by GSMA SGP.22. The user may use the provided
Es2pApiServerHandler base class to define the application logic for each
ES2+ function.

Related: SYS#7825
Change-Id: I277aa90fddb5171c4bf6c3436259aa371d30d092
pmaier@sysmocom.de at

#2707 (Jan 19, 2026, 5:08:28 PM)

esim/http_json_api: add missing check

The line actual_sec = func_ex_status.get('statusCodeData', None) suggests
that 'statusCodeData' may be None under normal circumstances. So let's guard
sec.update(actual_sec) so that we won't run into an exception in case
'statusCodeData' is not in func_ex_status.

Related: SYS#7825
Change-Id: I8a1a3cd5e029dba4a3aec1a64702e19b0d694ae2
pmaier@sysmocom.de at

#2706 (Jan 19, 2026, 2:32:38 PM)

Print SMSC in pySim-read.py

Change-Id: I17067b68086316d51fd71ba77049874605594e3f
phcoder at

#2142 (Mar 10, 2025, 1:01:16 PM)

[2/7] personalization: refactor ConfigurableParameter, Iccid, Imsi

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.

Related: SYS#6768
Change-Id: I6522be4c463e34897ca9bff2309b3706a88b3ce8
Neels Hofmeyr at