Skip to content

Instantly share code, notes, and snippets.

@dev-zzo
Last active May 25, 2024 21:30
Show Gist options
  • Save dev-zzo/5c21deca56b7e34e931e18250cf09c83 to your computer and use it in GitHub Desktop.
Save dev-zzo/5c21deca56b7e34e931e18250cf09c83 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import sys
import io
from pcsc import PcScTransport
def h2b(x):
return bytes.fromhex(x)
# cplc data; also try apdu D088000000?
# https://crocs.fi.muni.cz/_media/public/papers/jcalgtest_forensic_certification_profile_secrypt22_preprint.pdf
cplc_ic_fabricator = {
0x0003: 'Renesas',
0x0005: 'Infineon',
0x008C: 'Tongxin',
0x1290: 'Thales Dis France SAS',
0x2050: 'Philips',
0x3060: 'Renesas',
0x4070: 'NXP',
0x4090: 'Infineon',
0x4180: 'Atmel',
0x4250: 'Samsung',
0x4750: 'ST Microelectronics',
0x4790: 'NXP',
0x4830: 'Infineon',
0x8100: 'Infineon',
}
cplc_ic_type = {
# Infineon
0x0005: {},
0x4090: {
0x3401: 'SLC52GDA804',
0x3404: 'SLC52GDA700',
0x3405: 'SLC52GDA600',
0x00CE: 'SLE66CLX360PE / M1587/M1588', # https://www.commoncriteriaportal.org/files/epfiles/ANSSI-CC_2011-16fr.pdf
0x00CF: 'SLE66C(L)X800 / M1580/M1581', # https://www.commoncriteriaportal.org/files/epfiles/ANSSI-CC_2011-16fr.pdf
# SLE66CLX1600PEM M1590 A12
# SLE66CLX1600PE M1596 A12
# SLE66CLX1600PES M1597 A12
# SLE66CX1600PE* M1598 A12
# SLE66CLX1440PEM m2090 a13
0x00E9: 'SLE66C(L)X1440 / M2091/M2092/M2093', # https://www.commoncriteriaportal.org/files/epfiles/ANSSI-CC_2011-80fr.pdf
# SLE66CLX1280PEM M2094 A12
# SLE66CLX1280PE M2095 A12
# SLE66CLX1280PES M2096 A12
# SLE66CX1280PE* M2097 A12
0x7164: 'SLE78CLX1600P', # ??
0x7879: 'SLE78CLFX4007PHM / M7892 G12',
0x7897: 'SLE78CLFX400VPHM / M7892 G12',
0x78A0: 'SLE78CLX1440P',
0x78A4: 'SLE78CLX1600P',
0x78A7: 'SLE78CLX1280P',
0x78A9: 'SLE78CLX800P',
0x78AF: 'SLE78CLX480P',
0x78B1: 'SLE78CLX360P',
0x7901: 'SLE78CLFX400VPH',
0x79A2: 'SLE78CLX1440PM',
0x79A5: 'SLE78CLX1600PM',
0x79AB: 'SLE78CLX800PM',
0x79B0: 'SLE78CLX480PM',
0x79B2: 'SLE78CLX360PM',
# E80A: identical to 7229?
},
0x4830: {
0x7734: 'SLE77CLFX2400P / M7794',
0x7767: 'SLE77CLFX2407P / M7794?',
},
0x8100: {
0x160C: 'SLC52GDL448',
0x7352: 'SLE78CLFX5000PHM',
0x7353: 'SLE78CLFX5000PH',
0x7801: 'SLE78CLFX4000P',
0x7802: '? / M7892 B11',
0x7805: 'SLE78CLFX4000PM',
0x7813: 'SLE78CFX4000P',
0x7859: 'SLE78CAFX4000PM',
0x7877: 'SLE78CLFX4007PM',
0x7878: 'SLE78CLFX4007P',
0x7985: 'SLE78CLFX408AP',
0x7986: 'SLE78CLFX408APM',
},
# Atmel
0x4180: {
0x010E: 'AT90SC28880RCFV',
},
# Samsung
0x4250: {
0x1611: 'S3FT9MH',
},
# STMicro
0x4750: {
0x001A: 'ST33F1M / K8C0A',
0x0025: 'SC33F640',
0x0204: 'ST23YR80',
0x0205: 'ST23YR48',
},
# NXP
0x4070: {},
0x4790: {
0x0502: 'P60D080',
0x0503: 'J3H145',
0x5038: 'J2A080 80K',
0x5040: 'J3A080',
0x5074: 'J2E145G',
0x5075: 'J3E145',
0x5167: 'J2D081/J2E081/CJ2A081',
0xD321: 'J3R110/J3R180',
},
}
cplc_os_id = {
0x0027: 'STM027',
0x1291: 'Gemplus',
0x1981: 'unknown', # but seen itw
0x2391: 'unknown', # but seen itw
0x4A4B: 'JK',
0x8211: 'Athena OS755',
0x8231: 'unknown', # but seen itw
0xA201: 'unknown', # but seen itw
0xA202: 'unknown', # but seen itw
}
def cplc_parse(data):
if data.startswith(b"\x9f\x7f\x2a"):
data = data[3:]
return {
'ic_fabricator': int.from_bytes(data[0:2], 'big'),
'ic_type': int.from_bytes(data[2:4], 'big'),
'os_id': int.from_bytes(data[4:6], 'big'),
'os_release_date': int.from_bytes(data[6:8], 'big'),
'os_release_level': int.from_bytes(data[8:10], 'big'),
'ic_fab_date': int.from_bytes(data[10:12], 'big'),
'ic_serial_num': int.from_bytes(data[12:16], 'big'),
'ic_batch_id': int.from_bytes(data[16:18], 'big'),
'ic_module_fabricator': int.from_bytes(data[18:20], 'big'),
'ic_module_pkg_date': int.from_bytes(data[20:22], 'big'),
'icc_manufacturer': int.from_bytes(data[22:24], 'big'),
'ic_embedding_date': int.from_bytes(data[24:26], 'big'),
'ic_prepersonalizer': int.from_bytes(data[26:28], 'big'),
'ic_prepersonalization_equipment_date': int.from_bytes(data[28:30], 'big'),
'ic_prepersonalization_equipment_id': int.from_bytes(data[30:34], 'big'),
'ic_personalizer': int.from_bytes(data[34:36], 'big'),
'ic_personalization_date': int.from_bytes(data[36:38], 'big'),
'ic_personalization_equipment_id': int.from_bytes(data[38:42], 'big'),
}
def cplc_prettyprint(cplc):
print("IC fabricator ...........................: %04X (%s)" % (cplc['ic_fabricator'], cplc_ic_fabricator.get(cplc['ic_fabricator'], "unknown")))
ic_type = "unknown"
if cplc_ic_fabricator.get(cplc['ic_fabricator']) is not None:
ic_type = cplc_ic_type[cplc['ic_fabricator']].get(cplc['ic_type'], "unknown")
print("IC type .................................: %04X (%s)" % (cplc['ic_type'], ic_type))
print("OS identifier ...........................: %04X (%s)" % (cplc['os_id'], cplc_os_id.get(cplc['os_id'], "unknown")))
print("OS release date .........................: %04X" % cplc['os_release_date'])
print("OS release level ........................: %04X" % cplc['os_release_level'])
print("IC fabrication date .....................: %04X" % cplc['ic_fab_date'])
print("IC serial number ........................: %08X" % cplc['ic_serial_num'])
print("IC batch identifier .....................: %04X" % cplc['ic_batch_id'])
print("IC module fabricator ....................: %04X" % cplc['ic_module_fabricator'])
print("IC module packaging date ................: %04X" % cplc['ic_module_pkg_date'])
print("ICC manufacturer ........................: %04X" % cplc['icc_manufacturer'])
print("IC embedding date .......................: %04X" % cplc['ic_embedding_date'])
print("IC prepersonalizer ......................: %04X" % cplc['ic_prepersonalizer'])
print("IC prepersonalization equipment date ....: %04X" % cplc['ic_prepersonalization_equipment_date'])
print("IC prepersonalization equipment ID ......: %08X" % cplc['ic_prepersonalization_equipment_id'])
print("IC personalizer .........................: %04X" % cplc['ic_personalizer'])
print("IC personalization date .................: %04X" % cplc['ic_personalization_date'])
print("IC personalization equipment ID .........: %08X" % cplc['ic_personalization_equipment_id'])
print()
# card stuff
def send_apdu(sl, cla, ins, p1, p2, data, le):
apdu = bytearray([cla, ins, p1, p2])
if data:
lc = len(data) // 2 if data else 0
apdu = header + lc + data + le
apdu.append(lc)
apdu.extend(data)
apdu.append(le)
else:
apdu.append(le)
return sl.send_apdu(apdu)
def select_app(sl, aid):
(res, sw) = send_apdu(sl, 0x00, 0xA4, 0x04, 0x00, aid, 0)
return res, sw
def get_data_gp(sl, p1, p2):
(res, sw) = send_apdu(sl, 0x80, 0xCA, p1, p2, "", 0)
print(sw.hex(), res.hex())
if sw != h2b("6a88"):
return res
def get_data_iso(sl, p1, p2):
(res, sw) = send_apdu(sl, 0x00, 0xCA, p1, p2, "", 0)
print(sw.hex(), res.hex())
if sw != h2b("6a88"):
return res
def get_status(sl):
(res, sw) = send_apdu(sl, 0x80, 0xF2, 0x80, 0x00, "", 0)
print(sw.hex(), res.hex())
def get_funky_data(sl):
(res, sw) = send_apdu(sl, 0xD0, 0x88, 0x00, 0x00, "", 0)
print(sw.hex(), res.hex())
return res
def test_cplc_parsing():
"Test parsing using some observed data"
with open("cplcdata.txt", "r") as fp:
for line in fp:
if not line or line[0] == '#':
continue
line = line.strip()
cplc_prettyprint(cplc_parse(bytes.fromhex(line))),
sys.exit(0)
if __name__ == '__main__':
#test_cplc_parsing()
sl = PcScTransport()
sl.wait_for_card()
atr = sl.get_atr()
print("ATR: " + atr.hex() + " " + repr(atr))
aids_to_try = [
"",
"a000000003000000",
"a000000004000000",
"a0000001510000",
]
for aid in aids_to_try:
print("Trying AID %s ..." % (aid,))
try:
data, sw = select_app(sl, aid)
#print(sw.hex(), data.hex())
if sw == h2b("9000"):
break
except:
print("Resetting ...")
sl.reset_card()
data = get_data_gp(sl, 0x9f, 0x7f)
if data:
cplc_prettyprint(cplc_parse(data))
data = get_data_iso(sl, 0x9f, 0x7f)
if data:
cplc_prettyprint(cplc_parse(data))
#get_status(sl)
# I noted in one CC compliance report there was this funky PDU returning interesting data
get_funky_data(sl)
import sys
try:
from smartcard.CardConnection import CardConnection
from smartcard.CardRequest import CardRequest
from smartcard.Exceptions import NoCardException, CardRequestTimeoutException, CardConnectionException
from smartcard.System import readers
from smartcard.ExclusiveConnectCardConnection import ExclusiveConnectCardConnection
except ImportError:
print("Failed to import from pyscard; make sure you have it -- try pip install pyscard")
sys.exit(2)
# this is shamelessly stolen from pysim
class PcScTransport:
def __init__(self, reader_number=0):
r = readers()
self._reader = readers()[reader_number]
self._con = self._reader.createConnection()
self._con = ExclusiveConnectCardConnection(self._con)
def __del__(self):
try:
# FIXME: this causes multiple warnings in Python
self._con.disconnect()
except:
pass
def wait_for_card(self, timeout=None, newcardonly=False):
cr = CardRequest(readers=[self._reader], timeout=timeout, newcardonly=newcardonly)
cr.waitforcard()
self.connect()
def connect(self):
# try both T=0 and T=1
try:
self.disconnect()
# Explicitly select T=0 communication protocol
self._con.connect(CardConnection.T0_protocol)
except CardConnectionException as exc:
self.disconnect()
# Explicitly select T=1 communication protocol
self._con.connect(CardConnection.T1_protocol)
def get_atr(self):
return bytes(self._con.getATR())
def disconnect(self):
self._con.disconnect()
def reset_card(self):
self.disconnect()
self.connect()
def _send_apdu_raw(self, pdu: bytes):
data, sw1, sw2 = self._con.transmit(list(pdu))
return bytes(data), bytes([sw1, sw2])
def send_apdu_raw(self, pdu: bytes):
# todo: tracing/logging
return self._send_apdu_raw(pdu)
def send_apdu(self, apdu: bytes):
data, sw = self.send_apdu_raw(apdu)
while sw[0] in (0x61, 0x9f):
data_chunk, sw = self.send_apdu_raw(bytes([apdu[0], 0xc0, 0x00, 0x00, sw[1]]))
data += data_chunk
if sw[0] == 0x6c:
data, sw = self.send_apdu_raw(bytes([apdu[0], apdu[1], apdu[2], apdu[3], sw[1]]))
return data, sw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment