#!/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 . 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)