mirror of
https://gitea.osmocom.org/sim-card/pysim.git
synced 2026-05-08 02:15:07 +03:00
WIP: Remote Card Procedure Framework
Problem: When UICC/eUICC cards are deployed into the field it is often difficult to perform modifications to those cards. One important factor that makes after-deployment modifications often difficult is that the key material needed to perform the task must not be handed to the card holder due to security requirements. The presented Remote Card Procedure Framework solves this problem. It provides a so called Remote Card Procedure Client (RCPC), which is a lightwight software client which can be run by the card holder on the remote machine. With the RCPC, the card holder can access a so called Remote Card Procedure Server (RCPC), to which so called Remote Card Procedure Modules (RCPM) can subscribe and publish their functionality. With the RCPC, the card holder can browse the functionality offered by those connected modules and eventually the card holder may execute a certain procedure by passing a command to the RCPS. When a procedure is carried out, the RCPS automatically retrieves the required key material from a database or CSV file and passes those keys on to the selected RCPM. The RCPM can then use the key material to establish a secure channel to carry out the procedure. The procedure is then protected by a secure channel and the key material is never disclosed towards the card holder on the remote end. The framework is desinged in such a way that existing pySim APIs and functions can be used from the RCPM API user code. Also only minimal boilerplate code is required. The implementation also ships with a comprehensive example. Related: SYS#6959
This commit is contained in:
126
contrib/rcp/usage_example/rcp_module.py
Executable file
126
contrib/rcp/usage_example/rcp_module.py
Executable file
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# (C) 2026 by sysmocom - s.f.m.c. GmbH
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Author: Philipp Maier
|
||||
#
|
||||
# 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 logging
|
||||
from pathlib import Path
|
||||
from pySim.log import PySimLogger
|
||||
from argparse import Namespace
|
||||
from rcp_module_utils import rcpm_setup_argparse, rcpm_run_module, RcpModule, RcpmCmdSrvConnHdlr
|
||||
from pySim.global_platform import GpCardKeyset, SCP02, establish_scp, release_scp, install, store_data
|
||||
from Cryptodome.Random import get_random_bytes
|
||||
from osmocom.utils import h2b, b2h
|
||||
|
||||
log = PySimLogger.get(Path(__file__).stem)
|
||||
|
||||
class ExmpleModule(RcpModule):
|
||||
|
||||
name = Path(__file__).stem
|
||||
cmd_descr = [{'name' : 'reset',
|
||||
'help': 'reset the card',
|
||||
'args' : []},
|
||||
{'name' : 'read_binary',
|
||||
'help': 'read binary data from a transparent file.',
|
||||
'args' : [{ 'name' : '--fid',
|
||||
'spec' : {'required' : True,
|
||||
'help' : 'File identifier to of the file to read',
|
||||
'action' : 'append',
|
||||
'pytype' : 'str'},
|
||||
}
|
||||
]},
|
||||
{'name' : 'read_record',
|
||||
'help': 'read binary data from a transparent file.',
|
||||
'args' : [{ 'name' : '--fid',
|
||||
'spec' : {'required' : True,
|
||||
'help' : 'File identifier to of the file to read',
|
||||
'action' : 'append',
|
||||
'pytype' : 'str'},
|
||||
},
|
||||
{ 'name' : '--record',
|
||||
'spec' : {'required' : True,
|
||||
'help' : 'File record to read',
|
||||
'default' : 1,
|
||||
'pytype' : 'int'},
|
||||
}
|
||||
]},
|
||||
{'name' : 'unlock_aram',
|
||||
'help': 'unlock a locked ARA-M applet on a sysmoISIM-SJA5',
|
||||
'args' : [],
|
||||
'get_keys' : {'uicc' : ['KIC', 'KID', 'KIK']}}
|
||||
]
|
||||
suitable_for = [{'atr' : '3b9f96801f878031e073fe211b674a357530350265f8'}]
|
||||
|
||||
retrieve_uicc_keys = ['KIC', 'KID', 'KIK']
|
||||
|
||||
def cmd_reset(self, hdlr: RcpmCmdSrvConnHdlr) -> int:
|
||||
hdlr.print("resetting UICC/eUICC ...")
|
||||
hdlr.card._scc.reset_card()
|
||||
hdlr.print("ATR is: %s" % hdlr.card._scc.get_atr())
|
||||
return 0
|
||||
|
||||
def cmd_read_binary(self, hdlr: RcpmCmdSrvConnHdlr) -> int:
|
||||
fid = hdlr.cmd_args.fid
|
||||
hdlr.print("reading transparent file: %s ..." % fid)
|
||||
(res, _) = hdlr.card._scc.read_binary(fid)
|
||||
hdlr.print("file content is: %s" % res)
|
||||
return 0
|
||||
|
||||
def cmd_read_record(self, hdlr: RcpmCmdSrvConnHdlr) -> int:
|
||||
fid = hdlr.cmd_args.fid
|
||||
record = hdlr.cmd_args.record
|
||||
hdlr.print("reading linear-fixed file: %s ..." % fid)
|
||||
(res, _) = hdlr.card._scc.read_record(fid, record)
|
||||
hdlr.print("file content is: %s" % res)
|
||||
return 0
|
||||
|
||||
def cmd_unlock_aram(self, hdlr: RcpmCmdSrvConnHdlr) -> int:
|
||||
# Select ADF.ISD
|
||||
hdlr.print("Selecting ADF.ISD ...")
|
||||
hdlr.lchan.scc.send_apdu_checksw("00a4040408a00000000300000000")
|
||||
|
||||
# Estabish secure channel
|
||||
hdlr.print("Establishing secure channel ...")
|
||||
key_ver = 112
|
||||
key_enc = hdlr.keys_uicc['KIC']
|
||||
key_mac = hdlr.keys_uicc['KID']
|
||||
key_dek = hdlr.keys_uicc['KIK']
|
||||
security_level = 3
|
||||
host_challenge_len = 8
|
||||
host_challenge = get_random_bytes(host_challenge_len)
|
||||
kset = GpCardKeyset(key_ver, h2b(key_enc), h2b(key_mac), h2b(key_dek))
|
||||
scp = SCP02(card_keys=kset)
|
||||
establish_scp(hdlr.lchan, scp, host_challenge, security_level)
|
||||
|
||||
# To prove that it works, we need to do something that actually requires to be authenticated
|
||||
# via a secure channel. In this example we will send an unlock command to the ARA-M applet
|
||||
# found on any sysmoISIM-SJA5 card. (see also: https://gitea.osmocom.org/sim-card/aram-applet)
|
||||
hdlr.print("Unlocking ARA-M applet ...")
|
||||
ara_m_aid = "a00000015141434c00"
|
||||
install(hdlr.lchan, 0x20, 0x00, "0000%02x%s000000" % (len(ara_m_aid) // 2, ara_m_aid))
|
||||
store_data(hdlr.lchan, h2b("A2"), structure = 'ber_tlv')
|
||||
|
||||
# Release the secure channel
|
||||
hdlr.print("Done, releasing secure channel ...")
|
||||
release_scp(hdlr.lchan)
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
option_parser = rcpm_setup_argparse("Example Module")
|
||||
opts = option_parser.parse_args()
|
||||
rcpm_run_module(opts, ExmpleModule)
|
||||
Reference in New Issue
Block a user