mirror of
https://gitea.osmocom.org/sim-card/pysim.git
synced 2026-03-16 18:38:32 +03:00
Even though our tests are written in a way that they shouldn't interfere with each other, it may happen that one testrun writes content to a file that upsets a different testrun. The resulting problems are often difficult to diagnose. To minimize the problem, let's add code that can reset the cards to a defined state. This can be done using pySim-shell's export feature. We can generate a backup from a known good state and then play back the backup to reset files that have been changed. Files that didn't change will not be written thanks to the conserve_write feature of pySim-shell. Related: OS#4384 Change-Id: I42eaf61280968518164f2280245136fd30a603ce
132 lines
4.8 KiB
Python
Executable File
132 lines
4.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# Tool to restore (sanitize) card contents from backup files
|
|
#
|
|
# (C) 2024 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 sys
|
|
import os
|
|
import argparse
|
|
|
|
# Make sure we are int the root directly of pySim
|
|
if not os.access("pySim-shell.py", os.R_OK):
|
|
print("This script must be executed from the pySim root directory")
|
|
sys.exit(1)
|
|
sys.path.append("./")
|
|
|
|
from smartcard.util import toHexString, toBytes
|
|
from smartcard.System import readers
|
|
from smartcard.scard import SCARD_SHARE_EXCLUSIVE
|
|
from pySim.utils import i2h, h2i, dec_iccid, boxed_heading_str
|
|
|
|
def backup(reader:int, atr:str, iccid:str):
|
|
""" Create a backup of the card contents """
|
|
|
|
script_dir = os.path.abspath(os.path.dirname(__file__))
|
|
restore_script = script_dir + "/card_backup_" + atr + "_" + iccid + ".script"
|
|
|
|
cmdline = os.getcwd() + "/pySim-shell.py"
|
|
cmdline += " -p " + str(reader)
|
|
cmdline += " -e \"verify_adm\""
|
|
cmdline += " -e \"echo creating restore script...\""
|
|
cmdline += " -e \"export > " + restore_script + " > /dev/null\""
|
|
cmdline += " --noprompt --csv " + script_dir + "/card_data.csv"
|
|
|
|
print("Executing: " + cmdline)
|
|
rc = os.system(cmdline)
|
|
if rc != 0:
|
|
print("Backup failed!")
|
|
return
|
|
|
|
print("Backup done!")
|
|
|
|
def restore(reader:int, atr:str, iccid:str):
|
|
""" Restore the card contents from backup """
|
|
|
|
script_dir = os.path.abspath(os.path.dirname(__file__))
|
|
|
|
for file in os.listdir(script_dir):
|
|
if "card_backup" in file:
|
|
file_atr = os.path.basename(file).split('.')[0].split('_')[2]
|
|
file_iccid = os.path.basename(file).split('.')[0].split('_')[3]
|
|
|
|
if file_atr == atr and file_iccid == iccid:
|
|
print("Found file: %s" % file)
|
|
|
|
cmdline = os.getcwd() + "/pySim-shell.py"
|
|
cmdline += " -p " + str(reader)
|
|
cmdline += " -e \"verify_adm\""
|
|
cmdline += " -e \"echo running restore script...\""
|
|
cmdline += " -e \"run_script " + script_dir + "/" + file + " > /dev/null\""
|
|
cmdline += " --noprompt --csv " + script_dir + "/card_data.csv"
|
|
|
|
print("Executing: " + cmdline)
|
|
rc = os.system(cmdline)
|
|
if rc != 0:
|
|
print("Restore failed!")
|
|
return
|
|
|
|
print("Restore done!")
|
|
return
|
|
|
|
print("Restore failed, no backup file found for this card!")
|
|
|
|
def iterate_cards(action):
|
|
""" iterate over all cards, read the ATR and the ICCID of each card and perform an action """
|
|
reader_list=readers()
|
|
|
|
for i in range(len(reader_list)):
|
|
print("\n" + boxed_heading_str("reader: %u" % i))
|
|
|
|
# Connect to card reader
|
|
try:
|
|
reader_connection = reader_list[i].createConnection()
|
|
reader_connection.connect(mode = SCARD_SHARE_EXCLUSIVE)
|
|
except:
|
|
print("unresponsive card, skipping...")
|
|
continue
|
|
|
|
# Get ATR and ICCID
|
|
atr = i2h(reader_connection.getATR())
|
|
reader_connection.disconnect()
|
|
reader_connection.connect(mode = SCARD_SHARE_EXCLUSIVE)
|
|
response, sw1, sw2 = reader_connection.transmit(h2i("a0a40000022fe2"))
|
|
if sw1 != 0x9f:
|
|
print("Unable to select EF.ICCID on card %s (sw1=%02x, sw2=%02x), skipping..." % (atr, sw1, sw2))
|
|
continue
|
|
response, sw1, sw2 = reader_connection.transmit(h2i("a0b000000a"))
|
|
if [sw1, sw2] != [0x90, 0x00]:
|
|
print("Unable to read EF.ICCID from card %s (sw1=%02x, sw2=%02x), skipping..." % (atr, sw1, sw2))
|
|
continue
|
|
iccid = dec_iccid(i2h(response))
|
|
print("Found ATR: %s, ICCID: %s" % (atr, iccid))
|
|
reader_connection.disconnect()
|
|
|
|
action(i, atr, iccid)
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser(description='Tool to automatically restore (sanitize) card contents from backup files.')
|
|
parser.add_argument("-b", "--backup", dest="backup", action='store_true', help="(re)create backup files",
|
|
default=False)
|
|
opts = parser.parse_args()
|
|
if opts.backup:
|
|
iterate_cards(backup)
|
|
else:
|
|
iterate_cards(restore)
|