laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/40914?usp=email )
Change subject: Identify cards by Historical bytes of ATR ...................................................................... Identify cards by Historical bytes of ATR - try to identify the CardModel by just comparing the Historical Bytes if matching by Whole ATR failed - add decompose ATR code from pyscard-contrib Related: OS#6837 Change-Id: Id7555e42290d232a0e0efc47e7d97575007d846f --- M pySim/filesystem.py M pySim/utils.py 2 files changed, 137 insertions(+), 1 deletion(-) Approvals: laforge: Looks good to me, approved Jenkins Builder: Verified diff --git a/pySim/filesystem.py b/pySim/filesystem.py index 6712755..1d71e08 100644 --- a/pySim/filesystem.py +++ b/pySim/filesystem.py @@ -39,7 +39,7 @@ from osmocom.tlv import bertlv_parse_one from osmocom.construct import filter_dict, parse_construct, build_construct -from pySim.utils import sw_match +from pySim.utils import sw_match, decomposeATR from pySim.jsonpath import js_path_modify from pySim.commands import SimCardCommands from pySim.exceptions import SwMatchError @@ -1545,6 +1545,13 @@ if atr == card_atr: print("Detected CardModel:", cls.__name__) return True + # if nothing found try to just compare the Historical Bytes of the ATR + card_atr_hb = decomposeATR(card_atr)['hb'] + for atr in cls._atrs: + atr_hb = decomposeATR(atr)['hb'] + if atr_hb == card_atr_hb: + print("Detected CardModel:", cls.__name__) + return True return False @staticmethod diff --git a/pySim/utils.py b/pySim/utils.py index 48a9998..b0443c8 100644 --- a/pySim/utils.py +++ b/pySim/utils.py @@ -15,6 +15,7 @@ # Copyright (C) 2009-2010 Sylvain Munaut <t...@246tnt.com> # Copyright (C) 2021 Harald Welte <lafo...@osmocom.org> +# Copyright (C) 2009-2022 Ludovic Rousseau # # 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 @@ -585,6 +586,134 @@ raise ValueError('invalid APDU (%s), too short!' % b2h(apdu)) +# ATR handling code under GPL from parseATR: https://github.com/LudovicRousseau/pyscard-contrib +def normalizeATR(atr): + """Transform an ATR in list of integers. + valid input formats are + "3B A7 00 40 18 80 65 A2 08 01 01 52" + "3B:A7:00:40:18:80:65:A2:08:01:01:52" + + Args: + atr: string + Returns: + list of bytes + + >>> normalize("3B:A7:00:40:18:80:65:A2:08:01:01:52") + [59, 167, 0, 64, 24, 128, 101, 162, 8, 1, 1, 82] + """ + atr = atr.replace(":", "") + atr = atr.replace(" ", "") + + res = [] + while len(atr) >= 2: + byte, atr = atr[:2], atr[2:] + res.append(byte) + if len(atr) > 0: + raise ValueError("warning: odd string, remainder: %r" % atr) + + atr = [int(x, 16) for x in res] + return atr + + +# ATR handling code under GPL from parseATR: https://github.com/LudovicRousseau/pyscard-contrib +def decomposeATR(atr_txt): + """Decompose the ATR in elementary fields + + Args: + atr_txt: ATR as a hex bytes string + Returns: + dictionary of field and values + + >>> decomposeATR("3B A7 00 40 18 80 65 A2 08 01 01 52") + { 'T0': {'value': 167}, + 'TB': {1: {'value': 0}}, + 'TC': {2: {'value': 24}}, + 'TD': {1: {'value': 64}}, + 'TS': {'value': 59}, + 'atr': [59, 167, 0, 64, 24, 128, 101, 162, 8, 1, 1, 82], + 'hb': {'value': [128, 101, 162, 8, 1, 1, 82]}, + 'hbn': 7} + """ + ATR_PROTOCOL_TYPE_T0 = 0 + atr_txt = normalizeATR(atr_txt) + atr = {} + + # the ATR itself as a list of integers + atr["atr"] = atr_txt + + # store TS and T0 + atr["TS"] = {"value": atr_txt[0]} + TDi = atr_txt[1] + atr["T0"] = {"value": TDi} + hb_length = TDi & 15 + pointer = 1 + # protocol number + pn = 1 + + # store number of historical bytes + atr["hbn"] = TDi & 0xF + + while pointer < len(atr_txt): + # Check TAi is present + if (TDi | 0xEF) == 0xFF: + pointer += 1 + if "TA" not in atr: + atr["TA"] = {} + atr["TA"][pn] = {"value": atr_txt[pointer]} + + # Check TBi is present + if (TDi | 0xDF) == 0xFF: + pointer += 1 + if "TB" not in atr: + atr["TB"] = {} + atr["TB"][pn] = {"value": atr_txt[pointer]} + + # Check TCi is present + if (TDi | 0xBF) == 0xFF: + pointer += 1 + if "TC" not in atr: + atr["TC"] = {} + atr["TC"][pn] = {"value": atr_txt[pointer]} + + # Check TDi is present + if (TDi | 0x7F) == 0xFF: + pointer += 1 + if "TD" not in atr: + atr["TD"] = {} + TDi = atr_txt[pointer] + atr["TD"][pn] = {"value": TDi} + if (TDi & 0x0F) != ATR_PROTOCOL_TYPE_T0: + atr["TCK"] = True + pn += 1 + else: + break + + # Store historical bytes + atr["hb"] = {"value": atr_txt[pointer + 1 : pointer + 1 + hb_length]} + + # Store TCK + last = pointer + 1 + hb_length + if "TCK" in atr: + try: + atr["TCK"] = {"value": atr_txt[last]} + except IndexError: + atr["TCK"] = {"value": -1} + last += 1 + + if len(atr_txt) > last: + atr["extra"] = atr_txt[last:] + + if len(atr["hb"]["value"]) < hb_length: + missing = hb_length - len(atr["hb"]["value"]) + if missing > 1: + (t1, t2) = ("s", "are") + else: + (t1, t2) = ("", "is") + atr["warning"] = "ATR is truncated: %d byte%s %s missing" % (missing, t1, t2) + + return atr + + class DataObject(abc.ABC): """A DataObject (DO) in the sense of ISO 7816-4. Contrary to 'normal' TLVs where one simply has any number of different TLVs that may occur in any order at any point, ISO 7816 -- To view, visit https://gerrit.osmocom.org/c/pysim/+/40914?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: merged Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: Id7555e42290d232a0e0efc47e7d97575007d846f Gerrit-Change-Number: 40914 Gerrit-PatchSet: 3 Gerrit-Owner: bjoern <bjoern...@nixda.biz> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <lafo...@osmocom.org> Gerrit-CC: dexter <pma...@sysmocom.de> Gerrit-CC: fixeria <vyanits...@sysmocom.de> Gerrit-CC: pespin <pes...@sysmocom.de>