51 Commits

Author SHA1 Message Date
Vadim Yanitskiy
da2fa83256 pySim-shell.py: add a command for RUN GSM ALGORITHM
Change-Id: Id7876d83d018aca79253784411d3a9d54a249a0a
2022-04-22 15:54:00 +03:00
Vadim Yanitskiy
6865819859 SimCardCommands.run_gsm(): use send_apdu_checksw()
Change-Id: Ib713cf8154a3aba72bc5776a8d99ec47631ade28
2022-04-22 15:54:00 +03:00
Vadim Yanitskiy
a576764c5e fix SimCardCommands.run_gsm(): always use CLA=0xa0
Depending on the card type (SIM or USIM/ISUM), self.cla_byte may
be either 0xa0 or 0x00.  Sending RUN GSM ALGORITHM with CLA=0x00
fails with SW=6985 (Command not allowed), so let's make sure
that we always use CLA=0xa0 regardless of the card type.

Change-Id: Ia0abba136dbd4cdea8dbbc3c4d6abe12c2863680
2022-04-22 15:54:00 +03:00
Harald Welte
c30bed235e ts_102_221: Add encode/write support of EF.ARR records
With this change, we can also encode/write EF.ARR records, not just
decode/read.

Change-Id: Id0da2b474d05aba12136b9cae402ad8326700182
2022-04-05 14:45:18 +02:00
Harald Welte
0dcdfbfe94 utils: Add DataObjectSequence.encode_multi()
This is the analogous to the decode_multi() method.

Change-Id: Ifdd1b1bd4d67f447638858c3e92742ca6f884bfa
2022-04-05 14:42:48 +02:00
Harald Welte
785d484709 utils: Fix bugs in DataObject encoders
The DataObject is some weird / rarely used different code than the
normal TLV encoder/decoder.  It has apparently so far only been used
for decoding, without testing the encoding side, resulting in related
bugs.

Let's fix those that I encountered today, and add a test case.

Change-Id: I31370066f43c22fc3ce9e2b9ee75986a652f6fc4
2022-04-05 14:33:00 +02:00
æstrid smith
b7f35ac163 ts_31_103: Correct file-id of EF.DOMAIN in ADF.ISIM
While the short ID of this file is 05, the actual file-id is 6f03.
Reference to TS 31.103 section 4.2.3.

Change-Id: Idd572ab064ea38e74dffd583c27ea505b23214a2
2022-03-27 10:43:38 +00:00
Harald Welte
ab91d874e4 ts_31_102: Avoid pylint false positive
This should avoid the following pylint error:

************* Module pySim.ts_31_102
pySim/ts_31_102.py:621:100: E0601: Using variable 'sw' before assignment (used-before-assignment)

Change-Id: I0bb9607cdab0e6e3cd17b4d27129a51a607bc0f2
2022-03-27 12:33:55 +02:00
Harald Welte
aefd0649a2 pySim-shell: Add 'decode_hex' command for transparent + linear EF
These commands can be used to decode a user-provided hex-string,
instead of decoding the data read from the file.  This is useful
for quickly manually decoding some values read from other locations,
such as e.g. copy+pasted from a eSIM profile in ASN.1 value notation.

Change-Id: I81f73bce2c26e3e5dfc7538d223bb2d2483c7fa0
2022-03-01 16:48:22 +00:00
Harald Welte
34eb504b3b Initial support for GlobalPlatform
One can now select the Issuer Security Domain (hard-coded to
a000000003000000) and issue get_data requests.  FCI and other TLV
objects are dcoded, e.g.

pySIM-shell (MF)> select ADF.ISD
{
    "application_id": "a000000003000000",
    "proprietary_data": {
        "maximum_length_of_data_field_in_command_message": 255
    }
}
pySIM-shell (MF/ADF.ISD)> get_data CardData
{
    "card_data": [
        {
            "card_recognition_data": [
                {
                    "object_identifier": "2a864886fc6b01"
                },
                {
                    "card_management_type_and_version": [
                        {
                            "object_identifier": "2a864886fc6b02020101"
                        }
                    ]
                },
                {
                    "card_identification_scheme": [
                        {
                            "object_identifier": "2a864886fc6b03"
                        }
                    ]
                },
                {
                    "secure_channel_protocol_of_isd": [
                        {
                            "object_identifier": "2a864886fc6b040215"
                        }
                    ]
                }
            ]
        }
    ]
}

Change-Id: If11267d45ab7aa371eea8c143abd9320c32b54d0
2022-03-01 16:32:15 +00:00
Harald Welte
a037762b04 ts_31_102: Further decode TAI in EF.OPL5G
The TAI is not just an opaque bytestring but it consists of 3 fields.

Change-Id: Ie5a5ce74713deb0e151218ae553d3f3d96cef17d
2022-02-25 15:45:09 +01:00
Harald Welte
3a5afff022 ts_31_102: Further decode LAI in EF_LOCI
Change-Id: I21d9356e541eb320848a373804781ae0bef7d012
2022-02-25 15:45:02 +01:00
Harald Welte
1459e45005 ts_51_011: Better decode of EF_OPL LAI
before:
{
    "lai": "62f2300000fffe",
    "pnn_record_id": 1
}

after:
{
    "lai": {
        "mcc_mnc": "262f03",
        "lac_min": "0000",
        "lac_max": "fffe"
    },
    "pnn_record_id": 1
}

Change-Id: I82581220e9c33a8e67cbefd5dfeb40bbc2c31179
2022-02-25 15:44:26 +01:00
Harald Welte
22a1cdde25 ts_51_011: Properly decode EF.OPL
The OPL has 7 bytes "LAI" as the LAI actually contains a LAC
range (so two more bytes for the end of the 16bit range).

Change-Id: I74bcf10b0a8977af0f2844044a812c5780af1706
2022-02-25 15:31:16 +01:00
Harald Welte
dd45d8ee3b ts_31_102: Fix decoding of UServiceTable
range(0,7) in python is 0..6, and not 0..7, so we need range(0.8)
to produce the desired range covering all bits of a byte.

This resulted in services 8,16,24,... not being displayed in
the decoded output of EF.UST / EF.IST.

Change-Id: I22bbc481de342685352bf5b13d54931d3f37f9b7
2022-02-25 15:31:16 +01:00
Harald Welte
4ebeebffca ts_102_221: Fix decoding the 'num_of_rec' field
It is a 8bit integer, not a 16bit integer.  See TS 102 221 11.1.1.4.3

Change-Id: I3e258547dad21a248650cfbc02e0576268d3b3fd
2022-02-25 09:48:20 +01:00
Harald Welte
5e9bd93bbd ts_102_221: properly decode short file identifier
The SFI TLV contanins not the raw SFI, but it contains the SFI
shifted to left by 3 bits (for some strange reason).  So let's
un-shift it.

Change-Id: Ibc69b99010d2a25cbb69b6a3d1585d0cb63f1345
2022-02-25 09:37:40 +01:00
Harald Welte
fa578bd601 add scripts/deactivate-ims.script to deactivate IMS related services
Change-Id: I0cd93c8fa0024dd9d93647c565190abe94d3097e
2022-02-21 09:57:09 +01:00
Harald Welte
c89a1a99ca Add scripts/deacivate-5g.script
This script can be used to deactivate all 5G related services and files.

Change-Id: I5dc3e9f0ae76a7ae57484e5a3369e11ff02c7eca
2022-02-17 12:42:14 +01:00
Harald Welte
12af793d4b doc: Improve documentation in various places
* don't duplicate information between .rst files and docstrings
* if there's more than a trivial single-line documentation, put it as
  docstring into the python source and use ".. argparse" to pul it into
  the manual
* add documentation for some commands for which it was missing
* show one level deeper in the navigation table, listing the commands

Change-Id: Ib88bb7d12faaac7d149ee1f6379bc128b83bbdd5
2022-02-15 16:40:45 +01:00
Harald Welte
d01bd3632c docs: Document missing 'status' command in 7816 section
Change-Id: I9af85a36bc4f24c3a22b9b2a6b8e2abd86edfe4e
2022-02-15 15:56:48 +01:00
Harald Welte
799c354827 shell: Proper argparser (for help + manual) activate_file
Change-Id: I5929ae3deff4d15b5db4a1d866576271c57a955f
2022-02-15 15:56:28 +01:00
Harald Welte
2bb17f3df9 pySim-shell: export: Add FCP template to export
The FCP template provides us a lot of context, like the permissions of
a given file.  Let's make it part of the 'export' output, both in raw
and in decoded form.

Change-Id: I05f17bbebd7a9b3535204b821900851a5f66e88f
Closes: OS#5457
2022-02-15 15:41:55 +01:00
Harald Welte
9e241435cc docs/legcay.txt: Point to pySim-shell as replacement
Change-Id: I9ca6b9d8c35e23be2ec8752107bb7d1e4f6f9bc1
2022-02-15 15:38:19 +01:00
Harald Welte
3c9b784825 pySim-shell: support TS 102 222 administrative commands
This adds support for creating/deleting and terminating files,
as well as support for permanent card termination.

Change-Id: I5b1ffb1334afa18d62beb642268066a30deb7ea6
2022-02-15 15:35:36 +01:00
Harald Welte
747a978478 ts_102_221: Implement File Descriptor using construct
This automatically adds encoding support, which is needed for upcoming
CREATE FILE support.

Change-Id: Ia40dba4aab6ceb9d81fd170f7efa8dad1f9b43d0
2022-02-15 15:35:36 +01:00
Harald Welte
ee670bc1c6 pySim-shell: Allow selecting of deep paths like DF.GSM/EF.IMSI
With this patch applied, users can directly enter commands like

select DF.GSM/EF.IMSI or
select ADF.USIM/DF.5GS/EF.5GAUTHKEYS

This feature doesn't have tabl completion, so it's mostly useful
for when you know what to select, or for use within scripts.

Change-Id: I681a132eb2df4b2aba4c2ccbdd21c6d5b88443e3
2022-02-15 15:35:36 +01:00
Harald Welte
226b866f51 ts_31_103: TLV definitions for IMS, XCAP and MudMid configuration
Change-Id: I9a90ee978db668a70259eb48085ff5384cf696d6
2022-02-15 15:35:36 +01:00
Harald Welte
540adb0ee6 ts_51_011: EF_CMI: Decoder the alpha_id string
Change-Id: I45efe29ab98972945b4257229a995815f5632536
2022-02-15 15:35:36 +01:00
Harald Welte
1e73d228f4 ts_51_011: Convert EF_ADN and EF_ACC to Construct
this has the benefit of providing encoding support for free.

Change-Id: I31c118082e92892486c3688de2197c0c6dd2750e
2022-02-15 15:35:36 +01:00
Harald Welte
bc0e209a9f ts_51_011: Proper decode of EF.SMSP
Full decode of the SSM Parameters File

Change-Id: Iac5bb87ed3350978dc8b207f052510fdba2e4883
2022-02-15 15:35:35 +01:00
Harald Welte
3bb516b2b1 Improve IST/UST check documentation (for the user manual)
Change-Id: I18093d795721f2e729eff858c8922edde9e84451
2022-02-15 15:35:35 +01:00
Harald Welte
aceb2a548a ust_service_check: proper treatment of files in sub-directories
We must not only consider files in the current directory (ADF.USIM)
but also in its sub-directories.  This requires us to be able to
determine the path we need to traverse between the currently selected
file (EF.UST) and the respective file in some other directory,
which is implemented via CardFile.build_select_path_to().

Change-Id: I61797fefa9dafa36a8a62c11aa2cfaeecb015740
2022-02-15 15:35:35 +01:00
Harald Welte
419bb496e1 ts_31_102: service annotations for DF.{5GS,WLAN,HNB}
We had service annotations only for ADF.USIM so far, but not for
the related sub-directories.

Change-Id: Iaa56a26ba53eaf18fce14845ae07a27c52a2c58a
Note: The code doesn't make use of them in any reasonable way yet!
2022-02-15 15:35:35 +01:00
Harald Welte
fa8b8d1160 ts_31_102: Use perror() instead of poutput() for errors
This adds colorization and ensures they go to stderr and not stdout

Change-Id: I34b8f974b4ff13002679c4700bdf604db7d7f3cd
2022-02-15 15:35:35 +01:00
Harald Welte
82f75c200f ts_31_102: Add more EF.UST checks to 'ust_service_check' command
* check for service dependencies listed in TS 31.102
* print number of errors encountered

Change-Id: Id47f8f2c8de299bbf91243d0c8900d22a7d35b10
2022-02-15 15:35:35 +01:00
Harald Welte
d53918c3e1 filesystem: Fix CardMF.get_app_names()
This function was not used and doesn't work without this patch.

Change-Id: Id3dad7d97fe29a25792d2f8f0e879666c1d9c136
2022-02-15 15:35:35 +01:00
Harald Welte
6ca2fa7a5d Split EF.UST handling from EF.IST and EF.SST
The existing code had the following serious problems:
* when trying to update EF.SST or EF.IST, it would write to EF.UST !
* shell commands were called ust_* even for the EST/IST files

Let's introduce the proper separation between what is shared and what
is file-specific.

Change-Id: Ie55669ca37a4762fac9f71b1db528ca67056e8dd
2022-02-15 15:35:35 +01:00
Harald Welte
4c5e2310fa ts_31_102: Add "ust_service_check" command.
This command performs a consistency check between the services activated
in EF.UST/EF.IST and the files that should (or should not) be
active/selectable for the given service.

Produces output like:

Checking service No 48 (inactive)
  ERROR: File EF(EF.MWIS) is selectable but should not!
Checking service No 49 (active)
  ERROR: File EF(EF.CFIS) is not selectable (SW=6a82) but should!

Change-Id: Iea7166959e2015eb8fa34d86036560c9e42ce4d3
2022-02-15 15:35:35 +01:00
Harald Welte
d16d904c57 README.md: Remove old usage examples, refer to user manual instead
We want people to use pySim-shell and should not mislead them by
having usage examples of old tools in README.md.  Also, all
documentation should be in the manuals, let's try to have bits
and pieces in various places.

Change-Id: I8c07a2e0778ab95fb42be6074acb80874e681d20
2022-02-15 15:35:35 +01:00
Harald Welte
3729c47651 commands: Add method to select parent DF ("cd ..")
This is useful when walking around the filesystem tree.

Change-Id: Ib256c1b7319f2b5f9a06200fb96854ecb2b7f6bb
2022-02-14 00:51:27 +01:00
Harald Welte
a630a3cd28 cosmetic: Remove extraneous empty lines between spec-section-comment and class
This is an artefact of the recent autopep8 re-formatting.

Change-Id: I8b0e7781719d69e18856ada2f482de2c5396bcc3
2022-02-14 00:51:27 +01:00
Harald Welte
6169c72f82 USIM + ISIM: Specify the services associated with each file
This allows us [in a future patch] to perform consistency checking,
whether files exist for services not activated in EF.{UST,IST} or
vice-versa: Services are activated by files are not present or
deactivated.

Change-Id: I94bd1c3f9e977767553000077dd003423ed6dbd1
2022-02-14 00:51:27 +01:00
Harald Welte
9170fbf08d filesystem: Maintain a 'service' attribute for all files on a card
This can be populated by card profiles with the SST/IST/UST service
that is associated with the file.

Change-Id: I3b3f74b691368fa09967ecb377a9f7a6d8af7869
2022-02-14 00:51:22 +01:00
Harald Welte
afb8d3f925 pySim-shell: introduce 'apdu' command for sending raw APDU to card
This can be useful when playing around with cards, for example
sending commands for which pySim-shell doesn't yet have proper support.

Change-Id: Ib504431d26ed2b6f71f77a143ff0a7fb4f5ea02e
2022-02-14 00:48:16 +01:00
Harald Welte
08b11abc2f pySim-shell: export: allow export as JSON instead of hex
The primary use case of the --json option is to systematically execute
all of our decoder classes in order to find bugs.  As we don't have
encoders for all files yet, the output generated by 'export --json'
will in many cases not be executable as script again, unlike the normal
'export' output.

Change-Id: Idd820f8e3af70ebcbf82037b56fd2ae9655afbc5
2022-02-14 00:48:16 +01:00
Harald Welte
c8c3327b6e ts_102_221: Proper parsing of FCP using pySim.tlv instead of pytlv
pytlv is a nightmare of shortcomings, let's abandon it in favor of
our own meanwhile-created pySim.tlv.  This has the added benefit
that unknown tags finally no longer raise exceptions.

Change-Id: Ic8e0e0ddf915949670d620630d4ceb02a9116471
Closes: OS#5414
2022-02-14 00:48:11 +01:00
Harald Welte
e4a6eafc6f tlv: Don't raise exception if somebody passes empty data to TLV decoder
Change-Id: Id46994029d9b3cd6b67f4f7ee619466602cc8142
2022-02-14 00:44:55 +01:00
Harald Welte
c975251a48 filesystem: Don't pass empty string to parse_select_response()
This happens e.g. when selecting the ARA-M applet on sysmoISIM-SJA2:

pySIM-shell (MF)> select ADF.ARA-M
-> 00a4040409 a00000015141434c00
<- 9000:
Traceback (most recent call last):
  File "/space/home/laforge/.local/lib/python3.9/site-packages/cmd2/cmd2.py", line 2064, in onecmd_plus_hooks
    stop = self.onecmd(statement, add_to_history=add_to_history)
  File "/space/home/laforge/.local/lib/python3.9/site-packages/cmd2/cmd2.py", line 2494, in onecmd
    stop = func(statement)
  File "/space/home/laforge/projects/git/pysim/./pySim-shell.py", line 750, in do_select
    fcp_dec = self._cmd.rs.select(path, self._cmd)
  File "/space/home/laforge/projects/git/pysim/pySim/filesystem.py", line 1314, in select
    select_resp = f.decode_select_response(data)
  File "/space/home/laforge/projects/git/pysim/pySim/filesystem.py", line 193, in decode_select_response
    return self.parent.decode_select_response(data_hex)
  File "/space/home/laforge/projects/git/pysim/pySim/filesystem.py", line 378, in decode_select_response
    return profile.decode_select_response(data_hex)
  File "/space/home/laforge/projects/git/pysim/pySim/ts_102_221.py", line 796, in decode_select_response
    t.from_tlv(h2b(resp_hex))
  File "/space/home/laforge/projects/git/pysim/pySim/tlv.py", line 231, in from_tlv
    (rawtag, remainder) = self.__class__._parse_tag_raw(do)
  File "/space/home/laforge/projects/git/pysim/pySim/tlv.py", line 258, in _parse_tag_raw
    return bertlv_parse_tag_raw(do)
  File "/space/home/laforge/projects/git/pysim/pySim/utils.py", line 208, in bertlv_parse_tag_raw
    if binary[0] == 0xff and len(binary) == 1 or binary[0] == 0xff and binary[1] == 0xff:
IndexError: bytearray index out of range
EXCEPTION of type 'IndexError' occurred with message: 'bytearray index out of range'

Change-Id: I910e6deba27d1483dff1e986c89f1a1b2165f49b
2022-02-14 00:44:55 +01:00
Harald Welte
81f4b4058b Extend unit test coverage for construct, add [some] tests for TLV
Change-Id: I3470e0b2e978221aa0c1e46a4b65f71f71abef2e
2022-02-14 00:41:24 +01:00
Harald Welte
d0519e0c37 construct: Add Construct for variable-length int 'GreedyInteger'
We have a number of integers with variable-length encoding, so
add a Construct for this.  Naming inspired by GreedyBytes.

Related to https://github.com/construct/construct/issues/962

Change-Id: Ic6049b74ea3705fda24855f34b4a1d5f2c9327f7
2022-02-14 00:41:24 +01:00
26 changed files with 2060 additions and 829 deletions

View File

@@ -93,46 +93,30 @@ We are using a gerrit-based patch review process explained at
<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit>
Usage Examples
--------------
Documentation
-------------
* Program customizable SIMs. Two modes are possible:
The pySim user manual can be built from this very source code by means
of sphinx (with sphinxcontrib-napoleon and sphinx-argparse). See the
Makefile in the 'docs' directory.
- one where you specify every parameter manually:
```
./pySim-prog.py -n 26C3 -c 49 -x 262 -y 42 -i <IMSI> -s <ICCID>
```
A pre-rendered HTML user manual of the current pySim 'git master' is
available from <https://downloads.osmocom.org/docs/latest/pysim/> and
a downloadable PDF version is published at
<https://downloads.osmocom.org/docs/latest/osmopysim-usermanual.pdf>.
- one where they are generated from some minimal set:
```
./pySim-prog.py -n 26C3 -c 49 -x 262 -y 42 -z <random_string_of_choice> -j <card_num>
```
A slightly dated video presentation about pySim-shell can be found at
<https://media.ccc.de/v/osmodevcall-20210409-laforge-pysim-shell>.
With ``<random_string_of_choice>`` and ``<card_num>``, the soft will generate
'predictable' IMSI and ICCID, so make sure you choose them so as not to
conflict with anyone. (for e.g. your name as ``<random_string_of_choice>`` and
0 1 2 ... for ``<card num>``).
You also need to enter some parameters to select the device:
pySim-shell vs. legacy tools
----------------------------
-t TYPE : type of card (``supersim``, ``magicsim``, ``fakemagicsim`` or try ``auto``)
-d DEV : Serial port device (default ``/dev/ttyUSB0``)
-b BAUD : Baudrate (default 9600)
While you will find a lot of online resources still describing the use of
pySim-prog.py and pySim-read.py, those tools are considered legacy by
now and have by far been superseded by the much more capable
pySim-shell. We strongly encourage users to adopt pySim-shell, unless
they have very specific requirements like batch programming of large
quantities of cards, which is about the only remaining use case for the
legacy tools.
* Interact with SIMs from a python interactive shell (e.g. ipython):
```
from pySim.transport.serial import SerialSimLink
from pySim.commands import SimCardCommands
sl = SerialSimLink(device='/dev/ttyUSB0', baudrate=9600)
sc = SimCardCommands(sl)
sl.wait_for_card()
# Print IMSI
print(sc.read_binary(['3f00', '7f20', '6f07']))
# Run A3/A8
print(sc.run_gsm('00112233445566778899aabbccddeeff'))
```

View File

@@ -18,7 +18,7 @@ sys.path.insert(0, os.path.abspath('..'))
# -- Project information -----------------------------------------------------
project = 'osmopysim-usermanual'
copyright = '2009-2021 by Sylvain Munaut, Harald Welte, Philipp Maier, Supreeth Herle'
copyright = '2009-2022 by Sylvain Munaut, Harald Welte, Philipp Maier, Supreeth Herle'
author = 'Sylvain Munaut, Harald Welte, Philipp Maier, Supreeth Herle'

View File

@@ -34,7 +34,7 @@ pySim consists of several parts:
* the [legacy] :ref:`pySim-prog and pySim-read tools<Legacy tools>`
.. toctree::
:maxdepth: 2
:maxdepth: 3
:caption: Contents:
shell

View File

@@ -4,6 +4,9 @@ Legacy tools
*legacy tools* are the classic ``pySim-prog`` and ``pySim-read`` programs that
existed long before ``pySim-shell``.
These days, you should primarily use ``pySim-shell`` instead of these
legacy tools.
pySim-prog
----------
@@ -45,6 +48,11 @@ pySim-read
``pySim-read`` allows you to read some data from a SIM card. It will only some files
of the card, and will only read files accessible to a normal user (without any special authentication)
These days, you should use the ``export`` command of ``pySim-shell``
instead. It performs a much more comprehensive export of all of the
[standard] files that can be found on the card. To get a human-readable
decode instead of the raw hex export, you can use ``export --json``.
Specifically, pySim-read will dump the following:
* MF

View File

@@ -80,9 +80,11 @@ This will
pySIM-shell (MF)> select ADF.USIM
{
"file_descriptor": {
"shareable": true,
"file_type": "df",
"structure": "no_info_given"
"file_descriptor_byte": {
"shareable": true,
"file_type": "df",
"structure": "no_info_given"
}
},
"df_name": "A0000000871002FFFFFFFF8907090000",
"proprietary_info": {
@@ -96,6 +98,41 @@ This will
pySIM-shell (MF/ADF.USIM)>
status
~~~~~~
The ``status`` command [re-]obtains the File Control Template of the
currently-selected file and print its decoded output.
Example:
::
pySIM-shell (MF/ADF.ISIM)> status
{
"file_descriptor": {
"file_descriptor_byte": {
"shareable": true,
"file_type": "df",
"structure": "no_info_given"
},
"record_len": null,
"num_of_rec": null
},
"file_identifier": "ff01",
"df_name": "a0000000871004ffffffff8907090000",
"proprietary_information": {
"uicc_characteristics": "71",
"available_memory": 101640
},
"life_cycle_status_integer": "operational_activated",
"security_attrib_compact": "00",
"pin_status_template_do": {
"ps_do": "70",
"key_reference": 11
}
}
change_chv
~~~~~~~~~~
@@ -127,9 +164,6 @@ unblock_chv
verify_chv
~~~~~~~~~~
This command allows you to verify a CHV (PIN), which is how the specifications call
it if you authenticate yourself with the said CHV/PIN.
.. argparse::
:module: pySim-shell
:func: Iso7816Commands.verify_chv_parser
@@ -141,7 +175,9 @@ Deactivate the currently selected file. This used to be called INVALIDATE in TS
activate_file
~~~~~~~~~~~~~
Activate the currently selected file. This used to be called REHABILITATE in TS 11.11.
.. argparse::
:module: pySim-shell
:func: Iso7816Commands.activate_file_parser
open_channel
~~~~~~~~~~~~
@@ -170,6 +206,7 @@ including the electrical power down.
:func: Iso7816Commands.suspend_uicc_parser
pySim commands
--------------
@@ -179,7 +216,6 @@ a complex sequence of card-commands.
desc
~~~~
Display human readable file description for the currently selected file.
@@ -189,6 +225,17 @@ dir
:module: pySim-shell
:func: PySimCommands.dir_parser
Example:
::
pySIM-shell (MF)> dir
MF
3f00
.. ADF.USIM DF.SYSTEM EF.DIR EF.UMPC
ADF.ARA-M DF.EIRENE DF.TELECOM EF.ICCID MF
ADF.ISIM DF.GSM EF.ARR EF.PL
14 files
export
~~~~~~
@@ -208,15 +255,27 @@ all/most files.
tree
~~~~
Display a tree of the card filesystem. It is important to note that this displays a tree
of files that might potentially exist (based on the card profile). In order to determine if
a given file really exists on a given card, you have to try to select that file.
Example:
::
pySIM-shell (MF)> tree --help
EF.DIR 2f00 Application Directory
EF.ICCID 2fe2 ICC Identification
EF.PL 2f05 Preferred Languages
EF.ARR 2f06 Access Rule Reference
EF.UMPC 2f08 UICC Maximum Power Consumption
DF.TELECOM 7f10 None
EF.ADN 6f3a Abbreviated Dialing Numbers
...
verify_adm
~~~~~~~~~~
Verify the ADM (Administrator) PIN specified as argument. This is typically needed in order
to get write/update permissions to most of the files on SIM cards.
@@ -244,8 +303,6 @@ bulk_script
:module: pySim-shell
:func: PysimApp.bulk_script_parser
Run a script for bulk-provisioning of multiple cards.
echo
~~~~
@@ -254,6 +311,13 @@ echo
:func: PysimApp.echo_parser
apdu
~~~~
.. argparse::
:module: pySim-shell
:func: PySimCommands.apdu_cmd_parser
Linear Fixed EF commands
------------------------
@@ -320,6 +384,13 @@ back to the record on the SIM card.
This allows for easy interactive modification of records.
decode_hex
~~~~~~~~~~
.. argparse::
:module: pySim.filesystem
:func: LinFixedEF.ShellCommands.dec_hex_parser
Transparent EF commands
-----------------------
@@ -396,6 +467,13 @@ to the SIM card.
This allows for easy interactive modification of file contents.
decode_hex
~~~~~~~~~~
.. argparse::
:module: pySim.filesystem
:func: TransparentEF.ShellCommands.dec_hex_parser
BER-TLV EF commands
-------------------
@@ -442,6 +520,26 @@ authenticate
:module: pySim.ts_31_102
:func: ADF_USIM.AddlShellCommands.authenticate_parser
terminal_profile
~~~~~~~~~~~~~~~~
.. argparse::
:module: pySim.ts_31_102
:func: ADF_USIM.AddlShellCommands.term_prof_parser
envelope
~~~~~~~~
.. argparse::
:module: pySim.ts_31_102
:func: ADF_USIM.AddlShellCommands.envelope_parser
envelope_sms
~~~~~~~~~~~~
.. argparse::
:module: pySim.ts_31_102
:func: ADF_USIM.AddlShellCommands.envelope_sms_parser
ARA-M commands
--------------
@@ -504,21 +602,14 @@ Perform Config handshake with ARA-M applet: Tell it our version and retrieve its
NOTE: Not supported in all ARA-M implementations.
.. argparse::
:module: pySim.ara_m
:func: ADF_ARAM.AddlShellCommands.get_config_parser
aram_store_ref_ar_do
~~~~~~~~~~~~~~~~~~~~
Store a [new] access rule on the ARA-M applet.
.. argparse::
:module: pySim.ara_m
:func: ADF_ARAM.AddlShellCommands.store_ref_ar_do_parse
For example, to store an Android UICC carrier privilege rule for the SHA1 hash of the certificate used to sign the CoIMS android app of Supreeth Herle (https://github.com/herlesupreeth/CoIMS_Wiki) you can use the following command:
::
pySIM-shell (MF/ADF.ARA-M)> aram_store_ref_ar_do --aid FFFFFFFFFFFF --device-app-id E46872F28B350B7E1F140DE535C2A8D5804F0BE3 --android-permissions 0000000000000001 --apdu-always

View File

@@ -49,9 +49,11 @@ from pySim.profile import CardProfile
from pySim.ts_51_011 import CardProfileSIM, DF_TELECOM, DF_GSM
from pySim.ts_102_221 import CardProfileUICC
from pySim.ts_102_221 import CardProfileUICCSIM
from pySim.ts_102_222 import Ts102222Commands
from pySim.ts_31_102 import CardApplicationUSIM
from pySim.ts_31_103 import CardApplicationISIM
from pySim.ara_m import CardApplicationARAM
from pySim.global_platform import CardApplicationISD
from pySim.gsm_r import DF_EIRENE
# we need to import this module so that the SysmocomSJA2 sub-class of
@@ -102,6 +104,7 @@ def init_card(sl):
profile.add_application(CardApplicationUSIM())
profile.add_application(CardApplicationISIM())
profile.add_application(CardApplicationARAM())
profile.add_application(CardApplicationISD())
# Create runtime state with card profile
rs = RuntimeState(card, profile)
@@ -174,6 +177,7 @@ class PysimApp(cmd2.Cmd):
'conserve_write', False, self.conserve_write)
self._onchange_apdu_trace('apdu_trace', False, self.apdu_trace)
self.register_command_set(Iso7816Commands())
self.register_command_set(Ts102222Commands())
self.register_command_set(PySimCommands())
self.iccid, sw = self.card.read_iccid()
rs.select('MF', self)
@@ -457,7 +461,7 @@ class PySimCommands(CommandSet):
self._cmd.poutput(directory_str)
self._cmd.poutput("%d files" % len(selectables))
def walk(self, indent=0, action=None, context=None):
def walk(self, indent=0, action=None, context=None, as_json=False):
"""Recursively walk through the file system, starting at the currently selected DF"""
files = self._cmd.rs.selected_file.get_selectables(
flags=['FNAMES', 'ANAMES'])
@@ -489,12 +493,12 @@ class PySimCommands(CommandSet):
# If the DF was skipped, we never have entered the directory
# below, so we must not move up.
if skip_df == False:
self.walk(indent + 1, action, context)
self.walk(indent + 1, action, context, as_json)
fcp_dec = self._cmd.rs.select("..", self._cmd)
elif action:
df_before_action = self._cmd.rs.selected_file
action(f, context)
action(f, context, as_json)
# When walking through the file system tree the action must not
# always restore the currently selected file to the file that
# was selected before executing the action() callback.
@@ -506,7 +510,7 @@ class PySimCommands(CommandSet):
"""Display a filesystem-tree with all selectable files"""
self.walk()
def export(self, filename, context):
def export(self, filename, context, as_json=False):
""" Select and export a single file """
context['COUNT'] += 1
df = self._cmd.rs.selected_file
@@ -528,32 +532,46 @@ class PySimCommands(CommandSet):
self._cmd.poutput("# file: %s (%s)" % (
self._cmd.rs.selected_file.name, self._cmd.rs.selected_file.fid))
fd = fcp_dec['file_descriptor']
structure = fd['structure']
structure = self._cmd.rs.selected_file_structure()
self._cmd.poutput("# structure: %s" % str(structure))
self._cmd.poutput("# RAW FCP Template: %s" % str(self._cmd.rs.selected_file_fcp_hex))
self._cmd.poutput("# Decoded FCP Template: %s" % str(self._cmd.rs.selected_file_fcp))
for f in df_path_list:
self._cmd.poutput("select " + str(f))
self._cmd.poutput("select " + self._cmd.rs.selected_file.name)
if structure == 'transparent':
result = self._cmd.rs.read_binary()
self._cmd.poutput("update_binary " + str(result[0]))
if as_json:
result = self._cmd.rs.read_binary_dec()
self._cmd.poutput("update_binary_decoded '%s'" % json.dumps(result[0], cls=JsonEncoder))
else:
result = self._cmd.rs.read_binary()
self._cmd.poutput("update_binary " + str(result[0]))
elif structure == 'cyclic' or structure == 'linear_fixed':
# Use number of records specified in select response
if 'num_of_rec' in fd:
num_of_rec = fd['num_of_rec']
num_of_rec = self._cmd.rs.selected_file_num_of_rec()
if num_of_rec:
for r in range(1, num_of_rec + 1):
result = self._cmd.rs.read_record(r)
self._cmd.poutput("update_record %d %s" %
(r, str(result[0])))
if as_json:
result = self._cmd.rs.read_record_dec(r)
self._cmd.poutput("update_record_decoded %d '%s'" % (r, json.dumps(result[0], cls=JsonEncoder)))
else:
result = self._cmd.rs.read_record(r)
self._cmd.poutput("update_record %d %s" % (r, str(result[0])))
# When the select response does not return the number of records, read until we hit the
# first record that cannot be read.
else:
r = 1
while True:
try:
result = self._cmd.rs.read_record(r)
if as_json:
result = self._cmd.rs.read_record_dec(r)
self._cmd.poutput("update_record_decoded %d '%s'" % (r, json.dumps(result[0], cls=JsonEncoder)))
else:
result = self._cmd.rs.read_record(r)
self._cmd.poutput("update_record %d %s" % (r, str(result[0])))
except SwMatchError as e:
# We are past the last valid record - stop
if e.sw_actual == "9402":
@@ -561,8 +579,6 @@ class PySimCommands(CommandSet):
# Some other problem occurred
else:
raise e
self._cmd.poutput("update_record %d %s" %
(r, str(result[0])))
r = r + 1
elif structure == 'ber_tlv':
tags = self._cmd.rs.retrieve_tags()
@@ -591,6 +607,8 @@ class PySimCommands(CommandSet):
export_parser = argparse.ArgumentParser()
export_parser.add_argument(
'--filename', type=str, default=None, help='only export specific file')
export_parser.add_argument(
'--json', action='store_true', help='export as JSON (less reliable)')
@cmd2.with_argparser(export_parser)
def do_export(self, opts):
@@ -598,9 +616,9 @@ class PySimCommands(CommandSet):
context = {'ERR': 0, 'COUNT': 0, 'BAD': [],
'DF_SKIP': 0, 'DF_SKIP_REASON': []}
if opts.filename:
self.export(opts.filename, context)
self.export(opts.filename, context, opts.json)
else:
self.walk(0, self.export, context)
self.walk(0, self.export, context, opts.json)
self._cmd.poutput(boxed_heading_str("Export summary"))
@@ -660,6 +678,18 @@ class PySimCommands(CommandSet):
else:
raise ValueError("error: cannot authenticate, no adm-pin!")
apdu_cmd_parser = argparse.ArgumentParser()
apdu_cmd_parser.add_argument('APDU', type=str, help='APDU as hex string')
@cmd2.with_argparser(apdu_cmd_parser)
def do_apdu(self, opts):
"""Send a raw APDU to the card, and print SW + Response.
DANGEROUS: pySim-shell will not know any card state changes, and
not continue to work as expected if you e.g. select a different
file."""
data, sw = self._cmd.card._scc._tp.send_apdu(opts.APDU)
self._cmd.poutput("SW: %s %s, RESP: %s" % (sw, self._cmd.rs.interpret_sw(sw), data))
@with_default_category('ISO7816 Commands')
class Iso7816Commands(CommandSet):
@@ -712,7 +742,9 @@ class Iso7816Commands(CommandSet):
@cmd2.with_argparser(verify_chv_parser)
def do_verify_chv(self, opts):
"""Verify (authenticate) using specified PIN code"""
"""Verify (authenticate) using specified CHV (PIN) code, which is how the specifications
call it if you authenticate yourself using the specified PIN. There usually is at least PIN1 and
PIN2."""
pin = self.get_code(opts.pin_code)
(data, sw) = self._cmd.card._scc.verify_chv(opts.pin_nr, h2b(pin))
self._cmd.poutput("CHV verification successful")
@@ -778,13 +810,16 @@ class Iso7816Commands(CommandSet):
self._cmd.poutput("CHV enable successful")
def do_deactivate_file(self, opts):
"""Deactivate the current EF"""
"""Deactivate the currently selected EF"""
(data, sw) = self._cmd.card._scc.deactivate_file()
activate_file_parser = argparse.ArgumentParser()
activate_file_parser.add_argument('NAME', type=str, help='File name or FID of file to activate')
@cmd2.with_argparser(activate_file_parser)
def do_activate_file(self, opts):
"""Activate the specified EF"""
path = opts.arg_list[0]
(data, sw) = self._cmd.rs.activate_file(path)
"""Activate the specified EF. This used to be called REHABILITATE in TS 11.11 for classic
SIM. You need to specify the name or FID of the file to activate."""
(data, sw) = self._cmd.rs.activate_file(opts.NAME)
def complete_activate_file(self, text, line, begidx, endidx) -> List[str]:
"""Command Line tab completion for ACTIVATE FILE"""
@@ -831,6 +866,16 @@ class Iso7816Commands(CommandSet):
self._cmd.poutput(
'Negotiated Duration: %u secs, Token: %s, SW: %s' % (duration, token, sw))
run_gsm_algo_parser = argparse.ArgumentParser()
run_gsm_algo_parser.add_argument('rand', type=str, help='RAND value')
# not ISO7816-4 but TS 102 221
@cmd2.with_argparser(run_gsm_algo_parser)
def do_run_gsm_algo(self, opts):
(data, sw) = self._cmd.card._scc.run_gsm(opts.rand)
self._cmd.poutput('SRES: %s' % data[:8])
self._cmd.poutput('Kc: %s' % data[8:])
option_parser = argparse.ArgumentParser(prog='pySim-shell', description='interactive SIM card shell',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)

View File

@@ -311,7 +311,7 @@ class ADF_ARAM(CardADF):
self._cmd.poutput_json(res_do.to_dict())
def do_aram_get_config(self, opts):
"""GET DATA [Config] on the ARA-M Applet"""
"""Perform GET DATA [Config] on the ARA-M Applet: Tell it our version and retrieve its version."""
res_do = ADF_ARAM.get_config(self._cmd.card._scc._tp)
if res_do:
self._cmd.poutput_json(res_do.to_dict())
@@ -345,7 +345,7 @@ class ADF_ARAM(CardADF):
@cmd2.with_argparser(store_ref_ar_do_parse)
def do_aram_store_ref_ar_do(self, opts):
"""Perform STORE DATA [Command-Store-REF-AR-DO] to store a new access rule."""
"""Perform STORE DATA [Command-Store-REF-AR-DO] to store a (new) access rule."""
# REF
ref_do_content = []
if opts.aid:

View File

@@ -425,6 +425,15 @@ class UsimCard(SimCard):
EF_USIM_ADF_map['UST'], content)
return sw
def update_est(self, service, bit=1):
(res, sw) = self._scc.read_binary(EF_USIM_ADF_map['EST'])
if sw == '9000':
content = enc_st(res, service, bit)
(res, sw) = self._scc.update_binary(
EF_USIM_ADF_map['EST'], content)
return sw
class IsimCard(SimCard):
@@ -566,6 +575,14 @@ class IsimCard(SimCard):
sw)
return uiari_recs
def update_ist(self, service, bit=1):
(res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['IST'])
if sw == '9000':
content = enc_st(res, service, bit)
(res, sw) = self._scc.update_binary(
EF_ISIM_ADF_map['IST'], content)
return sw
class MagicSimBase(abc.ABC, SimCard):
"""

View File

@@ -24,48 +24,34 @@ from construct import *
# Tag values as per TS 101 220 Table 7.23
# TS 102 223 Section 8.1
class Address(COMPR_TLV_IE, tag=0x06):
_construct = Struct('ton_npi'/Int8ub,
'call_number'/BcdAdapter(Bytes(this._.total_len-1)))
# TS 102 223 Section 8.2
class AlphaIdentifier(COMPR_TLV_IE, tag=0x05):
# FIXME: like EF.ADN
pass
# TS 102 223 Section 8.3
class Subaddress(COMPR_TLV_IE, tag=0x08):
pass
# TS 102 223 Section 8.4
class CapabilityConfigParams(COMPR_TLV_IE, tag=0x07):
pass
# TS 31.111 Section 8.5
class CBSPage(COMPR_TLV_IE, tag=0x0C):
pass
# TS 102 223 Section 8.6
class CommandDetails(COMPR_TLV_IE, tag=0x01):
_construct = Struct('command_number'/Int8ub,
'type_of_command'/Int8ub,
'command_qualifier'/Int8ub)
# TS 102 223 Section 8.7
class DeviceIdentities(COMPR_TLV_IE, tag=0x82):
DEV_IDS = bidict({
0x01: 'keypad',
@@ -115,35 +101,25 @@ class DeviceIdentities(COMPR_TLV_IE, tag=0x82):
return bytes([src, dst])
# TS 102 223 Section 8.8
class Duration(COMPR_TLV_IE, tag=0x04):
_construct = Struct('time_unit'/Int8ub,
'time_interval'/Int8ub)
# TS 102 223 Section 8.9
class Item(COMPR_TLV_IE, tag=0x0f):
_construct = Struct('identifier'/Int8ub,
'text_string'/GsmStringAdapter(GreedyBytes))
# TS 102 223 Section 8.10
class ItemIdentifier(COMPR_TLV_IE, tag=0x10):
_construct = Struct('identifier'/Int8ub)
# TS 102 223 Section 8.11
class ResponseLength(COMPR_TLV_IE, tag=0x11):
_construct = Struct('minimum_length'/Int8ub,
'maximum_length'/Int8ub)
# TS 102 223 Section 8.12
class Result(COMPR_TLV_IE, tag=0x03):
_construct = Struct('general_result'/Int8ub,
'additional_information'/HexAdapter(GreedyBytes))
@@ -154,21 +130,15 @@ class SMS_TPDU(COMPR_TLV_IE, tag=0x8B):
_construct = Struct('tpdu'/HexAdapter(GreedyBytes))
# TS 102 223 Section 8.15
class TextString(COMPR_TLV_IE, tag=0x0d):
_construct = Struct('dcs'/Int8ub,
'text_string'/HexAdapter(GreedyBytes))
# TS 102 223 Section 8.16
class Tone(COMPR_TLV_IE, tag=0x0e):
_construct = Struct('tone'/Int8ub)
# TS 31 111 Section 8.17
class USSDString(COMPR_TLV_IE, tag=0x0a):
_construct = Struct('dcs'/Int8ub,
'ussd_string'/HexAdapter(GreedyBytes))
@@ -179,15 +149,11 @@ class ProactiveCommand(BER_TLV_IE, tag=0xD0):
pass
# TS 101 220 Table 7.17 + 31.111 7.1.1.2
class SMSPPDownload(BER_TLV_IE, tag=0xD1,
nested=[DeviceIdentities, Address, SMS_TPDU]):
pass
# TS 101 220 Table 7.17 + 31.111 7.1.1.3
class SMSCBDownload(BER_TLV_IE, tag=0xD2,
nested=[DeviceIdentities, CBSPage]):
pass

View File

@@ -136,6 +136,10 @@ class SimCardCommands(object):
return self._tp.send_apdu_checksw(self.cla_byte + "a4" + self.sel_ctrl + "02" + fid)
def select_parent_df(self):
"""Execute SELECT to switch to the parent DF """
return self._tp.send_apdu_checksw(self.cla_byte + "a4030400")
def select_adf(self, aid: str):
"""Execute SELECT a given Applicaiton ADF.
@@ -398,7 +402,7 @@ class SimCardCommands(object):
if len(rand) != 32:
raise ValueError('Invalid rand')
self.select_path(['3f00', '7f20'])
return self._tp.send_apdu(self.cla_byte + '88000010' + rand)
return self._tp.send_apdu_checksw('a0' + '88000010' + rand, sw='9000')
def authenticate(self, rand: str, autn: str, context='3g'):
"""Execute AUTHENTICATE (USIM/ISIM).
@@ -444,6 +448,26 @@ class SimCardCommands(object):
"""
return self._tp.send_apdu_checksw(self.cla_byte + '44000002' + fid)
def create_file(self, payload: Hexstr):
"""Execute CREEATE FILE command as per TS 102 222 Section 6.3"""
return self._tp.send_apdu_checksw(self.cla_byte + 'e00000%02x%s' % (len(payload)//2, payload))
def delete_file(self, fid):
"""Execute DELETE FILE command as per TS 102 222 Section 6.4"""
return self._tp.send_apdu_checksw(self.cla_byte + 'e4000002' + fid)
def terminate_df(self, fid):
"""Execute TERMINATE DF command as per TS 102 222 Section 6.7"""
return self._tp.send_apdu_checksw(self.cla_byte + 'e6000002' + fid)
def terminate_ef(self, fid):
"""Execute TERMINATE EF command as per TS 102 222 Section 6.8"""
return self._tp.send_apdu_checksw(self.cla_byte + 'e8000002' + fid)
def terminate_card_usage(self):
"""Execute TERMINATE CARD USAGE command as per TS 102 222 Section 6.9"""
return self._tp.send_apdu_checksw(self.cla_byte + 'fe000000')
def manage_channel(self, mode='open', lchan_nr=0):
"""Execute MANAGE CHANNEL command as per TS 102 221 Section 11.1.17.
@@ -597,3 +621,7 @@ class SimCardCommands(object):
negotiated_duration_secs = decode_duration(data[:4])
resume_token = data[4:]
return (negotiated_duration_secs, resume_token, sw)
def get_data(self, tag: int, cla: int = 0x00):
data, sw = self._tp.send_apdu('%02xca%04x00' % (cla, tag))
return (data, sw)

View File

@@ -2,12 +2,14 @@ from construct.lib.containers import Container, ListContainer
from construct.core import EnumIntegerString
import typing
from construct import *
from construct.core import evaluate, bytes2integer, integer2bytes, BitwisableString
from construct.lib import integertypes
from pySim.utils import b2h, h2b, swap_nibbles
import gsm0338
"""Utility code related to the integration of the 'construct' declarative parser."""
# (C) 2021 by Harald Welte <laforge@osmocom.org>
# (C) 2021-2022 by Harald Welte <laforge@osmocom.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -42,6 +44,25 @@ class BcdAdapter(Adapter):
def _encode(self, obj, context, path):
return h2b(swap_nibbles(obj))
class InvertAdapter(Adapter):
"""inverse logic (false->true, true->false)."""
@staticmethod
def _invert_bool_in_obj(obj):
for k,v in obj.items():
# skip all private entries
if k.startswith('_'):
continue
if v == False:
obj[k] = True
elif v == True:
obj[k] = False
return obj
def _decode(self, obj, context, path):
return self._invert_bool_in_obj(obj)
def _encode(self, obj, context, path):
return self._invert_bool_in_obj(obj)
class Rpad(Adapter):
"""
@@ -184,3 +205,53 @@ def GsmString(n):
n (Integer): Fixed length of the encoded byte string
'''
return GsmStringAdapter(Rpad(Bytes(n), pattern=b'\xff'), codec='gsm03.38')
class GreedyInteger(Construct):
"""A variable-length integer implementation, think of combining GrredyBytes with BytesInteger."""
def __init__(self, signed=False, swapped=False):
super().__init__()
self.signed = signed
self.swapped = swapped
def _parse(self, stream, context, path):
data = stream_read_entire(stream, path)
if evaluate(self.swapped, context):
data = swapbytes(data)
try:
return bytes2integer(data, self.signed)
except ValueError as e:
raise IntegerError(str(e), path=path)
def __bytes_required(self, i):
if self.signed:
raise NotImplementedError("FIXME: Implement support for encoding signed integer")
nbytes = 1
while True:
i = i >> 8
if i == 0:
return nbytes
else:
nbytes = nbytes + 1
# this should never happen, above loop must return eventually...
raise IntegerError(f"value {i} is out of range")
def _build(self, obj, stream, context, path):
if not isinstance(obj, integertypes):
raise IntegerError(f"value {obj} is not an integer", path=path)
length = self.__bytes_required(obj)
try:
data = integer2bytes(obj, length, self.signed)
except ValueError as e:
raise IntegerError(str(e), path=path)
if evaluate(self.swapped, context):
data = swapbytes(data)
stream_write(stream, data, length, path)
return obj
# merged definitions of 24.008 + 23.040
TypeOfNumber = Enum(BitsInteger(3), unknown=0, international=1, national=2, network_specific=3,
short_code=4, alphanumeric=5, abbreviated=6, reserved_for_extension=7)
NumberingPlan = Enum(BitsInteger(4), unknown=0, isdn_e164=1, data_x121=3, telex_f69=4,
sc_specific_5=5, sc_specific_6=6, national=8, private=9,
ermes=10, reserved_cts=11, reserved_for_extension=15)
TonNpi = BitStruct('ext'/Flag, 'type_of_number'/TypeOfNumber, 'numbering_plan_id'/NumberingPlan)

View File

@@ -34,7 +34,7 @@ import cmd2
from cmd2 import CommandSet, with_default_category, with_argparser
import argparse
from typing import cast, Optional, Iterable, List, Dict, Tuple
from typing import cast, Optional, Iterable, List, Dict, Tuple, Union
from smartcard.util import toBytes
@@ -44,6 +44,10 @@ from pySim.exceptions import *
from pySim.jsonpath import js_path_find, js_path_modify
from pySim.commands import SimCardCommands
# int: a single service is associated with this file
# list: any of the listed services requires this file
# tuple: logical-and of the listed services requires this file
CardFileService = Union[int, List[int], Tuple[int, ...]]
class CardFile(object):
"""Base class for all objects in the smart card filesystem.
@@ -53,7 +57,8 @@ class CardFile(object):
RESERVED_FIDS = ['3f00']
def __init__(self, fid: str = None, sfid: str = None, name: str = None, desc: str = None,
parent: Optional['CardDF'] = None, profile: Optional['CardProfile'] = None):
parent: Optional['CardDF'] = None, profile: Optional['CardProfile'] = None,
service: Optional[CardFileService] = None):
"""
Args:
fid : File Identifier (4 hex digits)
@@ -62,6 +67,7 @@ class CardFile(object):
desc : Description of the file
parent : Parent CardFile object within filesystem hierarchy
profile : Card profile that this file should be part of
service : Service (SST/UST/IST) associated with the file
"""
if not isinstance(self, CardADF) and fid == None:
raise ValueError("fid is mandatory")
@@ -75,6 +81,7 @@ class CardFile(object):
if self.parent and self.parent != self and self.fid:
self.parent.add_file(self)
self.profile = profile
self.service = service
self.shell_commands = [] # type: List[CommandSet]
# Note: the basic properties (fid, name, ect.) are verified when
@@ -108,6 +115,34 @@ class CardFile(object):
ret.append(elem)
return ret
def fully_qualified_path_fobj(self) -> List['CardFile']:
"""Return fully qualified path to file as list of CardFile instance references."""
if self.parent and self.parent != self:
ret = self.parent.fully_qualified_path_fobj()
else:
ret = []
if self:
ret.append(self)
return ret
def build_select_path_to(self, target: 'CardFile') -> Optional[List['CardFile']]:
"""Build the relative sequence of files we need to traverse to get from us to 'target'."""
cur_fqpath = self.fully_qualified_path_fobj()
target_fqpath = target.fully_qualified_path_fobj()
inter_path = []
cur_fqpath.pop() # drop last element (currently selected file, doesn't need re-selection
cur_fqpath.reverse()
for ce in cur_fqpath:
inter_path.append(ce)
for i in range(0, len(target_fqpath)-1):
te = target_fqpath[i]
if te == ce:
for te2 in target_fqpath[i+1:]:
inter_path.append(te2)
# we found our common ancestor
return inter_path
return None
def get_mf(self) -> Optional['CardMF']:
"""Return the MF (root) of the file system."""
if self.parent == None:
@@ -207,6 +242,28 @@ class CardFile(object):
return self.parent.get_profile()
return None
def should_exist_for_services(self, services: List[int]):
"""Assuming the provided list of activated services, should this file exist and be activated?."""
if self.service is None:
return None
elif isinstance(self.service, int):
# a single service determines the result
return self.service in services
elif isinstance(self.service, list):
# any of the services active -> true
for s in self.service:
if s in services:
return True
return False
elif isinstance(self.service, tuple):
# all of the services active -> true
for s in self.service:
if not s in services:
return False
return True
else:
raise ValueError("self.service must be either int or list or tuple")
class CardDF(CardFile):
"""DF (Dedicated File) in the smart card filesystem. Those are basically sub-directories."""
@@ -223,10 +280,27 @@ class CardDF(CardFile):
super().__init__(**kwargs)
self.children = dict()
self.shell_commands = [self.ShellCommands()]
# dict of CardFile affected by service(int), indexed by service
self.files_by_service = {}
def __str__(self):
return "DF(%s)" % (super().__str__())
def _add_file_services(self, child):
"""Add a child (DF/EF) to the files_by_services of the parent."""
if not child.service:
return
if isinstance(child.service, int):
self.files_by_service.setdefault(child.service, []).append(child)
elif isinstance(child.service, list):
for service in child.service:
self.files_by_service.setdefault(service, []).append(child)
elif isinstance(child.service, tuple):
for service in child.service:
self.files_by_service.setdefault(service, []).append(child)
else:
raise ValueError
def add_file(self, child: CardFile, ignore_existing: bool = False):
"""Add a child (DF/EF) to this DF.
Args:
@@ -256,6 +330,13 @@ class CardDF(CardFile):
"File with given name %s already exists in %s" % (child.name, self))
self.children[child.fid] = child
child.parent = self
# update the service -> file relationship table
self._add_file_services(child)
if isinstance(child, CardDF):
for c in child.children.values():
self._add_file_services(c)
if isinstance(c, CardDF):
raise ValueError('TODO: implement recursive service -> file mapping')
def add_files(self, children: Iterable[CardFile], ignore_existing: bool = False):
"""Add a list of child (DF/EF) to this DF
@@ -337,7 +418,7 @@ class CardMF(CardDF):
def get_app_names(self):
"""Get list of completions (AID names)"""
return [x.name for x in self.applications]
return list(self.applications.values())
def get_selectables(self, flags=[]) -> dict:
"""Return a dict of {'identifier': File} that is selectable from the current DF.
@@ -363,7 +444,7 @@ class CardMF(CardDF):
{x.name: x for x in self.applications.values() if x.name})
return sels
def decode_select_response(self, data_hex: str) -> object:
def decode_select_response(self, data_hex: Optional[str]) -> object:
"""Decode the response to a SELECT command.
This is the fall-back method which automatically defers to the standard decoding
@@ -372,6 +453,9 @@ class CardMF(CardDF):
install specific decoding.
"""
if not data_hex:
return data_hex
profile = self.get_profile()
if profile:
@@ -442,6 +526,17 @@ class TransparentEF(CardEF):
def __init__(self):
super().__init__()
dec_hex_parser = argparse.ArgumentParser()
dec_hex_parser.add_argument('--oneline', action='store_true',
help='No JSON pretty-printing, dump as a single line')
dec_hex_parser.add_argument('HEXSTR', help='Hex-string of encoded data to decode')
@cmd2.with_argparser(dec_hex_parser)
def do_decode_hex(self, opts):
"""Decode command-line provided hex-string as if it was read from the file."""
data = self._cmd.rs.selected_file.decode_hex(opts.HEXSTR)
self._cmd.poutput_json(data, opts.oneline)
read_bin_parser = argparse.ArgumentParser()
read_bin_parser.add_argument(
'--offset', type=int, default=0, help='Byte offset for start of read')
@@ -516,7 +611,7 @@ class TransparentEF(CardEF):
self._cmd.poutput_json(data)
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None,
size={1, None}):
size={1, None}, **kwargs):
"""
Args:
fid : File Identifier (4 hex digits)
@@ -526,7 +621,7 @@ class TransparentEF(CardEF):
parent : Parent CardFile object within filesystem hierarchy
size : tuple of (minimum_size, recommended_size)
"""
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent)
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, **kwargs)
self._construct = None
self._tlv = None
self.size = size
@@ -651,8 +746,19 @@ class LinFixedEF(CardEF):
class ShellCommands(CommandSet):
"""Shell commands specific for Linear Fixed EFs."""
def __init__(self):
super().__init__()
def __init__(self, **kwargs):
super().__init__(**kwargs)
dec_hex_parser = argparse.ArgumentParser()
dec_hex_parser.add_argument('--oneline', action='store_true',
help='No JSON pretty-printing, dump as a single line')
dec_hex_parser.add_argument('HEXSTR', help='Hex-string of encoded data to decode')
@cmd2.with_argparser(dec_hex_parser)
def do_decode_hex(self, opts):
"""Decode command-line provided hex-string as if it was read from the file."""
data = self._cmd.rs.selected_file.decode_record_hex(opts.HEXSTR)
self._cmd.poutput_json(data, opts.oneline)
read_rec_parser = argparse.ArgumentParser()
read_rec_parser.add_argument(
@@ -689,7 +795,7 @@ class LinFixedEF(CardEF):
@cmd2.with_argparser(read_recs_parser)
def do_read_records(self, opts):
"""Read all records from a record-oriented EF"""
num_of_rec = self._cmd.rs.selected_file_fcp['file_descriptor']['num_of_rec']
num_of_rec = self._cmd.rs.selected_file_num_of_rec()
for recnr in range(1, 1 + num_of_rec):
(data, sw) = self._cmd.rs.read_record(recnr)
if (len(data) > 0):
@@ -705,7 +811,7 @@ class LinFixedEF(CardEF):
@cmd2.with_argparser(read_recs_dec_parser)
def do_read_records_decoded(self, opts):
"""Read + decode all records from a record-oriented EF"""
num_of_rec = self._cmd.rs.selected_file_fcp['file_descriptor']['num_of_rec']
num_of_rec = self._cmd.rs.selected_file_num_of_rec()
# collect all results in list so they are rendered as JSON list when printing
data_list = []
for recnr in range(1, 1 + num_of_rec):
@@ -774,7 +880,7 @@ class LinFixedEF(CardEF):
self._cmd.poutput_json(data)
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None,
parent: Optional[CardDF] = None, rec_len={1, None}):
parent: Optional[CardDF] = None, rec_len={1, None}, **kwargs):
"""
Args:
fid : File Identifier (4 hex digits)
@@ -784,7 +890,7 @@ class LinFixedEF(CardEF):
parent : Parent CardFile object within filesystem hierarchy
rec_len : set of {minimum_length, recommended_length}
"""
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent)
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, **kwargs)
self.rec_len = rec_len
self.shell_commands = [self.ShellCommands()]
self._construct = None
@@ -905,9 +1011,8 @@ class CyclicEF(LinFixedEF):
# we don't really have any special support for those; just recycling LinFixedEF here
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None,
rec_len={1, None}):
super().__init__(fid=fid, sfid=sfid, name=name,
desc=desc, parent=parent, rec_len=rec_len)
rec_len={1, None}, **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, rec_len=rec_len, **kwargs)
class TransRecEF(TransparentEF):
@@ -921,7 +1026,7 @@ class TransRecEF(TransparentEF):
"""
def __init__(self, fid: str, rec_len: int, sfid: str = None, name: str = None, desc: str = None,
parent: Optional[CardDF] = None, size={1, None}):
parent: Optional[CardDF] = None, size={1, None}, **kwargs):
"""
Args:
fid : File Identifier (4 hex digits)
@@ -932,7 +1037,7 @@ class TransRecEF(TransparentEF):
rec_len : Length of the fixed-length records within transparent EF
size : tuple of (minimum_size, recommended_size)
"""
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, size=size)
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, size=size, **kwargs)
self.rec_len = rec_len
def decode_record_hex(self, raw_hex_data: str) -> dict:
@@ -1109,7 +1214,7 @@ class BerTlvEF(CardEF):
self._cmd.poutput(data)
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None,
size={1, None}):
size={1, None}, **kwargs):
"""
Args:
fid : File Identifier (4 hex digits)
@@ -1119,7 +1224,7 @@ class BerTlvEF(CardEF):
parent : Parent CardFile object within filesystem hierarchy
size : tuple of (minimum_size, recommended_size)
"""
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent)
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, **kwargs)
self._construct = None
self.size = size
self.shell_commands = [self.ShellCommands()]
@@ -1138,6 +1243,8 @@ class RuntimeState(object):
self.card = card
self.selected_file = self.mf # type: CardDF
self.profile = profile
self.selected_file_fcp = None
self.selected_file_fcp_hex = None
# make sure the class and selection control bytes, which are specified
# by the card profile are used
@@ -1196,6 +1303,21 @@ class RuntimeState(object):
pass
return apps_taken
def selected_file_descriptor_byte(self) -> dict:
return self.selected_file_fcp['file_descriptor']['file_descriptor_byte']
def selected_file_shareable(self) -> bool:
return self.selected_file_descriptor_byte()['shareable']
def selected_file_structure(self) -> str:
return self.selected_file_descriptor_byte()['structure']
def selected_file_type(self) -> str:
return self.selected_file_descriptor_byte()['file_type']
def selected_file_num_of_rec(self) -> Optional[int]:
return self.selected_file_fcp['file_descriptor'].get('num_of_rec')
def reset(self, cmd_app=None) -> Hexstr:
"""Perform physical card reset and obtain ATR.
Args:
@@ -1267,11 +1389,11 @@ class RuntimeState(object):
raise RuntimeError("%s: %s - %s" % (swm.sw_actual, k[0], k[1]))
select_resp = self.selected_file.decode_select_response(data)
if (select_resp['file_descriptor']['file_type'] == 'df'):
if (select_resp['file_descriptor']['file_descriptor_byte']['file_type'] == 'df'):
f = CardDF(fid=fid, sfid=None, name="DF." + str(fid).upper(),
desc="dedicated file, manually added at runtime")
else:
if (select_resp['file_descriptor']['structure'] == 'transparent'):
if (select_resp['file_descriptor']['file_descriptor_byte']['structure'] == 'transparent'):
f = TransparentEF(fid=fid, sfid=None, name="EF." + str(fid).upper(),
desc="elementary file, manually added at runtime")
else:
@@ -1282,6 +1404,45 @@ class RuntimeState(object):
self.selected_file = f
return select_resp
def _select_pre(self, cmd_app):
# unregister commands of old file
if cmd_app and self.selected_file.shell_commands:
for c in self.selected_file.shell_commands:
cmd_app.unregister_command_set(c)
def _select_post(self, cmd_app):
# register commands of new file
if cmd_app and self.selected_file.shell_commands:
for c in self.selected_file.shell_commands:
cmd_app.register_command_set(c)
def select_file(self, file: CardFile, cmd_app=None):
"""Select a file (EF, DF, ADF, MF, ...).
Args:
file : CardFile [or derived class] instance
cmd_app : Command Application State (for unregistering old file commands)
"""
# we need to find a path from our self.selected_file to the destination
inter_path = self.selected_file.build_select_path_to(file)
if not inter_path:
raise RuntimeError('Cannot determine path from %s to %s' % (self.selected_file, file))
self._select_pre(cmd_app)
for p in inter_path:
try:
if isinstance(p, CardADF):
(data, sw) = self.card.select_adf_by_aid(p.aid)
else:
(data, sw) = self.card._scc.select_file(p.fid)
self.selected_file = p
except SwMatchError as swm:
self._select_post(cmd_app)
raise(swm)
self._select_post(cmd_app)
def select(self, name: str, cmd_app=None):
"""Select a file (EF, DF, ADF, MF, ...).
@@ -1289,14 +1450,27 @@ class RuntimeState(object):
name : Name of file to select
cmd_app : Command Application State (for unregistering old file commands)
"""
# handling of entire paths with multiple directories/elements
if '/' in name:
prev_sel_file = self.selected_file
pathlist = name.split('/')
# treat /DF.GSM/foo like MF/DF.GSM/foo
if pathlist[0] == '':
pathlist[0] = 'MF'
try:
for p in pathlist:
self.select(p, cmd_app)
return
except Exception as e:
# if any intermediate step fails, go back to where we were
self.select_file(prev_sel_file, cmd_app)
raise e
sels = self.selected_file.get_selectables()
if is_hex(name):
name = name.lower()
# unregister commands of old file
if cmd_app and self.selected_file.shell_commands:
for c in self.selected_file.shell_commands:
cmd_app.unregister_command_set(c)
self._select_pre(cmd_app)
if name in sels:
f = sels[name]
@@ -1314,14 +1488,11 @@ class RuntimeState(object):
select_resp = f.decode_select_response(data)
else:
select_resp = self.probe_file(name, cmd_app)
# store the decoded FCP for later reference
# store the raw + decoded FCP for later reference
self.selected_file_fcp_hex = data
self.selected_file_fcp = select_resp
# register commands of new file
if cmd_app and self.selected_file.shell_commands:
for c in self.selected_file.shell_commands:
cmd_app.register_command_set(c)
self._select_post(cmd_app)
return select_resp
def status(self):
@@ -1329,6 +1500,10 @@ class RuntimeState(object):
(data, sw) = self.card._scc.status()
return self.selected_file.decode_select_response(data)
def get_file_for_selectable(self, name: str):
sels = self.selected_file.get_selectables()
return sels[name]
def activate_file(self, name: str):
"""Request ACTIVATE FILE of specified file."""
sels = self.selected_file.get_selectables()

256
pySim/global_platform.py Normal file
View File

@@ -0,0 +1,256 @@
# coding=utf-8
"""Partial Support for GlobalPLatform Card Spec (currently 2.1.1)
(C) 2022 by Harald Welte <laforge@osmocom.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from typing import Optional, List, Dict, Tuple
from construct import Optional as COptional
from construct import *
from bidict import bidict
from pySim.construct import *
from pySim.utils import *
from pySim.filesystem import *
from pySim.tlv import *
from pySim.profile import CardProfile
sw_table = {
'Warnings': {
'6200': 'Logical Channel already closed',
'6283': 'Card Life Cycle State is CARD_LOCKED',
'6310': 'More data available',
},
'Execution errors': {
'6400': 'No specific diagnosis',
'6581': 'Memory failure',
},
'Checking errors': {
'6700': 'Wrong length in Lc',
},
'Functions in CLA not supported': {
'6881': 'Logical channel not supported or active',
'6882': 'Secure messaging not supported',
},
'Command not allowed': {
'6982': 'Security Status not satisfied',
'6985': 'Conditions of use not satisfied',
},
'Wrong parameters': {
'6a80': 'Incorrect values in command data',
'6a81': 'Function not supported e.g. card Life Cycle State is CARD_LOCKED',
'6a82': 'Application not found',
'6a84': 'Not enough memory space',
'6a86': 'Incorrect P1 P2',
'6a88': 'Referenced data not found',
},
'GlobalPlatform': {
'6d00': 'Invalid instruction',
'6e00': 'Invalid class',
},
'Application errors': {
'9484': 'Algorithm not supported',
'9485': 'Invalid key check value',
},
}
# GlobalPlatform 2.1.1 Section 9.1.6
KeyType = Enum(Byte, des=0x80,
rsa_public_exponent_e_cleartex=0xA0,
rsa_modulus_n_cleartext=0xA1,
rsa_modulus_n=0xA2,
rsa_private_exponent_d=0xA3,
rsa_chines_remainder_p=0xA4,
rsa_chines_remainder_q=0xA5,
rsa_chines_remainder_pq=0xA6,
rsa_chines_remainder_dpi=0xA7,
rsa_chines_remainder_dqi=0xA8,
not_available=0xff)
# GlobalPlatform 2.1.1 Section 9.3.3.1
# example:
# e0 48
# c0 04 01708010
# c0 04 02708010
# c0 04 03708010
# c0 04 01018010
# c0 04 02018010
# c0 04 03018010
# c0 04 01028010
# c0 04 02028010
# c0 04 03028010
# c0 04 01038010
# c0 04 02038010
# c0 04 03038010
class KeyInformationData(BER_TLV_IE, tag=0xc0):
_construct = Struct('key_identifier'/Byte, 'key_version_number'/Byte,
'key_types'/GreedyRange(KeyType))
class KeyInformation(BER_TLV_IE, tag=0xe0, nested=[KeyInformationData]):
pass
# card data sample, returned in response to GET DATA (80ca006600):
# 66 31
# 73 2f
# 06 07
# 2a864886fc6b01
# 60 0c
# 06 0a
# 2a864886fc6b02020101
# 63 09
# 06 07
# 2a864886fc6b03
# 64 0b
# 06 09
# 2a864886fc6b040215
# GlobalPlatform 2.1.1 Table F-1
class ObjectIdentifier(BER_TLV_IE, tag=0x06):
_construct = GreedyBytes
class CardManagementTypeAndVersion(BER_TLV_IE, tag=0x60, nested=[ObjectIdentifier]):
pass
class CardIdentificationScheme(BER_TLV_IE, tag=0x63, nested=[ObjectIdentifier]):
pass
class SecureChannelProtocolOfISD(BER_TLV_IE, tag=0x64, nested=[ObjectIdentifier]):
pass
class CardConfigurationDetails(BER_TLV_IE, tag=0x65):
_construct = GreedyBytes
class CardChipDetails(BER_TLV_IE, tag=0x66):
_construct = GreedyBytes
class CardRecognitionData(BER_TLV_IE, tag=0x73, nested=[ObjectIdentifier,
CardManagementTypeAndVersion,
CardIdentificationScheme,
SecureChannelProtocolOfISD,
CardConfigurationDetails,
CardChipDetails]):
pass
class CardData(BER_TLV_IE, tag=0x66, nested=[CardRecognitionData]):
pass
# GlobalPlatform 2.1.1 Table F-2
class SecureChannelProtocolOfSelectedSD(BER_TLV_IE, tag=0x64, nested=[ObjectIdentifier]):
pass
class SecurityDomainMgmtData(BER_TLV_IE, tag=0x73, nested=[CardManagementTypeAndVersion,
CardIdentificationScheme,
SecureChannelProtocolOfSelectedSD,
CardConfigurationDetails,
CardChipDetails]):
pass
# GlobalPlatform 2.1.1 Section 9.1.1
IsdLifeCycleState = Enum(Byte, op_ready=0x01, initialized=0x07, secured=0x0f,
card_locked = 0x7f, terminated=0xff)
# GlobalPlatform 2.1.1 Section 9.9.3.1
class ApplicationID(BER_TLV_IE, tag=0x84):
_construct = GreedyBytes
# GlobalPlatform 2.1.1 Section 9.9.3.1
class SecurityDomainManagementData(BER_TLV_IE, tag=0x73):
_construct = GreedyBytes
# GlobalPlatform 2.1.1 Section 9.9.3.1
class ApplicationProductionLifeCycleData(BER_TLV_IE, tag=0x9f6e):
_construct = GreedyBytes
# GlobalPlatform 2.1.1 Section 9.9.3.1
class MaximumLengthOfDataFieldInCommandMessage(BER_TLV_IE, tag=0x9f65):
_construct = GreedyInteger()
# GlobalPlatform 2.1.1 Section 9.9.3.1
class ProprietaryData(BER_TLV_IE, tag=0xA5, nested=[SecurityDomainManagementData,
ApplicationProductionLifeCycleData,
MaximumLengthOfDataFieldInCommandMessage]):
pass
# GlobalPlatform 2.1.1 Section 9.9.3.1
class FciTemplate(BER_TLV_IE, tag=0x6f, nested=[ApplicationID, SecurityDomainManagementData,
ApplicationProductionLifeCycleData,
MaximumLengthOfDataFieldInCommandMessage,
ProprietaryData]):
pass
class IssuerIdentificationNumber(BER_TLV_IE, tag=0x42):
_construct = BcdAdapter(GreedyBytes)
class CardImageNumber(BER_TLV_IE, tag=0x45):
_construct = BcdAdapter(GreedyBytes)
class SequenceCounterOfDefaultKvn(BER_TLV_IE, tag=0xc1):
_construct = GreedyInteger()
class ConfirmationCounter(BER_TLV_IE, tag=0xc2):
_construct = GreedyInteger()
# Collection of all the data objects we can get from GET DATA
class DataCollection(TLV_IE_Collection, nested=[IssuerIdentificationNumber,
CardImageNumber,
CardData,
KeyInformation,
SequenceCounterOfDefaultKvn,
ConfirmationCounter]):
pass
def decode_select_response(resp_hex: str) -> object:
t = FciTemplate()
t.from_tlv(h2b(resp_hex))
d = t.to_dict()
return flatten_dict_lists(d['fci_template'])
# Application Dedicated File of a Security Domain
class ADF_SD(CardADF):
def __init__(self, aid: str, name: str, desc: str):
super().__init__(aid=aid, fid=None, sfid=None, name=name, desc=desc)
self.shell_commands += [self.AddlShellCommands()]
@staticmethod
def decode_select_response(res_hex: str) -> object:
return decode_select_response(res_hex)
@with_default_category('Application-Specific Commands')
class AddlShellCommands(CommandSet):
def __init__(self):
super().__init__()
def do_get_data(self, opts):
tlv_cls_name = opts.arg_list[0]
tlv_cls = DataCollection().members_by_name[tlv_cls_name]
(data, sw) = self._cmd.card._scc.get_data(cla=0x80, tag=tlv_cls.tag)
ie = tlv_cls()
ie.from_tlv(h2b(data))
self._cmd.poutput_json(ie.to_dict())
def complete_get_data(self, text, line, begidx, endidx) -> List[str]:
#data_dict = {camel_to_snake(str(x.__name__)): x for x in DataCollection.possible_nested}
data_dict = {str(x.__name__): x for x in DataCollection.possible_nested}
index_dict = {1: data_dict}
return self._cmd.index_based_complete(text, line, begidx, endidx, index_dict=index_dict)
# Card Application of a Security Domain
class CardApplicationSD(CardApplication):
def __init__(self, aid: str, name: str, desc: str):
super().__init__(name, adf=ADF_SD(aid, name, desc), sw=sw_table)
# Card Application of Issuer Security Domain
class CardApplicationISD(CardApplicationSD):
# FIXME: ISD AID is not static, but could be different. One can select the empty
# application using '00a4040000' and then parse the response FCI to get the ISD AID
def __init__(self, aid='a000000003000000'):
super().__init__(aid=aid, name='ADF.ISD', desc='Issuer Security Domain')
#class CardProfileGlobalPlatform(CardProfile):
# ORDER = 23
#
# def __init__(self, name='GlobalPlatform'):
# super().__init__(name, desc='GlobalPlatfomr 2.1.1', cla=['00','80','84'], sw=sw_table)

View File

@@ -24,56 +24,38 @@ from pySim.filesystem import *
from pySim.tlv import *
# Table 91 + Section 8.2.1.2
class ApplicationId(BER_TLV_IE, tag=0x4f):
_construct = GreedyBytes
# Table 91
class ApplicationLabel(BER_TLV_IE, tag=0x50):
_construct = GreedyBytes
# Table 91 + Section 5.3.1.2
class FileReference(BER_TLV_IE, tag=0x51):
_construct = GreedyBytes
# Table 91
class CommandApdu(BER_TLV_IE, tag=0x52):
_construct = GreedyBytes
# Table 91
class DiscretionaryData(BER_TLV_IE, tag=0x53):
_construct = GreedyBytes
# Table 91
class DiscretionaryTemplate(BER_TLV_IE, tag=0x73):
_construct = GreedyBytes
# Table 91 + RFC1738 / RFC2396
class URL(BER_TLV_IE, tag=0x5f50):
_construct = GreedyString('ascii')
# Table 91
class ApplicationRelatedDOSet(BER_TLV_IE, tag=0x61):
_construct = GreedyBytes
# Section 8.2.1.3 Application Template
class ApplicationTemplate(BER_TLV_IE, tag=0x61, nested=[ApplicationId, ApplicationLabel, FileReference,
CommandApdu, DiscretionaryData, DiscretionaryTemplate, URL,
ApplicationRelatedDOSet]):

View File

@@ -232,6 +232,8 @@ class TLV_IE(IE):
return self._encode_tag() + self._encode_len(val) + val
def from_tlv(self, do: bytes):
if len(do) == 0:
return {}, b''
(rawtag, remainder) = self.__class__._parse_tag_raw(do)
if rawtag:
if rawtag != self.tag:

View File

@@ -17,8 +17,8 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from pytlv.TLV import *
from construct import *
from construct import Optional as COptional
from pySim.construct import *
from pySim.utils import *
from pySim.filesystem import *
@@ -78,131 +78,177 @@ ts_102_22x_cmdset = CardCommandSet('TS 102 22x', [
CardCommand('RESIZE FILE', 0xD4, ['8X', 'CX']),
])
# ETSI TS 102 221 11.1.1.4.2
class FileSize(BER_TLV_IE, tag=0x80):
_construct = GreedyInteger()
FCP_TLV_MAP = {
'82': 'file_descriptor',
'83': 'file_identifier',
'84': 'df_name',
'A5': 'proprietary_info',
'8A': 'life_cycle_status_int',
'8B': 'security_attrib_ref_expanded',
'8C': 'security_attrib_compact',
'AB': 'security_attrib_espanded',
'C6': 'pin_status_template_do',
'80': 'file_size',
'81': 'total_file_size',
'88': 'short_file_id',
}
# ETSI TS 102 221 11.1.1.4.6
FCP_Proprietary_TLV_MAP = {
'80': 'uicc_characteristics',
'81': 'application_power_consumption',
'82': 'minimum_app_clock_freq',
'83': 'available_memory',
'84': 'file_details',
'85': 'reserved_file_size',
'86': 'maximum_file_size',
'87': 'suported_system_commands',
'88': 'specific_uicc_env_cond',
'89': 'p2p_cat_secured_apdu',
# Additional private TLV objects (bits b7 and b8 of the first byte of the tag set to '1')
}
# ETSI TS 102 221 11.1.1.4.2
class TotalFileSize(BER_TLV_IE, tag=0x81):
_construct = GreedyInteger()
# ETSI TS 102 221 11.1.1.4.3
class FileDescriptor(BER_TLV_IE, tag=0x82):
class BerTlvAdapter(Adapter):
def _parse(self, obj, context, path):
if obj == 0x39:
return 'ber_tlv'
raise ValidationError
def _build(self, obj, context, path):
if obj == 'ber_tlv':
return 0x39
raise ValidationError
FDB = Select(BitStruct(Const(0, Bit), 'shareable'/Flag, 'structure'/BerTlvAdapter(Const(0x39, BitsInteger(6)))),
BitStruct(Const(0, Bit), 'shareable'/Flag, 'file_type'/Enum(BitsInteger(3), working_ef=0, internal_ef=1, df=7),
'structure'/Enum(BitsInteger(3), no_info_given=0, transparent=1, linear_fixed=2, cyclic=6))
)
_construct = Struct('file_descriptor_byte'/FDB, Const(b'\x21'),
'record_len'/COptional(Int16ub), 'num_of_rec'/COptional(Int8ub))
def interpret_file_descriptor(in_hex):
in_bin = h2b(in_hex)
out = {}
ft_dict = {
0: 'working_ef',
1: 'internal_ef',
7: 'df'
}
fs_dict = {
0: 'no_info_given',
1: 'transparent',
2: 'linear_fixed',
6: 'cyclic',
0x39: 'ber_tlv',
}
fdb = in_bin[0]
ftype = (fdb >> 3) & 7
if fdb & 0xbf == 0x39:
fstruct = 0x39
else:
fstruct = fdb & 7
out['shareable'] = True if fdb & 0x40 else False
out['file_type'] = ft_dict[ftype] if ftype in ft_dict else ftype
out['structure'] = fs_dict[fstruct] if fstruct in fs_dict else fstruct
if len(in_bin) >= 5:
out['record_len'] = int.from_bytes(in_bin[2:4], 'big')
out['num_of_rec'] = int.from_bytes(in_bin[4:5], 'big')
return out
# ETSI TS 102 221 11.1.1.4.4
class FileIdentifier(BER_TLV_IE, tag=0x83):
_construct = HexAdapter(GreedyBytes)
# ETSI TS 102 221 11.1.1.4.5
class DfName(BER_TLV_IE, tag=0x84):
_construct = HexAdapter(GreedyBytes)
# ETSI TS 102 221 11.1.1.4.6.1
class UiccCharacteristics(BER_TLV_IE, tag=0x80):
_construct = GreedyBytes
# ETSI TS 102 221 11.1.1.4.6.2
class ApplicationPowerConsumption(BER_TLV_IE, tag=0x81):
_construct = Struct('voltage_class'/Int8ub,
'power_consumption_ma'/Int8ub,
'reference_freq_100k'/Int8ub)
# ETSI TS 102 221 11.1.1.4.6.3
class MinApplicationClockFrequency(BER_TLV_IE, tag=0x82):
_construct = Int8ub
# ETSI TS 102 221 11.1.1.4.6.4
class AvailableMemory(BER_TLV_IE, tag=0x83):
_construct = GreedyInteger()
# ETSI TS 102 221 11.1.1.4.6.5
class FileDetails(BER_TLV_IE, tag=0x84):
_construct = FlagsEnum(Byte, der_coding_only=1)
# ETSI TS 102 221 11.1.1.4.6.6
class ReservedFileSize(BER_TLV_IE, tag=0x85):
_construct = GreedyInteger()
# ETSI TS 102 221 11.1.1.4.6.7
class MaximumFileSize(BER_TLV_IE, tag=0x86):
_construct = GreedyInteger()
# ETSI TS 102 221 11.1.1.4.6.8
class SupportedFilesystemCommands(BER_TLV_IE, tag=0x87):
_construct = FlagsEnum(Byte, terminal_capability=1)
# ETSI TS 102 221 11.1.1.4.6.9
class SpecificUiccEnvironmentConditions(BER_TLV_IE, tag=0x88):
_construct = BitStruct('rfu'/BitsRFU(4),
'high_humidity_supported'/Flag,
'temperature_class'/Enum(BitsInteger(3), standard=0, class_A=1, class_B=2, class_C=3))
# ETSI TS 102 221 11.1.1.4.6.10
class Platform2PlatformCatSecuredApdu(BER_TLV_IE, tag=0x89):
_construct = GreedyBytes
# sysmoISIM-SJA2 specific
class ToolkitAccessConditions(BER_TLV_IE, tag=0xD2):
_construct = FlagsEnum(Byte, rfm_create=1, rfm_delete_terminate=2, other_applet_create=4,
other_applet_delete_terminate=8)
# ETSI TS 102 221 11.1.1.4.6.0
class ProprietaryInformation(BER_TLV_IE, tag=0xA5,
nested=[UiccCharacteristics, ApplicationPowerConsumption,
MinApplicationClockFrequency, AvailableMemory,
FileDetails, ReservedFileSize, MaximumFileSize,
SupportedFilesystemCommands, SpecificUiccEnvironmentConditions,
ToolkitAccessConditions]):
pass
# ETSI TS 102 221 11.1.1.4.7.1
class SecurityAttribCompact(BER_TLV_IE, tag=0x8c):
_construct = GreedyBytes
# ETSI TS 102 221 11.1.1.4.7.2
class SecurityAttribExpanded(BER_TLV_IE, tag=0xab):
_construct = GreedyBytes
# ETSI TS 102 221 11.1.1.4.7.3
class SecurityAttribReferenced(BER_TLV_IE, tag=0x8b):
# TODO: longer format with SEID
_construct = Struct('ef_arr_file_id'/HexAdapter(Bytes(2)), 'ef_arr_record_nr'/Int8ub)
# ETSI TS 102 221 11.1.1.4.8
class ShortFileIdentifier(BER_TLV_IE, tag=0x88):
# If the length of the TLV is 1, the SFI value is indicated in the 5 most significant bits (bits b8 to b4)
# of the TLV value field. In this case, bits b3 to b1 shall be set to 0
class Shift3RAdapter(Adapter):
def _decode(self, obj, context, path):
return obj >> 3
def _encode(self, obj, context, path):
return obj << 3
_construct = COptional(Shift3RAdapter(Byte))
# ETSI TS 102 221 11.1.1.4.9
class LifeCycleStatusInteger(BER_TLV_IE, tag=0x8A):
def _from_bytes(self, do: bytes):
lcsi = int.from_bytes(do, 'big')
if lcsi == 0x00:
ret = 'no_information'
elif lcsi == 0x01:
ret = 'creation'
elif lcsi == 0x03:
ret = 'initialization'
elif lcsi & 0x05 == 0x05:
ret = 'operational_activated'
elif lcsi & 0x05 == 0x04:
ret = 'operational_deactivated'
elif lcsi & 0xc0 == 0xc0:
ret = 'termination'
else:
ret = lcsi
self.decoded = ret
return self.decoded
def _to_bytes(self):
if self.decoded == 'no_information':
return b'\x00'
elif self.decoded == 'creation':
return b'\x01'
elif self.decoded == 'initialization':
return b'\x03'
elif self.decoded == 'operational_activated':
return b'\x05'
elif self.decoded == 'operational_deactivated':
return b'\x04'
elif self.decoded == 'termination':
return b'\x0c'
elif isinstance(self.decoded, int):
return self.decoded.to_bytes(1, 'big')
else:
raise ValueError
# ETSI TS 102 221 11.1.1.4.9
class PS_DO(BER_TLV_IE, tag=0x90):
_construct = GreedyBytes
class UsageQualifier_DO(BER_TLV_IE, tag=0x95):
_construct = GreedyBytes
class KeyReference(BER_TLV_IE, tag=0x83):
_construct = Byte
class PinStatusTemplate_DO(BER_TLV_IE, tag=0xC6, nested=[PS_DO, UsageQualifier_DO, KeyReference]):
pass
def interpret_life_cycle_sts_int(in_hex):
lcsi = int(in_hex, 16)
if lcsi == 0x00:
return 'no_information'
elif lcsi == 0x01:
return 'creation'
elif lcsi == 0x03:
return 'initialization'
elif lcsi & 0x05 == 0x05:
return 'operational_activated'
elif lcsi & 0x05 == 0x04:
return 'operational_deactivated'
elif lcsi & 0xc0 == 0xc0:
return 'termination'
else:
return in_hex
# ETSI TS 102 221 11.1.1.4.10
FCP_Pin_Status_TLV_MAP = {
'90': 'ps_do',
'95': 'usage_qualifier',
'83': 'key_reference',
}
def interpret_ps_templ_do(in_hex):
# cannot use the 'TLV' parser due to repeating tags
#psdo_tlv = TLV(FCP_Pin_Status_TLV_MAP)
# return psdo_tlv.parse(in_hex)
return in_hex
# 'interpreter' functions for each tag
FCP_interpreter_map = {
'80': lambda x: int(x, 16),
'82': interpret_file_descriptor,
'8A': interpret_life_cycle_sts_int,
'C6': interpret_ps_templ_do,
}
FCP_prorietary_interpreter_map = {
'83': lambda x: int(x, 16),
}
# pytlv unfortunately doesn't have a setting using which we can make it
# accept unknown tags. It also doesn't raise a specific exception type but
# just the generic ValueError, so we cannot ignore those either. Instead,
# we insert a dict entry for every possible proprietary tag permitted
def fixup_fcp_proprietary_tlv_map(tlv_map):
if 'D0' in tlv_map:
return
for i in range(0xc0, 0xff):
i_hex = i2h([i]).upper()
tlv_map[i_hex] = 'proprietary_' + i_hex
# Other non-standard TLV objects found on some cards
tlv_map['9B'] = 'target_ef' # for sysmoUSIM-SJS1
class FcpTemplate(BER_TLV_IE, tag=0x62, nested=[FileSize, TotalFileSize, FileDescriptor, FileIdentifier,
DfName, ProprietaryInformation, SecurityAttribCompact,
SecurityAttribExpanded, SecurityAttribReferenced,
ShortFileIdentifier, LifeCycleStatusInteger,
PinStatusTemplate_DO]):
pass
def tlv_key_replace(inmap, indata):
@@ -223,8 +269,6 @@ def tlv_val_interpret(inmap, indata):
return {d[0]: newval(inmap, d[0], d[1]) for d in indata.items()}
# ETSI TS 102 221 Section 9.2.7 + ISO7816-4 9.3.3/9.3.4
class _AM_DO_DF(DataObject):
def __init__(self):
super().__init__('access_mode', 'Access Mode', tag=0x80)
@@ -436,8 +480,6 @@ class CRT_DO(DataObject):
return b'\x83\x01' + pin.to_bytes(1, 'big') + b'\x95\x01\x08'
# ISO7816-4 9.3.3 Table 33
class SecCondByte_DO(DataObject):
def __init__(self, tag=0x9d):
super().__init__('security_condition_byte', tag=tag)
@@ -530,8 +572,6 @@ SC_DO = DataObjectChoice('security_condition', 'Security Condition',
OR_DO, AND_DO, NOT_DO])
# TS 102 221 Section 13.1
class EF_DIR(LinFixedEF):
class ApplicationLabel(BER_TLV_IE, tag=0x50):
# TODO: UCS-2 coding option as per Annex A of TS 102 221
@@ -551,8 +591,6 @@ class EF_DIR(LinFixedEF):
self._tlv = EF_DIR.ApplicationTemplate
# TS 102 221 Section 13.2
class EF_ICCID(TransparentEF):
def __init__(self, fid='2fe2', sfid=0x02, name='EF.ICCID', desc='ICC Identification'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size={10, 10})
@@ -564,8 +602,6 @@ class EF_ICCID(TransparentEF):
return enc_iccid(abstract['iccid'])
# TS 102 221 Section 13.3
class EF_PL(TransRecEF):
def __init__(self, fid='2f05', sfid=0x05, name='EF.PL', desc='Preferred Languages'):
super().__init__(fid, sfid=sfid, name=name,
@@ -636,6 +672,11 @@ class EF_ARR(LinFixedEF):
# 'un-flattening' decoder, and hence would be unable to encode :(
return dec[0]
def _encode_record_bin(self, in_json):
# we can only guess if we should decode for EF or DF here :(
arr_seq = DataObjectSequence('arr', sequence=[AM_DO_EF, SC_DO])
return arr_seq.encode_multi(in_json)
@with_default_category('File-Specific Commands')
class AddlShellCommands(CommandSet):
def __init__(self):
@@ -651,7 +692,7 @@ class EF_ARR(LinFixedEF):
@cmd2.with_argparser(LinFixedEF.ShellCommands.read_recs_dec_parser)
def do_read_arr_records(self, opts):
"""Read + decode all EF.ARR records in flattened, human-friendly form."""
num_of_rec = self._cmd.rs.selected_file_fcp['file_descriptor']['num_of_rec']
num_of_rec = self._cmd.rs.selected_file_num_of_rec()
# collect all results in list so they are rendered as JSON list when printing
data_list = []
for recnr in range(1, 1 + num_of_rec):
@@ -759,23 +800,10 @@ class CardProfileUICC(CardProfile):
@staticmethod
def decode_select_response(resp_hex: str) -> object:
"""ETSI TS 102 221 Section 11.1.1.3"""
fixup_fcp_proprietary_tlv_map(FCP_Proprietary_TLV_MAP)
resp_hex = resp_hex.upper()
# outer layer
fcp_base_tlv = TLV(['62'])
fcp_base = fcp_base_tlv.parse(resp_hex)
# actual FCP
fcp_tlv = TLV(FCP_TLV_MAP)
fcp = fcp_tlv.parse(fcp_base['62'])
# further decode the proprietary information
if 'A5' in fcp:
prop_tlv = TLV(FCP_Proprietary_TLV_MAP)
prop = prop_tlv.parse(fcp['A5'])
fcp['A5'] = tlv_val_interpret(FCP_prorietary_interpreter_map, prop)
fcp['A5'] = tlv_key_replace(FCP_Proprietary_TLV_MAP, fcp['A5'])
# finally make sure we get human-readable keys in the output dict
r = tlv_val_interpret(FCP_interpreter_map, fcp)
return tlv_key_replace(FCP_TLV_MAP, r)
t = FcpTemplate()
t.from_tlv(h2b(resp_hex))
d = t.to_dict()
return flatten_dict_lists(d['fcp_template'])
@staticmethod
def match_with_card(scc: SimCardCommands) -> bool:

206
pySim/ts_102_222.py Normal file
View File

@@ -0,0 +1,206 @@
#!/usr/bin/env python3
# Interactive shell for working with SIM / UICC / USIM / ISIM cards
#
# (C) 2022 by Harald Welte <laforge@osmocom.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import List
import cmd2
from cmd2 import style, fg, bg
from cmd2 import CommandSet, with_default_category, with_argparser
import argparse
from pySim.ts_31_102 import EF_UST_map, EF_USIM_ADF_map
from pySim.ts_31_103 import EF_IST_map, EF_ISIM_ADF_map
from pySim.exceptions import *
from pySim.utils import h2b, swap_nibbles, b2h, JsonEncoder
from pySim.ts_102_221 import *
@with_default_category('TS 102 222 Administrative Commands')
class Ts102222Commands(CommandSet):
"""Administrative commands for telecommunication applications."""
def __init__(self):
super().__init__()
delfile_parser = argparse.ArgumentParser()
delfile_parser.add_argument('--force-delete', action='store_true',
help='I really want to permanently delete the file. I know pySim cannot re-create it yet!')
delfile_parser.add_argument('NAME', type=str, help='File name or FID to delete')
@cmd2.with_argparser(delfile_parser)
def do_delete_file(self, opts):
"""Delete the specified file. DANGEROUS! See TS 102 222 Section 6.4.
This will permanently delete the specified file from the card.
pySim has no support to re-create files yet, and even if it did, your card may not allow it!"""
if not opts.force_delete:
self._cmd.perror("Refusing to permanently delete the file, please read the help text.")
return
f = self._cmd.rs.get_file_for_selectable(opts.NAME)
(data, sw) = self._cmd.card._scc.delete_file(f.fid)
def complete_delete_file(self, text, line, begidx, endidx) -> List[str]:
"""Command Line tab completion for DELETE FILE"""
index_dict = {1: self._cmd.rs.selected_file.get_selectable_names()}
return self._cmd.index_based_complete(text, line, begidx, endidx, index_dict=index_dict)
termdf_parser = argparse.ArgumentParser()
termdf_parser.add_argument('--force', action='store_true',
help='I really want to terminate the file. I know I can not recover from it!')
termdf_parser.add_argument('NAME', type=str, help='File name or FID')
@cmd2.with_argparser(termdf_parser)
def do_terminate_df(self, opts):
"""Terminate the specified DF. DANGEROUS! See TS 102 222 6.7.
This is a permanent, one-way operation on the card. There is no undo, you can not recover
a terminated DF. The only permitted command for a terminated DF is the DLETE FILE command."""
if not opts.force:
self._cmd.perror("Refusing to terminate the file, please read the help text.")
return
f = self._cmd.rs.get_file_for_selectable(opts.NAME)
(data, sw) = self._cmd.card._scc.terminate_df(f.fid)
def complete_terminate_df(self, text, line, begidx, endidx) -> List[str]:
"""Command Line tab completion for TERMINATE DF"""
index_dict = {1: self._cmd.rs.selected_file.get_selectable_names()}
return self._cmd.index_based_complete(text, line, begidx, endidx, index_dict=index_dict)
@cmd2.with_argparser(termdf_parser)
def do_terminate_ef(self, opts):
"""Terminate the specified EF. DANGEROUS! See TS 102 222 6.8.
This is a permanent, one-way operation on the card. There is no undo, you can not recover
a terminated EF. The only permitted command for a terminated EF is the DLETE FILE command."""
if not opts.force:
self._cmd.perror("Refusing to terminate the file, please read the help text.")
return
f = self._cmd.rs.get_file_for_selectable(opts.NAME)
(data, sw) = self._cmd.card._scc.terminate_ef(f.fid)
def complete_terminate_ef(self, text, line, begidx, endidx) -> List[str]:
"""Command Line tab completion for TERMINATE EF"""
index_dict = {1: self._cmd.rs.selected_file.get_selectable_names()}
return self._cmd.index_based_complete(text, line, begidx, endidx, index_dict=index_dict)
tcard_parser = argparse.ArgumentParser()
tcard_parser.add_argument('--force-terminate-card', action='store_true',
help='I really want to permanently terminate the card. It will not be usable afterwards!')
@cmd2.with_argparser(tcard_parser)
def do_terminate_card_usage(self, opts):
"""Terminate the Card. SUPER DANGEROUS! See TS 102 222 Section 6.9.
This will permanently brick the card and can NOT be recovered from!"""
if not opts.force_terminate_card:
self._cmd.perror("Refusing to permanently terminate the card, please read the help text.")
return
(data, sw) = self._cmd.card._scc.terminate_card_usage()
create_parser = argparse.ArgumentParser()
create_parser.add_argument('FILE_ID', type=str, help='File Identifier as 4-character hex string')
create_parser._action_groups.pop()
create_required = create_parser.add_argument_group('required arguments')
create_optional = create_parser.add_argument_group('optional arguments')
create_required.add_argument('--ef-arr-file-id', required=True, type=str, help='Referenced Security: File Identifier of EF.ARR')
create_required.add_argument('--ef-arr-record-nr', required=True, type=int, help='Referenced Security: Record Number within EF.ARR')
create_required.add_argument('--file-size', required=True, type=int, help='Size of file in octets')
create_required.add_argument('--structure', required=True, type=str, choices=['transparent', 'linear_fixed', 'ber_tlv'],
help='Structure of the to-be-created EF')
create_optional.add_argument('--short-file-id', type=str, help='Short File Identifier as 2-digit hex string')
create_optional.add_argument('--shareable', action='store_true', help='Should the file be shareable?')
create_optional.add_argument('--record-length', type=int, help='Length of each record in octets')
@cmd2.with_argparser(create_parser)
def do_create_ef(self, opts):
"""Create a new EF below the currently selected DF. Requires related privileges."""
file_descriptor = {
'file_descriptor_byte': {
'shareable': opts.shareable,
'file_type': 'working_ef',
'structure': opts.structure,
}
}
if opts.structure == 'linear_fixed':
if not opts.record_length:
self._cmd.perror("you must specify the --record-length for linear fixed EF")
return
file_descriptor['record_len'] = opts.record_length
elif opts.structure == 'ber_tlv':
self._cmd.perror("BER-TLV creation not yet fully supported, sorry")
return
ies = [FileDescriptor(decoded=file_descriptor), FileIdentifier(decoded=opts.FILE_ID),
LifeCycleStatusInteger(decoded='operational_activated'),
SecurityAttribReferenced(decoded={'ef_arr_file_id': opts.ef_arr_file_id,
'ef_arr_record_nr': opts.ef_arr_record_nr }),
FileSize(decoded=opts.file_size),
ShortFileIdentifier(decoded=opts.short_file_id),
]
fcp = FcpTemplate(children=ies)
(data, sw) = self._cmd.card._scc.create_file(b2h(fcp.to_tlv()))
# the newly-created file is automatically selected but our runtime state knows nothing of it
self._cmd.rs.select_file(self._cmd.rs.selected_file)
createdf_parser = argparse.ArgumentParser()
createdf_parser.add_argument('FILE_ID', type=str, help='File Identifier as 4-character hex string')
createdf_parser._action_groups.pop()
createdf_required = createdf_parser.add_argument_group('required arguments')
createdf_optional = createdf_parser.add_argument_group('optional arguments')
createdf_sja_optional = createdf_parser.add_argument_group('sysmoISIM-SJA optional arguments')
createdf_required.add_argument('--ef-arr-file-id', required=True, type=str, help='Referenced Security: File Identifier of EF.ARR')
createdf_required.add_argument('--ef-arr-record-nr', required=True, type=int, help='Referenced Security: Record Number within EF.ARR')
createdf_optional.add_argument('--shareable', action='store_true', help='Should the file be shareable?')
createdf_optional.add_argument('--aid', type=str, help='Application ID (creates an ADF, instead of a DF)')
# mandatory by spec, but ignored by several OS, so don't force the user
createdf_optional.add_argument('--total-file-size', type=int, help='Physical memory allocated for DF/ADi in octets')
createdf_sja_optional.add_argument('--permit-rfm-create', action='store_true')
createdf_sja_optional.add_argument('--permit-rfm-delete-terminate', action='store_true')
createdf_sja_optional.add_argument('--permit-other-applet-create', action='store_true')
createdf_sja_optional.add_argument('--permit-other-applet-delete-terminate', action='store_true')
@cmd2.with_argparser(createdf_parser)
def do_create_df(self, opts):
"""Create a new DF below the currently selected DF. Requires related privileges."""
file_descriptor = {
'file_descriptor_byte': {
'shareable': opts.shareable,
'file_type': 'df',
'structure': 'no_info_given',
}
}
ies = []
ies.append(FileDescriptor(decoded=file_descriptor))
ies.append(FileIdentifier(decoded=opts.FILE_ID))
if opts.aid:
ies.append(DfName(decoded=opts.aid))
ies.append(LifeCycleStatusInteger(decoded='operational_activated'))
ies.append(SecurityAttribReferenced(decoded={'ef_arr_file_id': opts.ef_arr_file_id,
'ef_arr_record_nr': opts.ef_arr_record_nr }))
if opts.total_file_size:
ies.append(TotalFileSize(decoded=opts.total_file_size))
# TODO: Spec states PIN Status Template DO is mandatory
if opts.permit_rfm_create or opts.permit_rfm_delete_terminate or opts.permit_other_applet_create or opts.permit_other_applet_delete_terminate:
toolkit_ac = {
'rfm_create': opts.permit_rfm_create,
'rfm_delete_terminate': opts.permit_rfm_delete_terminate,
'other_applet_create': opts.permit_other_applet_create,
'other_applet_delete_terminate': opts.permit_other_applet_delete_terminate,
}
ies.append(ProprietaryInformation(children=[ToolkitAccessConditions(decoded=toolkit_ac)]))
fcp = FcpTemplate(children=ies)
(data, sw) = self._cmd.card._scc.create_file(b2h(fcp.to_tlv()))
# the newly-created file is automatically selected but our runtime state knows nothing of it
self._cmd.rs.select_file(self._cmd.rs.selected_file)

View File

@@ -304,7 +304,6 @@ EF_USIM_ADF_map = {
# 3GPP TS 31.102 Section 4.4.11.4 (EF_5GS3GPPNSC)
class EF_5GS3GPPNSC(LinFixedEF):
class NgKSI(BER_TLV_IE, tag=0x80):
_construct = Int8ub
@@ -333,13 +332,11 @@ class EF_5GS3GPPNSC(LinFixedEF):
pass
def __init__(self, fid="4f03", sfid=0x03, name='EF.5GS3GPPNSC', rec_len={57, None},
desc='5GS 3GPP Access NAS Security Context'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
desc='5GS 3GPP Access NAS Security Context', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._tlv = EF_5GS3GPPNSC.FiveGSNasSecurityContext
# 3GPP TS 31.102 Section 4.4.11.6
class EF_5GAUTHKEYS(TransparentEF):
class K_AUSF(BER_TLV_IE, tag=0x80):
_construct = HexAdapter(GreedyBytes)
@@ -351,13 +348,11 @@ class EF_5GAUTHKEYS(TransparentEF):
pass
def __init__(self, fid='4f05', sfid=0x05, name='EF.5GAUTHKEYS', size={68, None},
desc='5G authentication keys'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='5G authentication keys', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self._tlv = EF_5GAUTHKEYS.FiveGAuthKeys
# 3GPP TS 31.102 Section 4.4.11.8
class ProtSchemeIdList(BER_TLV_IE, tag=0xa0):
# FIXME: 3GPP TS 24.501 Protection Scheme Identifier
# repeated sequence of (id, index) tuples
@@ -380,8 +375,6 @@ class HomeNetPubKeyList(BER_TLV_IE, tag=0xa1,
pass
# 3GPP TS 31.102 Section 4.4.11.6
class SUCI_CalcInfo(TLV_IE_Collection, nested=[ProtSchemeIdList, HomeNetPubKeyList]):
pass
@@ -389,8 +382,8 @@ class SUCI_CalcInfo(TLV_IE_Collection, nested=[ProtSchemeIdList, HomeNetPubKeyLi
# TS 31.102 4.4.11.8
class EF_SUCI_Calc_Info(TransparentEF):
def __init__(self, fid="4f07", sfid=0x07, name='EF.SUCI_Calc_Info', size={2, None},
desc='SUCI Calc Info'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='SUCI Calc Info', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
def _encode_prot_scheme_id_list(self, in_list):
out_bytes = [0xa0]
@@ -538,8 +531,6 @@ class EF_Keys(TransparentEF):
'ksi'/Int8ub, 'ck'/HexAdapter(Bytes(16)), 'ik'/HexAdapter(Bytes(16)))
# TS 31.102 Section 4.2.6
class EF_HPPLMN(TransparentEF):
def __init__(self, fid='6f31', sfid=0x12, name='EF.HPPLMN', size={1, 1},
desc='Higher Priority PLMN search period'):
@@ -547,14 +538,10 @@ class EF_HPPLMN(TransparentEF):
self._construct = Int8ub
# TS 31.102 Section 4.2.8
class EF_UServiceTable(TransparentEF):
def __init__(self, fid, sfid, name, desc, size, table):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size)
def __init__(self, fid, sfid, name, desc, size, table, **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self.table = table
# add those commands to the general commands of a TransparentEF
self.shell_commands += [self.AddlShellCommands()]
@staticmethod
def _bit_byte_offset_for_service(service: int) -> Tuple[int, int]:
@@ -567,7 +554,7 @@ class EF_UServiceTable(TransparentEF):
ret = {}
for i in range(0, len(in_bin)):
byte = in_bin[i]
for bitno in range(0, 7):
for bitno in range(0, 8):
service_nr = i * 8 + bitno + 1
ret[service_nr] = {
'activated': True if byte & (1 << bitno) else False
@@ -598,6 +585,52 @@ class EF_UServiceTable(TransparentEF):
out[byte_offset] |= (bit) << bit_offset
return out
def get_active_services(self, cmd):
# obtain list of currently active services
(service_data, sw) = cmd.rs.read_binary_dec()
active_services = []
for s in service_data.keys():
if service_data[s]['activated']:
active_services.append(s)
return active_services
def ust_service_check(self, cmd):
"""Check consistency between services of this file and files present/activated"""
num_problems = 0
# obtain list of currently active services
active_services = self.get_active_services(cmd)
# iterate over all the service-constraints we know of
files_by_service = self.parent.files_by_service
try:
for s in sorted(files_by_service.keys()):
active_str = 'active' if s in active_services else 'inactive'
cmd.poutput("Checking service No %u (%s)" % (s, active_str))
for f in files_by_service[s]:
should_exist = f.should_exist_for_services(active_services)
try:
cmd.rs.select_file(f)
sw = None
exists = True
except SwMatchError as e:
sw = str(e)
exists = False
if exists != should_exist:
num_problems += 1
if exists:
cmd.perror(" ERROR: File %s is selectable but should not!" % f)
else:
cmd.perror(" ERROR: File %s is not selectable (%s) but should!" % (f, sw))
finally:
# re-select the EF.UST
cmd.rs.select_file(self)
return num_problems
class EF_UST(EF_UServiceTable):
def __init__(self, **kwargs):
super().__init__(fid='6f38', sfid=0x04, name='EF.UST', desc='USIM Service Table', size={1,17}, table=EF_UST_map, **kwargs)
# add those commands to the general commands of a TransparentEF
self.shell_commands += [self.AddlShellCommands()]
@with_default_category('File-Specific Commands')
class AddlShellCommands(CommandSet):
def __init__(self):
@@ -611,9 +644,37 @@ class EF_UServiceTable(TransparentEF):
"""Deactivate a service within EF.UST"""
self._cmd.card.update_ust(int(arg), 0)
def do_ust_service_check(self, arg):
"""Check consistency between services of this file and files present/activated.
Many services determine if one or multiple files shall be present/activated or if they shall be
absent/deactivated. This performs a consistency check to ensure that no services are activated
for files that are not - and vice-versa, no files are activated for services that are not. Error
messages are printed for every inconsistency found."""
selected_file = self._cmd.rs.selected_file
num_problems = selected_file.ust_service_check(self._cmd)
# obtain list of currently active services
active_services = selected_file.get_active_services(self._cmd)
# Service n°46 can only be declared "available" if service n°45 is declared "available"
if 46 in active_services and not 45 in active_services:
self._cmd.perror("ERROR: Service 46 available, but it requires Service 45")
num_problems += 1
# Service n°125 shall only be taken into account if Service n°124 is declared "available"
if 125 in active_services and not 124 in active_services:
self._cmd.perror("ERROR: Service 125 is ignored as Service 124 not available")
num_problems += 1
# Service n°95, n°99 and n°115 shall not be declared "available" if an ISIM application is present on the UICC
non_isim_services = [95, 99, 115]
app_names = selected_file.get_mf().get_app_names()
if 'ADF.ISIM' in app_names:
for s in non_isim_services:
if s in active_services:
self._cmd.perror("ERROR: Service %u shall not be available as ISIM application is present" % s)
num_problems += 1
self._cmd.poutput("===> %u service / file inconsistencies detected" % num_problems)
# TS 31.103 Section 4.2.7 - *not* the same as DF.GSM/EF.ECC!
class EF_ECC(LinFixedEF):
cc_construct = Rpad(BcdAdapter(Rpad(Bytes(3))), pattern='f')
category_construct = FlagsEnum(Byte, police=1, ambulance=2, fire_brigade=3, marine_guard=4,
@@ -652,11 +713,9 @@ class EF_ECC(LinFixedEF):
class EF_LOCI(TransparentEF):
def __init__(self, fid='6f7e', sfid=0x0b, name='EF.LOCI', desc='Location information', size={11, 11}):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
self._construct = Struct('tmsi'/HexAdapter(Bytes(4)), 'lai'/HexAdapter(Bytes(5)), 'rfu'/Int8ub,
'lu_status'/Int8ub)
Lai = Struct('mcc_mnc'/BcdAdapter(Bytes(3)), 'lac'/HexAdapter(Bytes(2)))
self._construct = Struct('tmsi'/HexAdapter(Bytes(4)), 'lai'/Lai, 'rfu'/Int8ub, 'lu_status'/Int8ub)
# TS 31.102 Section 4.2.18
class EF_AD(TransparentEF):
class OP_MODE(enum.IntEnum):
normal = 0x00
@@ -680,8 +739,6 @@ class EF_AD(TransparentEF):
)
# TS 31.102 Section 4.2.23
class EF_PSLOCI(TransparentEF):
def __init__(self, fid='6f73', sfid=0x0c, name='EF.PSLOCI', desc='PS Location information', size={14, 14}):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
@@ -689,12 +746,10 @@ class EF_PSLOCI(TransparentEF):
'rai'/HexAdapter(Bytes(6)), 'rau_status'/Int8ub)
# TS 31.102 Section 4.2.33
class EF_ICI(CyclicEF):
def __init__(self, fid='6f80', sfid=0x14, name='EF.ICI', rec_len={28, 48},
desc='Incoming Call Information'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
desc='Incoming Call Information', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._construct = Struct('alpha_id'/Bytes(this._.total_len-28),
'len_of_bcd_contents'/Int8ub,
'ton_npi'/Int8ub,
@@ -707,12 +762,10 @@ class EF_ICI(CyclicEF):
'link_to_phonebook'/Bytes(3))
# TS 31.102 Section 4.2.34
class EF_OCI(CyclicEF):
def __init__(self, fid='6f81', sfid=0x15, name='EF.OCI', rec_len={27, 47},
desc='Outgoing Call Information'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
desc='Outgoing Call Information', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._construct = Struct('alpha_id'/Bytes(this._.total_len-27),
'len_of_bcd_contents'/Int8ub,
'ton_npi'/Int8ub,
@@ -724,81 +777,81 @@ class EF_OCI(CyclicEF):
'link_to_phonebook'/Bytes(3))
# TS 31.102 Section 4.2.35
class EF_ICT(CyclicEF):
def __init__(self, fid='6f82', sfid=None, name='EF.ICT', rec_len={3, 3},
desc='Incoming Call Timer'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
desc='Incoming Call Timer', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._construct = Struct('accumulated_call_timer'/Int24ub)
# TS 31.102 Section 4.2.38
class EF_CCP2(LinFixedEF):
def __init__(self, fid='6f4f', sfid=0x16, name='EF.CCP2', desc='Capability Configuration Parameters 2'):
super().__init__(fid=fid, sfid=sfid,
name=name, desc=desc, rec_len={15, None})
def __init__(self, fid='6f4f', sfid=0x16, name='EF.CCP2', desc='Capability Configuration Parameters 2', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len={15, None}, **kwargs)
# TS 31.102 Section 4.2.47
class EF_EST(EF_UServiceTable):
def __init__(self, **kwargs):
super().__init__(fid='6f56', sfid=0x05, name='EF.EST', desc='Enabled Services Table', size={1,None}, table=EF_EST_map, **kwargs)
# add those commands to the general commands of a TransparentEF
self.shell_commands += [self.AddlShellCommands()]
@with_default_category('File-Specific Commands')
class AddlShellCommands(CommandSet):
def __init__(self):
super().__init__()
def do_est_service_activate(self, arg):
"""Activate a service within EF.UST"""
self._cmd.card.update_est(int(arg), 1)
def do_est_service_deactivate(self, arg):
"""Deactivate a service within EF.UST"""
self._cmd.card.update_est(int(arg), 0)
# TS 31.102 Section 4.2.48
class EF_ACL(TransparentEF):
def __init__(self, fid='6f57', sfid=None, name='EF.ACL', size={32, None},
desc='Access Point Name Control List'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='Access Point Name Control List', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self._construct = Struct('num_of_apns'/Int8ub, 'tlvs'/GreedyBytes)
# TS 31.102 Section 4.2.51
class EF_START_HFN(TransparentEF):
def __init__(self, fid='6f5b', sfid=0x0f, name='EF.START-HFN', size={6, 6},
desc='Initialisation values for Hyperframe number'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='Initialisation values for Hyperframe number', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self._construct = Struct('start_cs'/Int24ub, 'start_ps'/Int24ub)
# TS 31.102 Section 4.2.52
class EF_THRESHOLD(TransparentEF):
def __init__(self, fid='6f5c', sfid=0x10, name='EF.THRESHOLD', size={3, 3},
desc='Maximum value of START'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='Maximum value of START', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self._construct = Struct('max_start'/Int24ub)
# TS 31.102 Section 4.2.77
class EF_VGCSCA(TransRecEF):
def __init__(self, fid='6fd4', sfid=None, name='EF.VGCSCA', size={2, 100}, rec_len=2,
desc='Voice Group Call Service Ciphering Algorithm'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
desc='Voice Group Call Service Ciphering Algorithm', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs)
self._construct = Struct('alg_v_ki_1'/Int8ub, 'alg_v_ki_2'/Int8ub)
# TS 31.102 Section 4.2.79
class EF_GBABP(TransparentEF):
def __init__(self, fid='6fd6', sfid=None, name='EF.GBABP', size={3, 50},
desc='GBA Bootstrapping parameters'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='GBA Bootstrapping parameters', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self._construct = Struct('rand'/LV, 'b_tid'/LV, 'key_lifetime'/LV)
# TS 31.102 Section 4.2.80
class EF_MSK(LinFixedEF):
def __init__(self, fid='6fd7', sfid=None, name='EF.MSK', desc='MBMS Service Key List'):
super().__init__(fid=fid, sfid=sfid,
name=name, desc=desc, rec_len={20, None})
def __init__(self, fid='6fd7', sfid=None, name='EF.MSK', desc='MBMS Service Key List', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len={20, None}, **kwargs)
msk_ts_constr = Struct('msk_id'/Int32ub, 'timestamp_counter'/Int32ub)
self._construct = Struct('key_domain_id'/Bytes(3),
'num_msk_id'/Int8ub,
'msk_ids'/msk_ts_constr[this.num_msk_id])
# TS 31.102 Section 4.2.81
class EF_MUK(LinFixedEF):
class MUK_Idr(BER_TLV_IE, tag=0x80):
_construct = HexAdapter(GreedyBytes)
@@ -815,14 +868,11 @@ class EF_MUK(LinFixedEF):
class EF_MUK_Collection(TLV_IE_Collection, nested=[MUK_ID, TimeStampCounter]):
pass
def __init__(self, fid='6fd8', sfid=None, name='EF.MUK', desc='MBMS User Key'):
super().__init__(fid=fid, sfid=sfid, name=name,
desc=desc, rec_len={None, None})
def __init__(self, fid='6fd8', sfid=None, name='EF.MUK', desc='MBMS User Key', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len={None, None}, **kwargs)
self._tlv = EF_MUK.EF_MUK_Collection
# TS 31.102 Section 4.2.83
class EF_GBANL(LinFixedEF):
class NAF_ID(BER_TLV_IE, tag=0x80):
_construct = HexAdapter(GreedyBytes)
@@ -833,35 +883,28 @@ class EF_GBANL(LinFixedEF):
class EF_GBANL_Collection(BER_TLV_IE, nested=[NAF_ID, B_TID]):
pass
def __init__(self, fid='6fda', sfid=None, name='EF.GBANL', desc='GBA NAF List'):
super().__init__(fid=fid, sfid=sfid, name=name,
desc=desc, rec_len={None, None})
def __init__(self, fid='6fda', sfid=None, name='EF.GBANL', desc='GBA NAF List', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len={None, None}, **kwargs)
self._tlv = EF_GBANL.EF_GBANL_Collection
# TS 31.102 Section 4.2.85
class EF_EHPLMNPI(TransparentEF):
def __init__(self, fid='6fdb', sfid=None, name='EF.EHPLMNPI', size={1, 1},
desc='Equivalent HPLMN Presentation Indication'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='Equivalent HPLMN Presentation Indication', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self._construct = Struct('presentation_ind' /
Enum(Byte, no_preference=0, display_highest_prio_only=1, display_all=2))
# TS 31.102 Section 4.2.87
class EF_NAFKCA(LinFixedEF):
class NAF_KeyCentreAddress(BER_TLV_IE, tag=0x80):
_construct = HexAdapter(GreedyBytes)
def __init__(self, fid='6fdd', sfid=None, name='EF.NAFKCA', rec_len={None, None},
desc='NAF Key Centre Address'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
desc='NAF Key Centre Address', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._tlv = EF_NAFKCA.NAF_KeyCentreAddress
# TS 31.102 Section 4.2.90
class EF_NCP_IP(LinFixedEF):
class DataDestAddrRange(TLV_IE, tag=0x83):
_construct = Struct('type_of_address'/Enum(Byte, IPv4=0x21, IPv6=0x56),
@@ -888,25 +931,21 @@ class EF_NCP_IP(LinFixedEF):
nested=[AccessPointName, Login, Password, BearerDescription]):
pass
def __init__(self, fid='6fe2', sfid=None, name='EF.NCP-IP', rec_len={None, None},
desc='Network Connectivity Parameters for USIM IP connections'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
desc='Network Connectivity Parameters for USIM IP connections', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._tlv = EF_NCP_IP.EF_NCP_IP_Collection
# TS 31.102 Section 4.2.91
class EF_EPSLOCI(TransparentEF):
def __init__(self, fid='6fe3', sfid=0x1e, name='EF.EPSLOCI', size={18, 18},
desc='EPS Location Information'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='EPS Location Information', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
upd_status_constr = Enum(
Byte, updated=0, not_updated=1, roaming_not_allowed=2)
self._construct = Struct('guti'/Bytes(12), 'last_visited_registered_tai'/Bytes(5),
'eps_update_status'/upd_status_constr)
# TS 31.102 Section 4.2.92
class EF_EPSNSC(LinFixedEF):
class KSI_ASME(BER_TLV_IE, tag=0x80):
_construct = Int8ub
@@ -928,33 +967,27 @@ class EF_EPSNSC(LinFixedEF):
IDofNASAlgorithms]):
pass
def __init__(self, fid='6fe4', sfid=0x18, name='EF.EPSNSC', rec_len={54, 128},
desc='EPS NAS Security Context'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
desc='EPS NAS Security Context', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._tlv = EF_EPSNSC.EPS_NAS_Security_Context
# TS 31.102 Section 4.2.96
class EF_PWS(TransparentEF):
def __init__(self, fid='6fec', sfid=None, name='EF.PWS', desc='Public Warning System', size={1, 1}):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
def __init__(self, fid='6fec', sfid=None, name='EF.PWS', desc='Public Warning System', size={1, 1}, **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
pws_config = FlagsEnum(
Byte, ignore_pws_in_hplmn_and_equivalent=1, ignore_pws_in_vplmn=2)
self._construct = Struct('pws_configuration'/pws_config)
# TS 31.102 Section 4.2.101
class EF_IPS(CyclicEF):
def __init__(self, fid='6ff1', sfid=None, name='EF.IPS', rec_len={4, 4},
desc='IMEI(SV) Pairing Status'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
desc='IMEI(SV) Pairing Status', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._construct = Struct('status'/PaddedString(2, 'ascii'),
'link_to_ef_ipd'/Int8ub, 'rfu'/Byte)
# TS 31.102 Section 4.2.103
class EF_ePDGId(TransparentEF):
class ePDGId(BER_TLV_IE, tag=0x80, nested=[]):
_construct = Struct('type_of_ePDG_address'/Enum(Byte, FQDN=0, IPv4=1, IPv6=2),
@@ -963,17 +996,15 @@ class EF_ePDGId(TransparentEF):
'IPv4': HexAdapter(GreedyBytes),
'IPv6': HexAdapter(GreedyBytes)}))
def __init__(self, fid='6ff3', sfid=None, name='EF.eDPDGId', desc='Home ePDG Identifier'):
super().__init__(fid, sfid=sfid, name=name, desc=desc)
def __init__(self, fid='6ff3', sfid=None, name='EF.eDPDGId', desc='Home ePDG Identifier', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, **kwargs)
self._tlv = EF_ePDGId.ePDGId
# TS 31.102 Section 4.2.106
class EF_FromPreferred(TransparentEF):
def __init__(self, fid='6ff7', sfid=None, name='EF.FromPreferred', size={1, 1},
desc='From Preferred'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='From Preferred', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self._construct = BitStruct('rfu'/BitsRFU(7), 'from_preferred'/Bit)
######################################################################
@@ -981,40 +1012,32 @@ class EF_FromPreferred(TransparentEF):
######################################################################
# TS 31.102 Section 4.4.11.2
class EF_5GS3GPPLOCI(TransparentEF):
def __init__(self, fid='4f01', sfid=0x01, name='EF.5GS3GPPLOCI', size={20, 20},
desc='5S 3GP location information'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='5S 3GP location information', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
upd_status_constr = Enum(
Byte, updated=0, not_updated=1, roaming_not_allowed=2)
self._construct = Struct('5g_guti'/Bytes(13), 'last_visited_registered_tai_in_5gs'/Bytes(6),
'5gs_update_status'/upd_status_constr)
# TS 31.102 Section 4.4.11.7
class EF_UAC_AIC(TransparentEF):
def __init__(self, fid='4f06', sfid=0x06, name='EF.UAC_AIC', size={4, 4},
desc='UAC Access Identities Configuration'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='UAC Access Identities Configuration', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
cfg_constr = FlagsEnum(Byte, multimedia_priority_service=1,
mission_critical_service=2)
self._construct = Struct('uac_access_id_config'/cfg_constr)
# TS 31.102 Section 4.4.11.9
class EF_OPL5G(LinFixedEF):
def __init__(self, fid='6f08', sfid=0x08, name='EF.OPL5G', desc='5GS Operator PLMN List'):
super().__init__(fid=fid, sfid=sfid,
name=name, desc=desc, rec_len={10, None})
self._construct = Struct('tai'/Bytes(9), 'pnn_record_id'/Int8ub)
def __init__(self, fid='6f08', sfid=0x08, name='EF.OPL5G', desc='5GS Operator PLMN List', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len={10, None}, **kwargs)
Tai = Struct('mcc_mnc'/BcdAdapter(Bytes(3)), 'tac_min'/Bytes(3), 'tac_max'/Bytes(3))
self._construct = Struct('tai'/Tai, 'pnn_record_id'/Int8ub)
# TS 31.102 Section 4.4.11.10
class EF_SUPI_NAI(TransparentEF):
class NetworkSpecificIdentifier(TLV_IE, tag=0x80):
# RFC 7542 encoded as UTF-8 string
@@ -1032,8 +1055,8 @@ class EF_SUPI_NAI(TransparentEF):
nested=[NetworkSpecificIdentifier, GlobalLineIdentifier, GlobalCableIdentifier]):
pass
def __init__(self, fid='4f09', sfid=0x09, name='EF.SUPI_NAI',
desc='SUPI as Network Access Identifier'):
super().__init__(fid, sfid=sfid, name=name, desc=desc)
desc='SUPI as Network Access Identifier', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, **kwargs)
self._tlv = EF_SUPI_NAI.NAI_TLV_Collection
@@ -1041,63 +1064,57 @@ class EF_TN3GPPSNN(TransparentEF):
class ServingNetworkName(BER_TLV_IE, tag=0x80):
_construct = GreedyString("utf8")
def __init__(self, fid='4f0c', sfid=0x0c, name='EF.TN3GPPSNN',
desc='Trusted non-3GPP Serving network names list'):
super().__init__(fid, sfid=sfid, name=name, desc=desc)
desc='Trusted non-3GPP Serving network names list', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, **kwargs)
self._tlv = EF_TN3GPPSNN.ServingNetworkName
# TS 31.102 Section 4.4.5
class DF_WLAN(CardDF):
def __init__(self, fid='5f40', name='DF.WLAN', desc='Files for WLAN purpose'):
super().__init__(fid=fid, name=name, desc=desc)
def __init__(self, fid='5f40', name='DF.WLAN', desc='Files for WLAN purpose', **kwargs):
super().__init__(fid=fid, name=name, desc=desc, **kwargs)
files = [
TransparentEF('4f41', 0x01, 'EF.Pseudo', 'Pseudonym'),
TransparentEF('4f41', 0x01, 'EF.Pseudo', 'Pseudonym', service=59),
TransparentEF('4f42', 0x02, 'EF.UPLMNWLAN',
'User controlled PLMN selector for I-WLAN Access'),
'User controlled PLMN selector for I-WLAN Access', service=60),
TransparentEF('4f43', 0x03, 'EF.OPLMNWLAN',
'Operator controlled PLMN selector for I-WLAN Access'),
'Operator controlled PLMN selector for I-WLAN Access', service=61),
LinFixedEF('4f44', 0x04, 'EF.UWSIDL',
'User controlled WLAN Specific Identifier List'),
'User controlled WLAN Specific Identifier List', service=62),
LinFixedEF('4f45', 0x05, 'EF.OWSIDL',
'Operator controlled WLAN Specific Identifier List'),
'Operator controlled WLAN Specific Identifier List', service=63),
TransparentEF('4f46', 0x06, 'EF.WRI',
'WLAN Reauthentication Identity'),
'WLAN Reauthentication Identity', service=66),
LinFixedEF('4f47', 0x07, 'EF.HWSIDL',
'Home I-WLAN Specific Identifier List'),
'Home I-WLAN Specific Identifier List', service=81),
TransparentEF('4f48', 0x08, 'EF.WEHPLMNPI',
'I-WLAN Equivalent HPLMN Presentation Indication'),
'I-WLAN Equivalent HPLMN Presentation Indication', service=82),
TransparentEF('4f49', 0x09, 'EF.WHPI',
'I-WLAN HPLMN Priority Indication'),
'I-WLAN HPLMN Priority Indication', service=83),
TransparentEF('4f4a', 0x0a, 'EF.WLRPLMN',
'I-WLAN Last Registered PLMN'),
'I-WLAN Last Registered PLMN', service=84),
TransparentEF('4f4b', 0x0b, 'EF.HPLMNDAI',
'HPLMN Direct Access Indicator'),
'HPLMN Direct Access Indicator', service=88),
]
self.add_files(files)
# TS 31.102 Section 4.4.6
class DF_HNB(CardDF):
def __init__(self, fid='5f50', name='DF.HNB', desc='Files for HomeNodeB purpose'):
super().__init__(fid=fid, name=name, desc=desc)
def __init__(self, fid='5f50', name='DF.HNB', desc='Files for HomeNodeB purpose', **kwargs):
super().__init__(fid=fid, name=name, desc=desc, **kwargs)
files = [
LinFixedEF('4f01', 0x01, 'EF.ACSGL', 'Allowed CSG Lists'),
LinFixedEF('4f02', 0x02, 'EF.CSGTL', 'CSG Types'),
LinFixedEF('4f03', 0x03, 'EF.HNBN', 'Home NodeB Name'),
LinFixedEF('4f04', 0x04, 'EF.OCSGL', 'Operator CSG Lists'),
LinFixedEF('4f05', 0x05, 'EF.OCSGT', 'Operator CSG Type'),
LinFixedEF('4f06', 0x06, 'EF.OHNBN', 'Operator Home NodeB Name'),
LinFixedEF('4f01', 0x01, 'EF.ACSGL', 'Allowed CSG Lists', service=86),
LinFixedEF('4f02', 0x02, 'EF.CSGTL', 'CSG Types', service=86),
LinFixedEF('4f03', 0x03, 'EF.HNBN', 'Home NodeB Name', service=86),
LinFixedEF('4f04', 0x04, 'EF.OCSGL', 'Operator CSG Lists', service=90),
LinFixedEF('4f05', 0x05, 'EF.OCSGT', 'Operator CSG Type', service=90),
LinFixedEF('4f06', 0x06, 'EF.OHNBN', 'Operator Home NodeB Name', service=90),
]
self.add_files(files)
# TS 31.102 Section 4.4.8
class DF_ProSe(CardDF):
def __init__(self, fid='5f90', name='DF.ProSe', desc='Files for ProSe purpose'):
super().__init__(fid=fid, name=name, desc=desc)
def __init__(self, fid='5f90', name='DF.ProSe', desc='Files for ProSe purpose', **kwargs):
super().__init__(fid=fid, name=name, desc=desc, **kwargs)
files = [
LinFixedEF('4f01', 0x01, 'EF.PROSE_MON',
'ProSe Monitoring Parameters'),
@@ -1128,26 +1145,26 @@ class DF_ProSe(CardDF):
class DF_USIM_5GS(CardDF):
def __init__(self, fid='5FC0', name='DF.5GS', desc='5GS related files'):
super().__init__(fid=fid, name=name, desc=desc)
def __init__(self, fid='5FC0', name='DF.5GS', desc='5GS related files', **kwargs):
super().__init__(fid=fid, name=name, desc=desc, **kwargs)
files = [
# I'm looking at 31.102 R16.6
EF_5GS3GPPLOCI(),
EF_5GS3GPPLOCI(service=122),
EF_5GS3GPPLOCI('4f02', 0x02, 'EF.5GSN3GPPLOCI',
'5GS non-3GPP location information'),
EF_5GS3GPPNSC(),
'5GS non-3GPP location information', service=122),
EF_5GS3GPPNSC(service=122),
EF_5GS3GPPNSC('4f04', 0x04, 'EF.5GSN3GPPNSC',
'5GS non-3GPP Access NAS Security Context'),
EF_5GAUTHKEYS(),
EF_UAC_AIC(),
EF_SUCI_Calc_Info(),
EF_OPL5G(),
EF_SUPI_NAI(),
'5GS non-3GPP Access NAS Security Context', service=122),
EF_5GAUTHKEYS(service=123),
EF_UAC_AIC(service=126),
EF_SUCI_Calc_Info(service=124),
EF_OPL5G(service=129),
EF_SUPI_NAI(service=130),
TransparentEF('4F0A', 0x0a, 'EF.Routing_Indicator',
'Routing Indicator', size={4, 4}),
'Routing Indicator', size={4, 4}, service=124),
TransparentEF('4F0B', 0x0b, 'EF.URSP',
'UE Route Selector Policies per PLMN'),
EF_TN3GPPSNN(),
'UE Route Selector Policies per PLMN', service=132),
EF_TN3GPPSNN(service=133),
]
self.add_files(files)
@@ -1166,123 +1183,113 @@ class ADF_USIM(CardADF):
EF_Keys('6f09', 0x09, 'EF.KeysPS',
desc='Ciphering and Integrity Keys for PS domain'),
EF_xPLMNwAcT('6f60', 0x0a, 'EF.PLMNwAcT',
'User controlled PLMN Selector with Access Technology'),
'User controlled PLMN Selector with Access Technology', service=20),
EF_HPPLMN(),
EF_ACMmax(),
EF_UServiceTable('6f38', 0x04, 'EF.UST', 'USIM Service Table', size={
1, 17}, table=EF_UST_map),
EF_ACMmax(service=13),
EF_UST(),
CyclicEF('6f39', None, 'EF.ACM',
'Accumulated call meter', rec_len={3, 3}),
TransparentEF('6f3e', None, 'EF.GID1', 'Group Identifier Level 1'),
TransparentEF('6f3f', None, 'EF.GID2', 'Group Identifier Level 2'),
EF_SPN(),
'Accumulated call meter', rec_len={3, 3}, service=13),
TransparentEF('6f3e', None, 'EF.GID1', 'Group Identifier Level 1', service=17),
TransparentEF('6f3f', None, 'EF.GID2', 'Group Identifier Level 2', service=18),
EF_SPN(service=19),
TransparentEF('6f41', None, 'EF.PUCT',
'Price per unit and currency table', size={5, 5}),
EF_CBMI(),
'Price per unit and currency table', size={5, 5}, service=13),
EF_CBMI(service=15),
EF_ACC(sfid=0x06),
EF_PLMNsel('6f7b', 0x0d, 'EF.FPLMN',
'Forbidden PLMNs', size={12, None}),
EF_LOCI(),
EF_AD(),
EF_CBMID(sfid=0x0e),
EF_CBMID(sfid=0x0e, service=29),
EF_ECC(),
EF_CBMIR(),
EF_CBMIR(service=16),
EF_PSLOCI(),
EF_ADN('6f3b', None, 'EF.FDN', 'Fixed Dialling Numbers'),
EF_SMS('6f3c', None),
EF_MSISDN(),
EF_SMSP(),
EF_SMSS(),
EF_ADN('6f49', None, 'EF.SDN', 'Service Dialling Numbers'),
EF_EXT('6f4b', None, 'EF.EXT2', 'Extension2 (FDN)'),
EF_EXT('6f4c', None, 'EF.EXT3', 'Extension2 (SDN)'),
EF_SMSR(),
EF_ICI(),
EF_OCI(),
EF_ICT(),
EF_ICT('6f83', None, 'EF.OCT', 'Outgoing Call Timer'),
EF_EXT('6f4e', None, 'EF.EXT5', 'Extension5 (ICI/OCI/MSISDN)'),
EF_CCP2(),
EF_eMLPP(),
EF_AAeM(),
EF_ADN('6f3b', None, 'EF.FDN', 'Fixed Dialling Numbers', service=[2, 89]),
EF_SMS('6f3c', None, service=10),
EF_MSISDN(service=21),
EF_SMSP(service=12),
EF_SMSS(service=10),
EF_ADN('6f49', None, 'EF.SDN', 'Service Dialling Numbers', service=[4, 89]),
EF_EXT('6f4b', None, 'EF.EXT2', 'Extension2 (FDN)', service=3),
EF_EXT('6f4c', None, 'EF.EXT3', 'Extension2 (SDN)', service=5),
EF_SMSR(service=11),
EF_ICI(service=9),
EF_OCI(service=8),
EF_ICT(service=9),
EF_ICT('6f83', None, 'EF.OCT', 'Outgoing Call Timer', service=8),
EF_EXT('6f4e', None, 'EF.EXT5', 'Extension5 (ICI/OCI/MSISDN)', service=44),
EF_CCP2(service=14),
EF_eMLPP(service=24),
EF_AAeM(service=25),
# EF_Hiddenkey
EF_ADN('6f4d', None, 'EF.BDN', 'Barred Dialling Numbers'),
EF_EXT('6f55', None, 'EF.EXT4', 'Extension4 (BDN/SSC)'),
EF_CMI(),
EF_UServiceTable('6f56', 0x05, 'EF.EST', 'Enabled Services Table', size={
1, None}, table=EF_EST_map),
EF_ACL(),
EF_DCK(),
EF_CNL(),
EF_ADN('6f4d', None, 'EF.BDN', 'Barred Dialling Numbers', service=6),
EF_EXT('6f55', None, 'EF.EXT4', 'Extension4 (BDN/SSC)', service=7),
EF_CMI(service=6),
EF_EST(service=[2, 6, 34, 35]),
EF_ACL(service=35),
EF_DCK(service=36),
EF_CNL(service=37),
EF_START_HFN(),
EF_THRESHOLD(),
EF_xPLMNwAcT('6f61', 0x11, 'EF.OPLMNwAcT',
'User controlled PLMN Selector with Access Technology'),
EF_xPLMNwAcT('6f61', 0x11, 'EF.OPLMNwAcT', 'User controlled PLMN Selector with Access Technology', service=42),
EF_xPLMNwAcT('6f62', 0x13, 'EF.HPLMNwAcT', 'HPLMN Selector with Access Technology', service=43),
EF_ARR('6f06', 0x17),
TransparentEF('6fc4', None, 'EF.NETPAR', 'Network Parameters'),
EF_PNN('6fc5', 0x19),
EF_OPL(),
EF_ADN('6fc7', None, 'EF.MBDN', 'Mailbox Dialling Numbers'),
EF_MBI(),
EF_MWIS(),
EF_ADN('6fcb', None, 'EF.CFIS',
'Call Forwarding Indication Status'),
EF_PNN('6fc5', 0x19, service=45),
EF_OPL(service=46),
EF_ADN('6fc7', None, 'EF.MBDN', 'Mailbox Dialling Numbers', service=47),
EF_EXT('6fc8', None, 'EF.EXT6', 'Extension6 (MBDN)'),
EF_MBI(service=47),
EF_MWIS(service=48),
EF_ADN('6fcb', None, 'EF.CFIS', 'Call Forwarding Indication Status', service=49),
EF_EXT('6fcc', None, 'EF.EXT7', 'Extension7 (CFIS)'),
TransparentEF('6fcd', None, 'EF.SPDI',
'Service Provider Display Information'),
EF_MMSN(),
EF_EXT('6fcf', None, 'EF.EXT8', 'Extension8 (MMSN)'),
EF_MMSICP(),
EF_MMSUP(),
EF_MMSUCP(),
EF_NIA(),
EF_VGCS(),
EF_VGCSS(),
EF_VGCS('6fb3', None, 'EF.VBS', 'Voice Broadcast Service'),
EF_VGCSS('6fb4', None, 'EF.VBSS',
'Voice Broadcast Service Status'),
EF_VGCSCA(),
EF_VGCSCA('6fd5', None, 'EF.VBCSCA',
'Voice Broadcast Service Ciphering Algorithm'),
EF_GBABP(),
EF_MSK(),
EF_MUK(),
EF_GBANL(),
EF_PLMNsel('6fd9', 0x1d, 'EF.EHPLMN',
'Equivalent HPLMN', size={12, None}),
EF_EHPLMNPI(),
EF_NAFKCA(),
TransparentEF('6fde', None, 'EF.SPNI',
'Service Provider Name Icon'),
LinFixedEF('6fdf', None, 'EF.PNNI', 'PLMN Network Name Icon'),
EF_NCP_IP(),
EF_EPSLOCI('6fe3', 0x1e, 'EF.EPSLOCI', 'EPS location information'),
EF_EPSNSC(),
TransparentEF('6fe6', None, 'EF.UFC',
'USAT Facility Control', size={1, 16}),
TransparentEF('6fe8', None, 'EF.NASCONFIG',
'Non Access Stratum Configuration'),
# UICC IARI (only in cards that have no ISIM)
EF_PWS(),
LinFixedEF('6fed', None, 'EF.FDNURI',
'Fixed Dialling Numbers URI'),
LinFixedEF('6fee', None, 'EF.BDNURI',
'Barred Dialling Numbers URI'),
LinFixedEF('6fef', None, 'EF.SDNURI',
'Service Dialling Numbers URI'),
TransparentEF('6fcd', None, 'EF.SPDI', 'Service Provider Display Information', service=51),
EF_MMSN(service=52),
EF_EXT('6fcf', None, 'EF.EXT8', 'Extension8 (MMSN)', service=53),
EF_MMSICP(service=52),
EF_MMSUP(service=52),
EF_MMSUCP(service=(52, 55)),
EF_NIA(service=56),
EF_VGCS(service=57),
EF_VGCSS(service=57),
EF_VGCS('6fb3', None, 'EF.VBS', 'Voice Broadcast Service', service=58),
EF_VGCSS('6fb4', None, 'EF.VBSS', 'Voice Broadcast Service Status', service=58),
EF_VGCSCA(service=64),
EF_VGCSCA('6fd5', None, 'EF.VBCSCA', 'Voice Broadcast Service Ciphering Algorithm', service=65),
EF_GBABP(service=68),
EF_MSK(service=69),
EF_MUK(service=69),
EF_GBANL(service=68),
EF_PLMNsel('6fd9', 0x1d, 'EF.EHPLMN', 'Equivalent HPLMN', size={12, None}, service=71),
EF_EHPLMNPI(service=(71, 73)),
# EF_LRPLMNSI ('6fdc', service=74)
EF_NAFKCA(service=(68, 76)),
TransparentEF('6fde', None, 'EF.SPNI', 'Service Provider Name Icon', service=78),
LinFixedEF('6fdf', None, 'EF.PNNI', 'PLMN Network Name Icon', service=79),
EF_NCP_IP(service=80),
EF_EPSLOCI('6fe3', 0x1e, 'EF.EPSLOCI', 'EPS location information', service=85),
EF_EPSNSC(service=85),
TransparentEF('6fe6', None, 'EF.UFC', 'USAT Facility Control', size={1, 16}),
TransparentEF('6fe8', None, 'EF.NASCONFIG', 'Non Access Stratum Configuration', service=96),
# UICC IARI (only in cards that have no ISIM) service=95
EF_PWS(service=97),
LinFixedEF('6fed', None, 'EF.FDNURI', 'Fixed Dialling Numbers URI', service=(2, 99)),
LinFixedEF('6fee', None, 'EF.BDNURI', 'Barred Dialling Numbers URI', service=(6, 99)),
LinFixedEF('6fef', None, 'EF.SDNURI', 'Service Dialling Numbers URI', service=(4, 99)),
# EF_IWL (IMEI(SV) White List)
EF_IPS(),
EF_ePDGId(),
EF_ePDGId(service=(106, 107)),
# FIXME: from EF_ePDGSelection onwards
EF_FromPreferred(),
# FIXME: DF_SoLSA
EF_FromPreferred(service=114),
# FIXME: DF_SoLSA service=23
# FIXME: DF_PHONEBOOK
# FIXME: DF_GSM_ACCESS
DF_WLAN(),
DF_HNB(),
DF_ProSe(),
# FIXME: DF_ACDC
# FIXME: DF_TV
DF_USIM_5GS(),
# FIXME: DF_GSM_ACCESS service=27
DF_WLAN(service=[59, 60, 61, 62, 63, 66, 81, 82, 83, 84, 88]),
DF_HNB(service=[86, 90]),
DF_ProSe(service=101),
# FIXME: DF_ACDC service=108
# FIXME: DF_TV service=116
DF_USIM_5GS(service=[122, 123, 124, 125, 126, 127, 129, 130]),
]
self.add_files(files)
@@ -1305,18 +1312,42 @@ class ADF_USIM(CardADF):
(data, sw) = self._cmd.card._scc.authenticate(opts.rand, opts.autn)
self._cmd.poutput_json(data)
term_prof_parser = argparse.ArgumentParser()
term_prof_parser.add_argument('PROFILE', help='Hexstring of encoded terminal profile')
@cmd2.with_argparser(term_prof_parser)
def do_terminal_profile(self, arg):
"""Send a TERMINAL PROFILE command to the card."""
"""Send a TERMINAL PROFILE command to the card.
This is used to inform the card about which optional
features the terminal (modem/phone) supports, particularly
in the context of SIM Toolkit, Proactive SIM and OTA. You
must specify a hex-string with the encoded terminal profile
you want to send to the card."""
(data, sw) = self._cmd.card._scc.terminal_profile(arg)
self._cmd.poutput('SW: %s, data: %s' % (sw, data))
envelope_parser = argparse.ArgumentParser()
envelope_parser.add_argument('PAYLOAD', help='Hexstring of encoded payload to ENVELOPE')
@cmd2.with_argparser(envelope_parser)
def do_envelope(self, arg):
"""Send an ENVELOPE command to the card."""
"""Send an ENVELOPE command to the card. This is how a
variety of information is communicated from the terminal
(modem/phone) to the card, particularly in the context of
SIM Toolkit, Proactive SIM and OTA."""
(data, sw) = self._cmd.card._scc.envelope(arg)
self._cmd.poutput('SW: %s, data: %s' % (sw, data))
envelope_sms_parser = argparse.ArgumentParser()
envelope_sms_parser.add_argument('TPDU', help='Hexstring of encoded SMS TPDU')
@cmd2.with_argparser(envelope_sms_parser)
def do_envelope_sms(self, arg):
"""Send an ENVELOPE command to the card."""
"""Send an ENVELOPE(SMS-PP-Download) command to the card.
This emulates a terminal (modem/phone) having received a SMS
with a PID of 'SMS for the SIM card'. You can use this
command in the context of testing OTA related features
without a modem/phone or a cellular netwokr."""
tpdu_ie = SMS_TPDU()
tpdu_ie.from_bytes(h2b(arg))
dev_ids = DeviceIdentities(

View File

@@ -78,44 +78,68 @@ EF_ISIM_ADF_map = {
}
# TS 31.103 Section 4.2.2
class EF_IMPI(TransparentEF):
class nai(BER_TLV_IE, tag=0x80):
_construct = GreedyString("utf8")
def __init__(self, fid='6f02', sfid=0x02, name='EF.IMPI', desc='IMS private user identity'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
def __init__(self, fid='6f02', sfid=0x02, name='EF.IMPI', desc='IMS private user identity', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
self._tlv = EF_IMPI.nai
# TS 31.103 Section 4.2.3
class EF_DOMAIN(TransparentEF):
class domain(BER_TLV_IE, tag=0x80):
_construct = GreedyString("utf8")
def __init__(self, fid='6f05', sfid=0x05, name='EF.DOMAIN', desc='Home Network Domain Name'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
def __init__(self, fid='6f03', sfid=0x05, name='EF.DOMAIN', desc='Home Network Domain Name', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
self._tlv = EF_DOMAIN.domain
# TS 31.103 Section 4.2.4
class EF_IMPU(LinFixedEF):
class impu(BER_TLV_IE, tag=0x80):
_construct = GreedyString("utf8")
def __init__(self, fid='6f04', sfid=0x04, name='EF.IMPU', desc='IMS public user identity'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
def __init__(self, fid='6f04', sfid=0x04, name='EF.IMPU', desc='IMS public user identity', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
self._tlv = EF_IMPU.impu
# TS 31.103 Section 4.2.7
class EF_IST(EF_UServiceTable):
def __init__(self, **kwargs):
super().__init__('6f07', 0x07, 'EF.IST', 'ISIM Service Table', {1, None}, EF_IST_map)
# add those commands to the general commands of a TransparentEF
self.shell_commands += [self.AddlShellCommands()]
@with_default_category('File-Specific Commands')
class AddlShellCommands(CommandSet):
def __init__(self):
super().__init__()
def do_ist_service_activate(self, arg):
"""Activate a service within EF.IST"""
self._cmd.card.update_ist(int(arg), 1)
def do_ist_service_deactivate(self, arg):
"""Deactivate a service within EF.IST"""
self._cmd.card.update_ist(int(arg), 0)
def do_ist_service_check(self, arg):
"""Check consistency between services of this file and files present/activated.
Many services determine if one or multiple files shall be present/activated or if they shall be
absent/deactivated. This performs a consistency check to ensure that no services are activated
for files that are not - and vice-versa, no files are activated for services that are not. Error
messages are printed for every inconsistency found."""
selected_file = self._cmd.rs.selected_file
num_problems = selected_file.ust_service_check(self._cmd)
self._cmd.poutput("===> %u service / file inconsistencies detected" % num_problems)
# TS 31.103 Section 4.2.8
class EF_PCSCF(LinFixedEF):
def __init__(self, fid='6f09', sfid=None, name='EF.P-CSCF', desc='P-CSCF Address'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
def __init__(self, fid='6f09', sfid=None, name='EF.P-CSCF', desc='P-CSCF Address', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
def _decode_record_hex(self, raw_hex):
addr, addr_type = dec_addr_tlv(raw_hex)
@@ -127,69 +151,109 @@ class EF_PCSCF(LinFixedEF):
return enc_addr_tlv(addr, addr_type)
# TS 31.103 Section 4.2.9
class EF_GBABP(TransparentEF):
def __init__(self, fid='6fd5', sfid=None, name='EF.GBABP', desc='GBA Bootstrapping'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
def __init__(self, fid='6fd5', sfid=None, name='EF.GBABP', desc='GBA Bootstrapping', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
# TS 31.103 Section 4.2.10
class EF_GBANL(LinFixedEF):
def __init__(self, fid='6fd7', sfid=None, name='EF.GBANL', desc='GBA NAF List'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
def __init__(self, fid='6fd7', sfid=None, name='EF.GBANL', desc='GBA NAF List', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
# TS 31.103 Section 4.2.11
class EF_NAFKCA(LinFixedEF):
def __init__(self, fid='6fdd', sfid=None, name='EF.NAFKCA', desc='NAF Key Centre Address'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
def __init__(self, fid='6fdd', sfid=None, name='EF.NAFKCA', desc='NAF Key Centre Address', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
# TS 31.103 Section 4.2.16
class EF_UICCIARI(LinFixedEF):
class iari(BER_TLV_IE, tag=0x80):
_construct = GreedyString("utf8")
def __init__(self, fid='6fe7', sfid=None, name='EF.UICCIARI', desc='UICC IARI'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
def __init__(self, fid='6fe7', sfid=None, name='EF.UICCIARI', desc='UICC IARI', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
self._tlv = EF_UICCIARI.iari
# TS 31.103 Section 4.2.18
class EF_IMSConfigData(BerTlvEF):
def __init__(self, fid='6ff8', sfid=None, name='EF.IMSConfigData', desc='IMS Configuration Data'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
class ImsConfigDataEncoding(BER_TLV_IE, tag=0x80):
_construct = HexAdapter(Bytes(1))
class ImsConfigData(BER_TLV_IE, tag=0x81):
_construct = GreedyString
# pylint: disable=undefined-variable
class ImsConfigDataCollection(TLV_IE_Collection, neted=[ImsConfigDataEncoding, ImsConfigData]):
pass
def __init__(self, fid='6ff8', sfid=None, name='EF.IMSConfigData', desc='IMS Configuration Data', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
self._tlv = EF_IMSConfigData.ImsConfigDataCollection
# TS 31.103 Section 4.2.19
class EF_XCAPConfigData(BerTlvEF):
def __init__(self, fid='6ffc', sfid=None, name='EF.XCAPConfigData', desc='XCAP Configuration Data'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
class Access(BER_TLV_IE, tag=0x81):
pass
class ApplicationName(BER_TLV_IE, tag=0x82):
pass
class ProviderID(BER_TLV_IE, tag=0x83):
pass
class URI(BER_TLV_IE, tag=0x84):
pass
class XcapAuthenticationUserName(BER_TLV_IE, tag=0x85):
pass
class XcapAuthenticationPassword(BER_TLV_IE, tag=0x86):
pass
class XcapAuthenticationType(BER_TLV_IE, tag=0x87):
pass
class AddressType(BER_TLV_IE, tag=0x88):
pass
class Address(BER_TLV_IE, tag=0x89):
pass
class PDPAuthenticationType(BER_TLV_IE, tag=0x8a):
pass
class PDPAuthenticationName(BER_TLV_IE, tag=0x8b):
pass
class PDPAuthenticationSecret(BER_TLV_IE, tag=0x8c):
pass
class AccessForXCAP(BER_TLV_IE, tag=0x81):
pass
class NumberOfXcapConnParPolicy(BER_TLV_IE, tag=0x82):
_construct = Int8ub
# pylint: disable=undefined-variable
class XcapConnParamsPolicyPart(BER_TLV_IE, tag=0xa1, nested=[Access, ApplicationName, ProviderID, URI,
XcapAuthenticationUserName, XcapAuthenticationPassword,
XcapAuthenticationType, AddressType, Address, PDPAuthenticationType,
PDPAuthenticationName, PDPAuthenticationSecret]):
pass
class XcapConnParamsPolicy(BER_TLV_IE, tag=0xa0, nested=[AccessForXCAP, NumberOfXcapConnParPolicy, XcapConnParamsPolicyPart]):
pass
class XcapConnParamsPolicyDO(BER_TLV_IE, tag=0x80, nested=[XcapConnParamsPolicy]):
pass
def __init__(self, fid='6ffc', sfid=None, name='EF.XCAPConfigData', desc='XCAP Configuration Data', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
self._tlv = EF_XCAPConfigData.XcapConnParamsPolicy
# TS 31.103 Section 4.2.20
class EF_WebRTCURI(TransparentEF):
class uri(BER_TLV_IE, tag=0x80):
_construct = GreedyString("utf8")
def __init__(self, fid='6ffa', sfid=None, name='EF.WebRTCURI', desc='WebRTC URI'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
def __init__(self, fid='6ffa', sfid=None, name='EF.WebRTCURI', desc='WebRTC URI', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
self._tlv = EF_WebRTCURI.uri
# TS 31.103 Section 4.2.21
class EF_MuDMiDConfigData(BerTlvEF):
class MudMidConfigDataEncoding(BER_TLV_IE, tag=0x80):
_construct = HexAdapter(Bytes(1))
class MudMidConfigData(BER_TLV_IE, tag=0x81):
_construct = GreedyString
# pylint: disable=undefined-variable
class MudMidConfigDataCollection(TLV_IE_Collection, neted=[MudMidConfigDataEncoding, MudMidConfigData]):
pass
def __init__(self, fid='6ffe', sfid=None, name='EF.MuDMiDConfigData',
desc='MuD and MiD Configuration Data'):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc)
desc='MuD and MiD Configuration Data', **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
self._tlv = EF_MuDMiDConfigData.MudMidConfigDataCollection
class ADF_ISIM(CardADF):
@@ -203,22 +267,21 @@ class ADF_ISIM(CardADF):
EF_IMPU(),
EF_AD(),
EF_ARR('6f06', 0x06),
EF_UServiceTable('6f07', 0x07, 'EF.IST',
'ISIM Service Table', {1, None}, EF_IST_map),
EF_PCSCF(),
EF_GBABP(),
EF_GBANL(),
EF_NAFKCA(),
EF_SMS(),
EF_SMSS(),
EF_SMSR(),
EF_SMSP(),
EF_UICCIARI(),
EF_FromPreferred(),
EF_IMSConfigData(),
EF_XCAPConfigData(),
EF_WebRTCURI(),
EF_MuDMiDConfigData(),
EF_IST(),
EF_PCSCF(service=5),
EF_GBABP(service=2),
EF_GBANL(service=2),
EF_NAFKCA(service=2),
EF_SMS(service=(6,8)),
EF_SMSS(service=(6,8)),
EF_SMSR(service=(7,8)),
EF_SMSP(service=8),
EF_UICCIARI(service=10),
EF_FromPreferred(service=17),
EF_IMSConfigData(service=18),
EF_XCAPConfigData(service=19),
EF_WebRTCURI(service=20),
EF_MuDMiDConfigData(service=21),
]
self.add_files(files)
# add those commands to the general commands of a TransparentEF

View File

@@ -340,26 +340,20 @@ EF_SST_map = {
######################################################################
# TS 51.011 Section 10.5.1
class EF_ADN(LinFixedEF):
def __init__(self, fid='6f3a', sfid=None, name='EF.ADN', desc='Abbreviated Dialing Numbers'):
super().__init__(fid, sfid=sfid, name=name,
desc=desc, rec_len={14, 30})
def _decode_record_bin(self, raw_bin_data):
alpha_id_len = len(raw_bin_data) - 14
alpha_id = raw_bin_data[:alpha_id_len]
u = unpack('!BB10sBB', raw_bin_data[-14:])
return {'alpha_id': alpha_id, 'len_of_bcd': u[0], 'ton_npi': u[1],
'dialing_nr': u[2], 'cap_conf_id': u[3], 'ext1_record_id': u[4]}
def __init__(self, fid='6f3a', sfid=None, name='EF.ADN', desc='Abbreviated Dialing Numbers', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={14, 30}, **kwargs)
self._construct = Struct('alpha_id'/COptional(GsmStringAdapter(Rpad(Bytes(this._.total_len-14)), codec='ascii')),
'len_of_bcd'/Int8ub,
'ton_npi'/TonNpi,
'dialing_nr'/BcdAdapter(Rpad(Bytes(10))),
'cap_conf_id'/Int8ub,
'ext1_record_id'/Int8ub)
# TS 51.011 Section 10.5.5
class EF_SMS(LinFixedEF):
def __init__(self, fid='6f3c', sfid=None, name='EF.SMS', desc='Short messages'):
super().__init__(fid, sfid=sfid, name=name,
desc=desc, rec_len={176, 176})
def __init__(self, fid='6f3c', sfid=None, name='EF.SMS', desc='Short messages', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={176, 176}, **kwargs)
def _decode_record_bin(self, raw_bin_data):
def decode_status(status):
@@ -389,9 +383,8 @@ class EF_SMS(LinFixedEF):
# TS 51.011 Section 10.5.5
class EF_MSISDN(LinFixedEF):
def __init__(self, fid='6f40', sfid=None, name='EF.MSISDN', desc='MSISDN'):
super().__init__(fid, sfid=sfid, name=name,
desc=desc, rec_len={15, 34})
def __init__(self, fid='6f40', sfid=None, name='EF.MSISDN', desc='MSISDN', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={15, 34}, **kwargs)
def _decode_record_hex(self, raw_hex_data):
return {'msisdn': dec_msisdn(raw_hex_data)}
@@ -407,16 +400,45 @@ class EF_MSISDN(LinFixedEF):
return alpha_identifier + encoded_msisdn
# TS 51.011 Section 10.5.6
class EF_SMSP(LinFixedEF):
def __init__(self, fid='6f42', sfid=None, name='EF.SMSP', desc='Short message service parameters'):
super().__init__(fid, sfid=sfid, name=name,
desc=desc, rec_len={28, None})
class ValidityPeriodAdapter(Adapter):
def _decode(self, obj, context, path):
if obj <= 143:
return obj + 1 * 5
elif obj <= 167:
return 12 * 60 + ((obj - 143) * 30)
elif obj <= 196:
return (obj - 166) * (24 * 60)
elif obj <= 255:
return (obj - 192) * (7 * 24 * 60)
else:
raise ValueError
def _encode(self, obj, context, path):
if obj <= 12*60:
return obj/5 - 1
elif obj <= 24*60:
return 143 + ((obj - (12 * 60)) / 30)
elif obj <= 30 * 24 * 60:
return 166 + (obj / (24 * 60))
elif obj <= 63 * 7 * 24 * 60:
return 192 + (obj / (7 * 24 * 60))
else:
raise ValueError
def __init__(self, fid='6f42', sfid=None, name='EF.SMSP', desc='Short message service parameters', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={28, None}, **kwargs)
ScAddr = Struct('length'/Int8ub, 'ton_npi'/TonNpi, 'call_number'/BcdAdapter(Rpad(Bytes(10))))
self._construct = Struct('alpha_id'/COptional(GsmStringAdapter(Rpad(Bytes(this._.total_len-28)))),
'parameter_indicators'/InvertAdapter(FlagsEnum(Byte, tp_dest_addr=1, tp_sc_addr=2,
tp_pid=3, tp_dcs=4, tp_vp=5)),
'tp_dest_addr'/ScAddr,
'tp_sc_addr'/ScAddr,
'tp_pid'/HexAdapter(Bytes(1)),
'tp_dcs'/HexAdapter(Bytes(1)),
'tp_vp_minutes'/EF_SMSP.ValidityPeriodAdapter(Byte))
# TS 51.011 Section 10.5.7
class EF_SMSS(TransparentEF):
class MemCapAdapter(Adapter):
def _decode(self, obj, context, path):
@@ -425,41 +447,37 @@ class EF_SMSS(TransparentEF):
def _encode(self, obj, context, path):
return 0 if obj else 1
def __init__(self, fid='6f43', sfid=None, name='EF.SMSS', desc='SMS status', size={2, 8}):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
def __init__(self, fid='6f43', sfid=None, name='EF.SMSS', desc='SMS status', size={2, 8}, **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self._construct = Struct(
'last_used_tpmr'/Int8ub, 'memory_capacity_exceeded'/self.MemCapAdapter(Int8ub))
# TS 51.011 Section 10.5.8
class EF_SMSR(LinFixedEF):
def __init__(self, fid='6f47', sfid=None, name='EF.SMSR', desc='SMS status reports', rec_len={30, 30}):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
def __init__(self, fid='6f47', sfid=None, name='EF.SMSR', desc='SMS status reports', rec_len={30, 30}, **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._construct = Struct(
'sms_record_id'/Int8ub, 'sms_status_report'/HexAdapter(Bytes(29)))
class EF_EXT(LinFixedEF):
def __init__(self, fid, sfid=None, name='EF.EXT', desc='Extension', rec_len={13, 13}):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
def __init__(self, fid, sfid=None, name='EF.EXT', desc='Extension', rec_len={13, 13}, **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._construct = Struct(
'record_type'/Int8ub, 'extension_data'/HexAdapter(Bytes(11)), 'identifier'/Int8ub)
# TS 51.011 Section 10.5.16
class EF_CMI(LinFixedEF):
def __init__(self, fid='6f58', sfid=None, name='EF.CMI', rec_len={2, 21},
desc='Comparison Method Information'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
desc='Comparison Method Information', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._construct = Struct(
'alpha_id'/Bytes(this._.total_len-1), 'comparison_method_id'/Int8ub)
'alpha_id'/GsmStringAdapter(Rpad(Bytes(this._.total_len-1))), 'comparison_method_id'/Int8ub)
class DF_TELECOM(CardDF):
def __init__(self, fid='7f10', name='DF.TELECOM', desc=None):
super().__init__(fid=fid, name=name, desc=desc)
def __init__(self, fid='7f10', name='DF.TELECOM', desc=None, **kwargs):
super().__init__(fid=fid, name=name, desc=desc, **kwargs)
files = [
EF_ADN(),
EF_ADN(fid='6f3b', name='EF.FDN', desc='Fixed dialling numbers'),
@@ -487,8 +505,6 @@ class DF_TELECOM(CardDF):
######################################################################
# TS 51.011 Section 10.3.1
class EF_LP(TransRecEF):
def __init__(self, fid='6f05', sfid=None, name='EF.LP', size={1, None}, rec_len=1,
desc='Language Preference'):
@@ -501,8 +517,6 @@ class EF_LP(TransRecEF):
return h2b(in_json)
# TS 51.011 Section 10.3.2
class EF_IMSI(TransparentEF):
def __init__(self, fid='6f07', sfid=None, name='EF.IMSI', desc='IMSI', size={9, 9}):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
@@ -543,8 +557,8 @@ class EF_IMSI(TransparentEF):
# TS 51.011 Section 10.3.4
class EF_PLMNsel(TransRecEF):
def __init__(self, fid='6f30', sfid=None, name='EF.PLMNsel', desc='PLMN selector',
size={24, None}, rec_len=3):
super().__init__(fid, name=name, sfid=sfid, desc=desc, size=size, rec_len=rec_len)
size={24, None}, rec_len=3, **kwargs):
super().__init__(fid, name=name, sfid=sfid, desc=desc, size=size, rec_len=rec_len, **kwargs)
def _decode_record_hex(self, in_hex):
if in_hex[:6] == "ffffff":
@@ -559,17 +573,13 @@ class EF_PLMNsel(TransRecEF):
return enc_plmn(in_json['mcc'], in_json['mnc'])
# TS 51.011 Section 10.3.6
class EF_ACMmax(TransparentEF):
def __init__(self, fid='6f37', sfid=None, name='EF.ACMmax', size={3, 3},
desc='ACM maximum value'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='ACM maximum value', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self._construct = Struct('acm_max'/Int24ub)
# TS 51.011 Section 10.3.7
class EF_ServiceTable(TransparentEF):
def __init__(self, fid, sfid, name, desc, size, table):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
@@ -620,11 +630,10 @@ class EF_ServiceTable(TransparentEF):
return out
# TS 51.011 Section 10.3.11
class EF_SPN(TransparentEF):
def __init__(self, fid='6f46', sfid=None, name='EF.SPN', desc='Service Provider Name', size={17, 17}):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
def __init__(self, fid='6f46', sfid=None, name='EF.SPN',
desc='Service Provider Name', size={17, 17}, **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self._construct = BitStruct(
# Byte 1
'rfu'/BitsRFU(6),
@@ -635,30 +644,20 @@ class EF_SPN(TransparentEF):
)
# TS 51.011 Section 10.3.13
class EF_CBMI(TransRecEF):
def __init__(self, fid='6f45', sfid=None, name='EF.CBMI', size={2, None}, rec_len=2,
desc='Cell Broadcast message identifier selection'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
desc='Cell Broadcast message identifier selection', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs)
self._construct = GreedyRange(Int16ub)
# TS 51.011 Section 10.3.15
class EF_ACC(TransparentEF):
def __init__(self, fid='6f78', sfid=None, name='EF.ACC', desc='Access Control Class', size={2, 2}):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
def _decode_bin(self, raw_bin):
return {'acc': unpack('!H', raw_bin)[0]}
def _encode_bin(self, abstract):
return pack('!H', abstract['acc'])
def __init__(self, fid='6f78', sfid=None, name='EF.ACC',
desc='Access Control Class', size={2, 2}, **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self._construct = HexAdapter(Bytes(2))
# TS 51.011 Section 10.3.16
class EF_LOCI(TransparentEF):
def __init__(self, fid='6f7e', sfid=None, name='EF.LOCI', desc='Location Information', size={11, 11}):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
@@ -667,8 +666,6 @@ class EF_LOCI(TransparentEF):
location_area_not_allowed=3))
# TS 51.011 Section 10.3.18
class EF_AD(TransparentEF):
class OP_MODE(enum.IntEnum):
normal = 0x00
@@ -700,92 +697,74 @@ class EF_AD(TransparentEF):
)
# TS 51.011 Section 10.3.20 / 10.3.22
class EF_VGCS(TransRecEF):
def __init__(self, fid='6fb1', sfid=None, name='EF.VGCS', size={4, 200}, rec_len=4,
desc='Voice Group Call Service'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
desc='Voice Group Call Service', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs)
self._construct = BcdAdapter(Bytes(4))
# TS 51.011 Section 10.3.21 / 10.3.23
class EF_VGCSS(TransparentEF):
def __init__(self, fid='6fb2', sfid=None, name='EF.VGCSS', size={7, 7},
desc='Voice Group Call Service Status'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='Voice Group Call Service Status', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self._construct = BitStruct(
'flags'/Bit[50], Padding(6, pattern=b'\xff'))
# TS 51.011 Section 10.3.24
class EF_eMLPP(TransparentEF):
def __init__(self, fid='6fb5', sfid=None, name='EF.eMLPP', size={2, 2},
desc='enhanced Multi Level Pre-emption and Priority'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='enhanced Multi Level Pre-emption and Priority', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
FlagsConstruct = FlagsEnum(
Byte, A=1, B=2, zero=4, one=8, two=16, three=32, four=64)
self._construct = Struct(
'levels'/FlagsConstruct, 'fast_call_setup_cond'/FlagsConstruct)
# TS 51.011 Section 10.3.25
class EF_AAeM(TransparentEF):
def __init__(self, fid='6fb6', sfid=None, name='EF.AAeM', size={1, 1},
desc='Automatic Answer for eMLPP Service'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='Automatic Answer for eMLPP Service', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
FlagsConstruct = FlagsEnum(
Byte, A=1, B=2, zero=4, one=8, two=16, three=32, four=64)
self._construct = Struct('auto_answer_prio_levels'/FlagsConstruct)
# TS 51.011 Section 10.3.26
class EF_CBMID(EF_CBMI):
def __init__(self, fid='6f48', sfid=None, name='EF.CBMID', size={2, None}, rec_len=2,
desc='Cell Broadcast Message Identifier for Data Download'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
desc='Cell Broadcast Message Identifier for Data Download', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs)
self._construct = GreedyRange(Int16ub)
# TS 51.011 Section 10.3.27
class EF_ECC(TransRecEF):
def __init__(self, fid='6fb7', sfid=None, name='EF.ECC', size={3, 15}, rec_len=3,
desc='Emergency Call Codes'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
desc='Emergency Call Codes', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs)
self._construct = GreedyRange(BcdAdapter(Bytes(3)))
# TS 51.011 Section 10.3.28
class EF_CBMIR(TransRecEF):
def __init__(self, fid='6f50', sfid=None, name='EF.CBMIR', size={4, None}, rec_len=4,
desc='Cell Broadcast message identifier range selection'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
desc='Cell Broadcast message identifier range selection', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs)
self._construct = GreedyRange(Struct('lower'/Int16ub, 'upper'/Int16ub))
# TS 51.011 Section 10.3.29
class EF_DCK(TransparentEF):
def __init__(self, fid='6f2c', sfid=None, name='EF.DCK', size={16, 16},
desc='Depersonalisation Control Keys'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='Depersonalisation Control Keys', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self._construct = Struct('network'/BcdAdapter(Bytes(4)),
'network_subset'/BcdAdapter(Bytes(4)),
'service_provider'/BcdAdapter(Bytes(4)),
'corporate'/BcdAdapter(Bytes(4)))
# TS 51.011 Section 10.3.30
class EF_CNL(TransRecEF):
def __init__(self, fid='6f32', sfid=None, name='EF.CNL', size={6, None}, rec_len=6,
desc='Co-operative Network List'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
desc='Co-operative Network List', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs)
def _decode_record_hex(self, in_hex):
(in_plmn, sub, svp, corp) = unpack('!3sBBB', h2b(in_hex))
@@ -804,26 +783,20 @@ class EF_CNL(TransRecEF):
in_json['corporate_id']))
# TS 51.011 Section 10.3.31
class EF_NIA(LinFixedEF):
def __init__(self, fid='6f51', sfid=None, name='EF.NIA', rec_len={1, 32},
desc='Network\'s Indication of Alerting'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
desc='Network\'s Indication of Alerting', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._construct = Struct(
'alerting_category'/Int8ub, 'category'/GreedyBytes)
# TS 51.011 Section 10.3.32
class EF_Kc(TransparentEF):
def __init__(self, fid='6f20', sfid=None, name='EF.Kc', desc='Ciphering key Kc', size={9, 9}):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
self._construct = Struct('kc'/HexAdapter(Bytes(8)), 'cksn'/Int8ub)
# TS 51.011 Section 10.3.33
class EF_LOCIGPRS(TransparentEF):
def __init__(self, fid='6f53', sfid=None, name='EF.LOCIGPRS', desc='GPRS Location Information', size={14, 14}):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
@@ -832,11 +805,9 @@ class EF_LOCIGPRS(TransparentEF):
routing_area_not_allowed=3))
# TS 51.011 Section 10.3.35..37
class EF_xPLMNwAcT(TransRecEF):
def __init__(self, fid, sfid=None, name=None, desc=None, size={40, None}, rec_len=5):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
def __init__(self, fid, sfid=None, name=None, desc=None, size={40, None}, rec_len=5, **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs)
def _decode_record_hex(self, in_hex):
if in_hex[:6] == "ffffff":
@@ -883,8 +854,6 @@ class EF_xPLMNwAcT(TransRecEF):
return '%04X' % (u16)
# TS 51.011 Section 10.3.38
class EF_CPBCCH(TransRecEF):
def __init__(self, fid='6f63', sfid=None, name='EF.CPBCCH', size={2, 14}, rec_len=2,
desc='CPBCCH Information'):
@@ -892,8 +861,6 @@ class EF_CPBCCH(TransRecEF):
self._construct = Struct('cpbcch'/Int16ub)
# TS 51.011 Section 10.3.39
class EF_InvScan(TransparentEF):
def __init__(self, fid='6f64', sfid=None, name='EF.InvScan', size={1, 1},
desc='IOnvestigation Scan'):
@@ -902,8 +869,6 @@ class EF_InvScan(TransparentEF):
Byte, in_limited_service_mode=1, after_successful_plmn_selection=2)
# TS 51.011 Section 4.2.58
class EF_PNN(LinFixedEF):
class FullNameForNetwork(BER_TLV_IE, tag=0x43):
# TS 24.008 10.5.3.5a
@@ -916,42 +881,34 @@ class EF_PNN(LinFixedEF):
class NetworkNameCollection(TLV_IE_Collection, nested=[FullNameForNetwork, ShortNameForNetwork]):
pass
def __init__(self, fid='6fc5', sfid=None, name='EF.PNN', desc='PLMN Network Name'):
super().__init__(fid, sfid=sfid, name=name, desc=desc)
def __init__(self, fid='6fc5', sfid=None, name='EF.PNN', desc='PLMN Network Name', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, **kwargs)
self._tlv = EF_PNN.NetworkNameCollection
# TS 51.011 Section 10.3.42
class EF_OPL(LinFixedEF):
def __init__(self, fid='6fc6', sfid=None, name='EF.OPL', rec_len={8, 8}, desc='Operator PLMN List'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
self._construct = Struct('lai'/Bytes(5), 'pnn_record_id'/Int8ub)
def __init__(self, fid='6fc6', sfid=None, name='EF.OPL', rec_len={8, 8}, desc='Operator PLMN List', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._construct = Struct('lai'/Struct('mcc_mnc'/BcdAdapter(Bytes(3)), 'lac_min'/Bytes(2), 'lac_max'/Bytes(2)), 'pnn_record_id'/Int8ub)
# TS 51.011 Section 10.3.44 + TS 31.102 4.2.62
class EF_MBI(LinFixedEF):
def __init__(self, fid='6fc9', sfid=None, name='EF.MBI', rec_len={4, 5}, desc='Mailbox Identifier'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
def __init__(self, fid='6fc9', sfid=None, name='EF.MBI', rec_len={4, 5}, desc='Mailbox Identifier', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._construct = Struct('mbi_voicemail'/Int8ub, 'mbi_fax'/Int8ub, 'mbi_email'/Int8ub,
'mbi_other'/Int8ub, 'mbi_videocall'/COptional(Int8ub))
# TS 51.011 Section 10.3.45 + TS 31.102 4.2.63
class EF_MWIS(LinFixedEF):
def __init__(self, fid='6fca', sfid=None, name='EF.MWIS', rec_len={5, 6},
desc='Message Waiting Indication Status'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
desc='Message Waiting Indication Status', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._construct = Struct('mwi_status'/FlagsEnum(Byte, voicemail=1, fax=2, email=4, other=8, videomail=16),
'num_waiting_voicemail'/Int8ub,
'num_waiting_fax'/Int8ub, 'num_waiting_email'/Int8ub,
'num_waiting_other'/Int8ub, 'num_waiting_videomail'/COptional(Int8ub))
# TS 51.011 Section 10.3.66
class EF_SPDI(TransparentEF):
class ServiceProviderPLMN(BER_TLV_IE, tag=0x80):
# flexible numbers of 3-byte PLMN records
@@ -960,28 +917,22 @@ class EF_SPDI(TransparentEF):
class SPDI(BER_TLV_IE, tag=0xA3, nested=[ServiceProviderPLMN]):
pass
def __init__(self, fid='6fcd', sfid=None, name='EF.SPDI',
desc='Service Provider Display Information'):
super().__init__(fid, sfid=sfid, name=name, desc=desc)
desc='Service Provider Display Information', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, **kwargs)
self._tlv = EF_SPDI.SPDI
# TS 51.011 Section 10.3.51
class EF_MMSN(LinFixedEF):
def __init__(self, fid='6fce', sfid=None, name='EF.MMSN', rec_len={4, 20}, desc='MMS Notification'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
def __init__(self, fid='6fce', sfid=None, name='EF.MMSN', rec_len={4, 20}, desc='MMS Notification', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._construct = Struct('mms_status'/Bytes(2), 'mms_implementation'/Bytes(1),
'mms_notification'/Bytes(this._.total_len-4), 'ext_record_nr'/Byte)
# TS 51.011 Annex K.1
class MMS_Implementation(BER_TLV_IE, tag=0x80):
_construct = FlagsEnum(Byte, WAP=1)
# TS 51.011 Section 10.3.53
class EF_MMSICP(TransparentEF):
class MMS_Relay_Server(BER_TLV_IE, tag=0x81):
# 3GPP TS 23.140
@@ -999,13 +950,11 @@ class EF_MMSICP(TransparentEF):
nested=[MMS_Implementation, MMS_Relay_Server, Interface_to_CN, Gateway]):
pass
def __init__(self, fid='6fd0', sfid=None, name='EF.MMSICP', size={1, None},
desc='MMS Issuer Connectivity Parameters'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='MMS Issuer Connectivity Parameters', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
self._tlv = EF_MMSICP.MMS_ConnectivityParamters
# TS 51.011 Section 10.3.54
class EF_MMSUP(LinFixedEF):
class MMS_UserPref_ProfileName(BER_TLV_IE, tag=0x81):
pass
@@ -1017,17 +966,15 @@ class EF_MMSUP(LinFixedEF):
nested=[MMS_Implementation, MMS_UserPref_ProfileName, MMS_UserPref_Info]):
pass
def __init__(self, fid='6fd1', sfid=None, name='EF.MMSUP', rec_len={1, None},
desc='MMS User Preferences'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len)
desc='MMS User Preferences', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs)
self._tlv = EF_MMSUP.MMS_User_Preferences
# TS 51.011 Section 10.3.55
class EF_MMSUCP(TransparentEF):
def __init__(self, fid='6fd2', sfid=None, name='EF.MMSUCP', size={1, None},
desc='MMS User Connectivity Parameters'):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
desc='MMS User Connectivity Parameters', **kwargs):
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs)
class DF_GSM(CardDF):
@@ -1162,7 +1109,9 @@ class CardProfileSIM(CardProfile):
4: 'working_ef'
}
ret = {
'file_descriptor': {},
'file_descriptor': {
'file_descriptor_byte': {},
},
'proprietary_info': {},
}
ret['file_id'] = b2h(resp_bin[4:6])
@@ -1170,7 +1119,7 @@ class CardProfileSIM(CardProfile):
resp_bin[2:4], 'big')
file_type = type_of_file_map[resp_bin[6]
] if resp_bin[6] in type_of_file_map else resp_bin[6]
ret['file_descriptor']['file_type'] = file_type
ret['file_descriptor']['file_descriptor_byte']['file_type'] = file_type
if file_type in ['mf', 'df']:
ret['file_characteristics'] = b2h(resp_bin[13:14])
ret['num_direct_child_df'] = resp_bin[14]
@@ -1180,7 +1129,7 @@ class CardProfileSIM(CardProfile):
elif file_type in ['working_ef']:
file_struct = struct_of_file_map[resp_bin[13]
] if resp_bin[13] in struct_of_file_map else resp_bin[13]
ret['file_descriptor']['structure'] = file_struct
ret['file_descriptor']['file_descriptor_byte']['structure'] = file_struct
ret['access_conditions'] = b2h(resp_bin[8:10])
if resp_bin[11] & 0x01 == 0:
ret['life_cycle_status_int'] = 'operational_activated'

View File

@@ -1333,7 +1333,7 @@ class DataObject(abc.ABC):
bytes encoded in TLV format.
"""
val = self.to_bytes()
return bytes(self._compute_tag()) + bytes(len(val)) + val
return bertlv_encode_tag(self._compute_tag()) + bertlv_encode_len(len(val)) + val
# 'codec' interface
def decode(self, binary: bytes) -> Tuple[dict, bytes]:
@@ -1481,7 +1481,8 @@ class DataObjectChoice(DataObjectCollection):
# 'codec' interface
def encode(self, decoded) -> bytes:
obj = self.members_by_name(decoded[0])
obj = self.members_by_name[list(decoded)[0]]
obj.decoded = list(decoded.values())[0]
return obj.to_tlv()
@@ -1560,6 +1561,18 @@ class DataObjectSequence:
i += 1
return encoded
def encode_multi(self, decoded) -> bytes:
"""Encode multiple occurrences of the sequence from the decoded input data.
Args:
decoded : list of json-serializable input data; one sequence per list item
Returns:
binary encoded output data
"""
encoded = bytearray()
for d in decoded:
encoded += self.encode(d)
return encoded
class CardCommand:
"""A single card command / instruction."""

View File

@@ -0,0 +1,69 @@
# script to be used with pySim-shell.py which is part of the Osmocom pysim package,
# found at https://osmocom.org/projects/pysim/wiki
set echo true
# this script will deactivate all 5G related services and files. This can be used
# in case you do not wish to use any 5G services, or you do not wish to configure
# the 5G specific files on the USIM card. The card will then behave like a 3G USIM
# without any 5G capability, using the default fall-back mechanisms specified by 3GPP.
# TODO: add your card-specific ADM pin at the end of the verify_adm line below
verify_adm
# deactivate any 5G related services in EF.UST
select ADF.USIM
select EF.UST
ust_service_deactivate 122
ust_service_deactivate 123
ust_service_deactivate 124
ust_service_deactivate 125
ust_service_deactivate 127
ust_service_deactivate 129
ust_service_deactivate 130
ust_service_deactivate 132
ust_service_deactivate 133
ust_service_deactivate 134
ust_service_deactivate 135
# deactivate all files in EF.5GS
select ADF.USIM
select DF.5GS
select EF.5GAUTHKEYS
deactivate_file
select EF.5GS3GPPLOCI
deactivate_file
select EF.5GSN3GPPNSC
deactivate_file
select EF.5GSN3GPPLOCI
deactivate_file
select EF.5GS3GPPNSC
deactivate_file
# only exists on sysmoISIM-SJA2v2
select EF.OPL5G
deactivate_file
select EF.Routing_Indicator
deactivate_file
select EF.SUCI_Calc_Info
deactivate_file
select EF.SUPI_NAI
deactivate_file
# only exists on sysmoISIM-SJA2v2
select EF.TN3GPPSNN
deactivate_file
select EF.UAC_AIC
deactivate_file
# only exists on sysmoISIM-SJA2v2
select EF.URSP
deactivate_file

View File

@@ -0,0 +1,74 @@
# script to be used with pySim-shell.py which is part of the Osmocom pysim package,
# found at https://osmocom.org/projects/pysim/wiki
set echo true
# this script will deactivate all IMS related services and files. This can be used
# in case you do not wish to use any IMS services, or you do not wish to configure
# the IMS specific files on the USIM/ISIM cards. The card will then behave like a 3G USIM
# without any IMS capability, using the default fall-back mechanisms specified by 3GPP.
# TODO: add your card-specific ADM pin at the end of the verify_adm line below
verify_adm
# deactivate any IMS related services in EF.UST
select ADF.USIM
select EF.UST
ust_service_deactivate 93
ust_service_deactivate 95
ust_service_deactivate 104
ust_service_deactivate 105
ust_service_deactivate 106
ust_service_deactivate 107
ust_service_deactivate 108
ust_service_deactivate 109
ust_service_deactivate 110
ust_service_deactivate 112
ust_service_deactivate 114
ust_service_deactivate 115
ust_service_deactivate 118
ust_service_deactivate 120
ust_service_deactivate 131
ust_service_deactivate 134
# deactivate all IMS related files in ADF.USIM
select ADF.USIM
select EF.UICCIARI
deactivate_file
select EF.ePDGId
deactivate_file
select EF.ePDGSelection
deactivate_file
select EF.ePDGIdEm
deactivate_file
select EF.ePDGSelectionEm
deactivate_file
select EF.FromPreferred
deactivate_file
select EF.IMSConfigData
deactivate_file
select EF.3GPPPSDATAOFF
deactivate_file
select EF.3GPPPSDATAOFFservicelist
deactivate_file
select EF.XCAPConfigData
deactivate_file
select EF.MuDMiDConfigData
deactivate_file
echo "Please make sure to manually disable the ISIM applet as described in the end of the script"
# you can currently only manually do this via GlobalPlatformPro or some other tool using
# java -jar ./gp.jar --key-enc KIC1 --key-mac KID1 --key-dek KIK1 --lock-applet A0000000871004FFFFFFFF8907090000
# (substituting KIC1/KID1/KIK1 with the card-specific keys, of course)
quit

37
tests/test_construct.py Normal file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env python3
import unittest
from pySim.construct import *
tests = [
( b'\x80', 0x80 ),
( b'\x80\x01', 0x8001 ),
( b'\x80\x00\x01', 0x800001 ),
( b'\x80\x23\x42\x01', 0x80234201 ),
]
class TestGreedyInt(unittest.TestCase):
def test_GreedyInt_decoder(self):
gi = GreedyInteger()
for t in tests:
self.assertEqual(gi.parse(t[0]), t[1])
def test_GreedyInt_encoder(self):
gi = GreedyInteger()
for t in tests:
self.assertEqual(t[0], gi.build(t[1]))
pass
class TestUtils(unittest.TestCase):
def test_filter_dict(self):
inp = {'foo': 0xf00, '_bar' : 0xba5, 'baz': 0xba2 }
out = {'foo': 0xf00, 'baz': 0xba2 }
self.assertEqual(filter_dict(inp), out)
def test_filter_dict_nested(self):
inp = {'foo': 0xf00, 'nest': {'_bar' : 0xba5}, 'baz': 0xba2 }
out = {'foo': 0xf00, 'nest': {}, 'baz': 0xba2 }
self.assertEqual(filter_dict(inp), out)
if __name__ == "__main__":
unittest.main()

120
tests/test_tlv.py Normal file
View File

@@ -0,0 +1,120 @@
#!/usr/bin/env python3
# (C) 2022 by Harald Welte <laforge@osmocom.org>
# All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
from pySim.tlv import *
class TestUtils(unittest.TestCase):
def test_camel_to_snake(self):
cases = [
('CamelCase', 'camel_case'),
('CamelCaseUPPER', 'camel_case_upper'),
('Camel_CASE_underSCORE', 'camel_case_under_score'),
]
for c in cases:
self.assertEqual(camel_to_snake(c[0]), c[1])
def test_flatten_dict_lists(self):
inp = [
{ 'first': 1 },
{ 'second': 2 },
{ 'third': 3 },
]
out = { 'first': 1, 'second':2, 'third': 3}
self.assertEqual(flatten_dict_lists(inp), out)
def test_flatten_dict_lists_nodict(self):
inp = [
{ 'first': 1 },
{ 'second': 2 },
{ 'third': 3 },
4,
]
self.assertEqual(flatten_dict_lists(inp), inp)
def test_flatten_dict_lists_nested(self):
inp = {'top': [
{ 'first': 1 },
{ 'second': 2 },
{ 'third': 3 },
] }
out = {'top': { 'first': 1, 'second':2, 'third': 3 } }
self.assertEqual(flatten_dict_lists(inp), out)
class TestTranscodable(unittest.TestCase):
class XC_constr_class(Transcodable):
_construct = Int8ub
def __init__(self):
super().__init__();
def test_XC_constr_class(self):
"""Transcodable derived class with _construct class variable"""
xc = TestTranscodable.XC_constr_class()
self.assertEqual(xc.from_bytes(b'\x23'), 35)
self.assertEqual(xc.to_bytes(), b'\x23')
class XC_constr_instance(Transcodable):
def __init__(self):
super().__init__();
self._construct = Int8ub
def test_XC_constr_instance(self):
"""Transcodable derived class with _construct instance variable"""
xc = TestTranscodable.XC_constr_instance()
self.assertEqual(xc.from_bytes(b'\x23'), 35)
self.assertEqual(xc.to_bytes(), b'\x23')
class XC_method_instance(Transcodable):
def __init__(self):
super().__init__();
def _from_bytes(self, do):
return ('decoded', do)
def _to_bytes(self):
return self.decoded[1]
def test_XC_method_instance(self):
"""Transcodable derived class with _{from,to}_bytes() methods"""
xc = TestTranscodable.XC_method_instance()
self.assertEqual(xc.to_bytes(), b'')
self.assertEqual(xc.from_bytes(b''), None)
self.assertEqual(xc.from_bytes(b'\x23'), ('decoded', b'\x23'))
self.assertEqual(xc.to_bytes(), b'\x23')
class TestIE(unittest.TestCase):
class MyIE(IE, tag=0x23, desc='My IE description'):
_construct = Int8ub
def to_ie(self):
return self.to_bytes()
def test_IE_empty(self):
ie = TestIE.MyIE()
self.assertEqual(ie.to_dict(), {'my_ie': None})
self.assertEqual(repr(ie), 'MyIE(None)')
self.assertEqual(ie.is_constructed(), False)
def test_IE_from_bytes(self):
ie = TestIE.MyIE()
ie.from_bytes(b'\x42')
self.assertEqual(ie.to_dict(), {'my_ie': 66})
self.assertEqual(repr(ie), 'MyIE(66)')
self.assertEqual(ie.is_constructed(), False)
self.assertEqual(ie.to_bytes(), b'\x42')
self.assertEqual(ie.to_ie(), b'\x42')
if __name__ == "__main__":
unittest.main()

View File

@@ -4,6 +4,22 @@ import unittest
from pySim import utils
from pySim.ts_31_102 import EF_SUCI_Calc_Info
# we don't really want to thest TS 102 221, but the underlying DataObject codebase
from pySim.ts_102_221 import AM_DO_EF, AM_DO_DF, SC_DO
class DoTestCase(unittest.TestCase):
def testSeqOfChoices(self):
"""A sequence of two choices with each a variety of DO/TLVs"""
arr_seq = utils.DataObjectSequence('arr', sequence=[AM_DO_EF, SC_DO])
# input data
dec_in = [{'access_mode': ['update_erase', 'read_search_compare']}, {'control_reference_template':'PIN1'}]
# encode it once
encoded = arr_seq.encode(dec_in)
# decode again
re_decoded = arr_seq.decode(encoded)
self.assertEqual(dec_in, re_decoded[0])
class DecTestCase(unittest.TestCase):
# TS33.501 Annex C.4 test keys
hnet_pubkey_profile_b = "0272DA71976234CE833A6907425867B82E074D44EF907DFB4B3E21C1C2256EBCD1" # ID 27 in test file