Compare commits
13 Commits
pmaier/ota
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8f3c78135 | ||
|
|
6b9b46a5a4 | ||
|
|
b6b4501e37 | ||
|
|
54658fa3a9 | ||
|
|
eb04bb1082 | ||
|
|
453fde5a3a | ||
|
|
57237b650e | ||
|
|
1f94791240 | ||
|
|
1a28575327 | ||
|
|
e7016b5b57 | ||
|
|
e80f3160a9 | ||
|
|
917ad7f9f5 | ||
|
|
8b2a49aa8e |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
.*.swp
|
.*.sw?
|
||||||
|
|
||||||
/docs/_*
|
/docs/_*
|
||||||
/docs/generated
|
/docs/generated
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ class SmppHandler:
|
|||||||
tuple containing the last response data and the last status word as byte strings
|
tuple containing the last response data and the last status word as byte strings
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.info("C-APDU sending: %s..." % b2h(apdu))
|
logger.info("C-APDU sending: %s...", b2h(apdu))
|
||||||
|
|
||||||
# translate to Secured OTA RFM
|
# translate to Secured OTA RFM
|
||||||
secured = self.ota_dialect.encode_cmd(self.ota_keyset, self.tar, self.spi, apdu=apdu)
|
secured = self.ota_dialect.encode_cmd(self.ota_keyset, self.tar, self.spi, apdu=apdu)
|
||||||
@@ -194,19 +194,19 @@ if __name__ == '__main__':
|
|||||||
option_parser.add_argument('--tar', required=True, type=is_hexstr, help='Toolkit Application Reference')
|
option_parser.add_argument('--tar', required=True, type=is_hexstr, help='Toolkit Application Reference')
|
||||||
option_parser.add_argument("--cntr_req", choices=CNTR_REQ.decmapping.values(), default='no_counter',
|
option_parser.add_argument("--cntr_req", choices=CNTR_REQ.decmapping.values(), default='no_counter',
|
||||||
help="Counter requirement")
|
help="Counter requirement")
|
||||||
option_parser.add_argument('--ciphering', default=True, type=bool, help='Enable ciphering')
|
option_parser.add_argument('--no-ciphering', action='store_true', default=False, help='Disable ciphering')
|
||||||
option_parser.add_argument("--rc-cc-ds", choices=RC_CC_DS.decmapping.values(), default='cc',
|
option_parser.add_argument("--rc-cc-ds", choices=RC_CC_DS.decmapping.values(), default='cc',
|
||||||
help="message check (rc=redundency check, cc=crypt. checksum, ds=digital signature)")
|
help="message check (rc=redundency check, cc=crypt. checksum, ds=digital signature)")
|
||||||
option_parser.add_argument('--por-in-submit', default=False, type=bool,
|
option_parser.add_argument('--por-in-submit', action='store_true', default=False,
|
||||||
help='require PoR to be sent via SMS-SUBMIT')
|
help='require PoR to be sent via SMS-SUBMIT')
|
||||||
option_parser.add_argument('--por-shall-be-ciphered', default=True, type=bool, help='require encrypted PoR')
|
option_parser.add_argument('--por-no-ciphering', action='store_true', default=False, help='Disable ciphering (PoR)')
|
||||||
option_parser.add_argument("--por-rc-cc-ds", choices=RC_CC_DS.decmapping.values(), default='cc',
|
option_parser.add_argument("--por-rc-cc-ds", choices=RC_CC_DS.decmapping.values(), default='cc',
|
||||||
help="PoR check (rc=redundency check, cc=crypt. checksum, ds=digital signature)")
|
help="PoR check (rc=redundency check, cc=crypt. checksum, ds=digital signature)")
|
||||||
option_parser.add_argument("--por_req", choices=POR_REQ.decmapping.values(), default='por_required',
|
option_parser.add_argument("--por_req", choices=POR_REQ.decmapping.values(), default='por_required',
|
||||||
help="Proof of Receipt requirements")
|
help="Proof of Receipt requirements")
|
||||||
option_parser.add_argument('--src-addr', default='12', type=str, help='TODO')
|
option_parser.add_argument('--src-addr', default='12', type=str, help='SMS source address (MSISDN)')
|
||||||
option_parser.add_argument('--dest-addr', default='23', type=str, help='TODO')
|
option_parser.add_argument('--dest-addr', default='23', type=str, help='SMS destination address (MSISDN)')
|
||||||
option_parser.add_argument('--timeout', default=10, type=int, help='TODO')
|
option_parser.add_argument('--timeout', default=10, type=int, help='Maximum response waiting time')
|
||||||
option_parser.add_argument('-a', '--apdu', action='append', required=True, type=is_hexstr, help='C-APDU to send')
|
option_parser.add_argument('-a', '--apdu', action='append', required=True, type=is_hexstr, help='C-APDU to send')
|
||||||
opts = option_parser.parse_args()
|
opts = option_parser.parse_args()
|
||||||
|
|
||||||
@@ -214,18 +214,22 @@ if __name__ == '__main__':
|
|||||||
format='%(asctime)s %(levelname)s %(message)s',
|
format='%(asctime)s %(levelname)s %(message)s',
|
||||||
datefmt='%Y-%m-%d %H:%M:%S')
|
datefmt='%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
if opts.kic_idx != opts.kid_idx:
|
||||||
|
logger.warning("KIC index (%s) and KID index (%s) are different (security violation, card should reject message)",
|
||||||
|
opts.kic_idx, opts.kid_idx)
|
||||||
|
|
||||||
ota_keyset = OtaKeyset(algo_crypt=opts.algo_crypt,
|
ota_keyset = OtaKeyset(algo_crypt=opts.algo_crypt,
|
||||||
kic_idx=opts.kic_idx,
|
kic_idx=opts.kic_idx,
|
||||||
kic=h2b(opts.kic),
|
kic=h2b(opts.kic),
|
||||||
algo_auth=opts.algo_auth,
|
algo_auth=opts.algo_auth,
|
||||||
kid_idx=opts.kic_idx,
|
kid_idx=opts.kid_idx,
|
||||||
kid=h2b(opts.kid),
|
kid=h2b(opts.kid),
|
||||||
cntr=opts.cntr)
|
cntr=opts.cntr)
|
||||||
spi = {'counter' : opts.cntr_req,
|
spi = {'counter' : opts.cntr_req,
|
||||||
'ciphering' : opts.ciphering,
|
'ciphering' : not opts.no_ciphering,
|
||||||
'rc_cc_ds': opts.rc_cc_ds,
|
'rc_cc_ds': opts.rc_cc_ds,
|
||||||
'por_in_submit':opts.por_in_submit,
|
'por_in_submit': opts.por_in_submit,
|
||||||
'por_shall_be_ciphered':opts.por_shall_be_ciphered,
|
'por_shall_be_ciphered': not opts.por_no_ciphering,
|
||||||
'por_rc_cc_ds': opts.por_rc_cc_ds,
|
'por_rc_cc_ds': opts.por_rc_cc_ds,
|
||||||
'por': opts.por_req}
|
'por': opts.por_req}
|
||||||
apdu = h2b("".join(opts.apdu))
|
apdu = h2b("".join(opts.apdu))
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ def compile_asn1_subdir(subdir_name:str, codec='der'):
|
|||||||
__ver = sys.version_info
|
__ver = sys.version_info
|
||||||
if (__ver.major, __ver.minor) >= (3, 9):
|
if (__ver.major, __ver.minor) >= (3, 9):
|
||||||
for i in resources.files('pySim.esim').joinpath('asn1').joinpath(subdir_name).iterdir():
|
for i in resources.files('pySim.esim').joinpath('asn1').joinpath(subdir_name).iterdir():
|
||||||
|
if not i.name.endswith('.asn'):
|
||||||
|
continue
|
||||||
asn_txt += i.read_text()
|
asn_txt += i.read_text()
|
||||||
asn_txt += "\n"
|
asn_txt += "\n"
|
||||||
#else:
|
#else:
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import abc
|
|||||||
import requests
|
import requests
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
from re import match
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import base64
|
import base64
|
||||||
from twisted.web.server import Request
|
from twisted.web.server import Request
|
||||||
@@ -211,7 +210,7 @@ class JsonHttpApiFunction(abc.ABC):
|
|||||||
# additional custom HTTP headers (server responses)
|
# additional custom HTTP headers (server responses)
|
||||||
extra_http_res_headers = {}
|
extra_http_res_headers = {}
|
||||||
|
|
||||||
def __new__(cls, *args, role = None, **kwargs):
|
def __new__(cls, *args, role = 'legacy_client', **kwargs):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
args: (see JsonHttpApiClient and JsonHttpApiServer)
|
args: (see JsonHttpApiClient and JsonHttpApiServer)
|
||||||
@@ -221,14 +220,13 @@ class JsonHttpApiFunction(abc.ABC):
|
|||||||
|
|
||||||
# Create a dictionary with the class attributes of this class (the properties listed above and the encode_
|
# Create a dictionary with the class attributes of this class (the properties listed above and the encode_
|
||||||
# decode_ methods below). The dictionary will not include any dunder/magic methods
|
# decode_ methods below). The dictionary will not include any dunder/magic methods
|
||||||
cls_attr = { attr_name: getattr(cls, attr_name) for attr_name in dir(cls) if not match("__.*__", attr_name) }
|
cls_attr = {attr_name: getattr(cls, attr_name) for attr_name in dir(cls) if not attr_name.startswith('__')}
|
||||||
|
|
||||||
# Normal instantiation as JsonHttpApiFunction:
|
# Normal instantiation as JsonHttpApiFunction:
|
||||||
if len(args) == 0:
|
if len(args) == 0 and len(kwargs) == 0:
|
||||||
return type(cls.__name__, (abc.ABC,), cls_attr)()
|
return type(cls.__name__, (abc.ABC,), cls_attr)()
|
||||||
|
|
||||||
# Instantiation as as JsonHttpApiFunction with a JsonHttpApiClient or JsonHttpApiServer base
|
# Instantiation as as JsonHttpApiFunction with a JsonHttpApiClient or JsonHttpApiServer base
|
||||||
role = kwargs.get('role', 'legacy_client')
|
|
||||||
if role == 'legacy_client':
|
if role == 'legacy_client':
|
||||||
# Deprecated: With the advent of the server role (JsonHttpApiServer) the API had to be changed. To maintain
|
# Deprecated: With the advent of the server role (JsonHttpApiServer) the API had to be changed. To maintain
|
||||||
# compatibility with existing code (out-of-tree) the original behaviour and API interface and behaviour had
|
# compatibility with existing code (out-of-tree) the original behaviour and API interface and behaviour had
|
||||||
|
|||||||
@@ -352,6 +352,7 @@ class SmspTpScAddr(ConfigurableParameter):
|
|||||||
strip_chars = ' \t\r\n'
|
strip_chars = ' \t\r\n'
|
||||||
max_len = 21 # '+' and 20 digits
|
max_len = 21 # '+' and 20 digits
|
||||||
min_len = 1
|
min_len = 1
|
||||||
|
example_input = '+49301234567'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_val(cls, val):
|
def validate_val(cls, val):
|
||||||
@@ -627,7 +628,7 @@ class MilenageRotationConstants(BinaryParam, AlgoConfig):
|
|||||||
name = 'MilenageRotation'
|
name = 'MilenageRotation'
|
||||||
algo_config_key = 'rotationConstants'
|
algo_config_key = 'rotationConstants'
|
||||||
allow_len = 5 # length in bytes (from BinaryParam)
|
allow_len = 5 # length in bytes (from BinaryParam)
|
||||||
example_input = '0a 0b 0c 0d 0e'
|
example_input = '40 00 20 40 60'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_val(cls, val):
|
def validate_val(cls, val):
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ class SeqNumber(BER_TLV_IE, tag=0x80):
|
|||||||
class NotificationAddress(BER_TLV_IE, tag=0x0c):
|
class NotificationAddress(BER_TLV_IE, tag=0x0c):
|
||||||
_construct = Utf8Adapter(GreedyBytes)
|
_construct = Utf8Adapter(GreedyBytes)
|
||||||
class Iccid(BER_TLV_IE, tag=0x5a):
|
class Iccid(BER_TLV_IE, tag=0x5a):
|
||||||
_construct = BcdAdapter(GreedyBytes)
|
_construct = PaddedBcdAdapter(GreedyBytes)
|
||||||
class NotificationMetadata(BER_TLV_IE, tag=0xbf2f, nested=[SeqNumber, ProfileMgmtOperation,
|
class NotificationMetadata(BER_TLV_IE, tag=0xbf2f, nested=[SeqNumber, ProfileMgmtOperation,
|
||||||
NotificationAddress, Iccid]):
|
NotificationAddress, Iccid]):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"profile_info": {
|
"profile_info": {
|
||||||
"iccid": "8949449999999990031f",
|
"iccid": "8949449999999990031",
|
||||||
"isdp_aid": "a0000005591010ffffffff8900001200",
|
"isdp_aid": "a0000005591010ffffffff8900001200",
|
||||||
"profile_state": "disabled",
|
"profile_state": "disabled",
|
||||||
"service_provider_name": "OsmocomSPN",
|
"service_provider_name": "OsmocomSPN",
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import os
|
|||||||
import json
|
import json
|
||||||
from utils import *
|
from utils import *
|
||||||
|
|
||||||
# This testcase requires a sysmoEUICC1-C2T with the test prfile TS48V1-B-UNIQUE (ICCID 8949449999999990031f)
|
# This testcase requires a sysmoEUICC1-C2T with the test prfile TS48V1-B-UNIQUE (ICCID 8949449999999990031)
|
||||||
# installed, and in disabled state. Also the profile must be installed in such a way that notifications are
|
# installed, and in disabled state. Also the profile must be installed in such a way that notifications are
|
||||||
# generated when the profile is disabled or enabled (ProfileMetadata)
|
# generated when the profile is disabled or enabled (ProfileMetadata)
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ set echo true
|
|||||||
|
|
||||||
select ADF.ISD-R
|
select ADF.ISD-R
|
||||||
|
|
||||||
|
# Ensure that the test-profile we intend to test with is actually enabled
|
||||||
|
enable_profile --iccid 89000123456789012341
|
||||||
|
|
||||||
# by ICCID (pre-installed test profile on sysmoEUICC1-C2T)
|
# by ICCID (pre-installed test profile on sysmoEUICC1-C2T)
|
||||||
disable_profile --iccid 89000123456789012341 > enable_disable_profile.tmp
|
disable_profile --iccid 89000123456789012341 > enable_disable_profile.tmp
|
||||||
enable_profile --iccid 89000123456789012341 >> enable_disable_profile.tmp
|
enable_profile --iccid 89000123456789012341 >> enable_disable_profile.tmp
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ set echo true
|
|||||||
|
|
||||||
select ADF.ISD-R
|
select ADF.ISD-R
|
||||||
|
|
||||||
# Generate two (additional) notifications by quickly enabeling the test profile
|
# Ensure that the test-profile is actually enabled. (In case te test-profile
|
||||||
enable_profile --iccid 8949449999999990031f
|
# was disabled, a notification may be generated. The testcase should tolerate
|
||||||
|
# that)
|
||||||
|
enable_profile --iccid 89000123456789012341
|
||||||
|
|
||||||
|
# Generate two (additional) notifications by quickly enabeling the test profile
|
||||||
|
enable_profile --iccid 8949449999999990031
|
||||||
enable_profile --iccid 89000123456789012341
|
enable_profile --iccid 89000123456789012341
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
set debug true
|
set debug true
|
||||||
set echo true
|
set echo true
|
||||||
|
|
||||||
|
# The output of get_profiles_info will also include the "profile_state", which
|
||||||
|
# can be either "enabled" or "disabled". Ensure that the correct profile is
|
||||||
|
# enabled.
|
||||||
|
enable_profile --iccid 89000123456789012341
|
||||||
|
|
||||||
select ADF.ISD-R
|
select ADF.ISD-R
|
||||||
get_profiles_info > get_profiles_info.tmp
|
get_profiles_info > get_profiles_info.tmp
|
||||||
|
|||||||
Reference in New Issue
Block a user