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 <tnt(a)246tNt.com>
# Copyright (C) 2021 Harald Welte <laforge(a)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.c3(a)nixda.biz>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-CC: dexter <pmaier(a)sysmocom.de>
Gerrit-CC: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-CC: pespin <pespin(a)sysmocom.de>