[tools] improve smartcard traffic interpreter
This patch adds interpretations for more commands and adds a new application.
This commit is contained in:
parent
a2a0cc0792
commit
7bf8669e6d
@ -69,6 +69,7 @@ AIDs = {
|
|||||||
"a00000039742544659": "MsGidsAID",
|
"a00000039742544659": "MsGidsAID",
|
||||||
"a000000308": "PIV",
|
"a000000308": "PIV",
|
||||||
"a0000003974349445f0100": "SC PNP",
|
"a0000003974349445f0100": "SC PNP",
|
||||||
|
"a0000001510000": "GPC",
|
||||||
}
|
}
|
||||||
|
|
||||||
FIDs = {
|
FIDs = {
|
||||||
@ -89,7 +90,9 @@ DOs = {
|
|||||||
|
|
||||||
ERROR_CODES = {
|
ERROR_CODES = {
|
||||||
0x9000: "success",
|
0x9000: "success",
|
||||||
|
0x6100: "more bytes",
|
||||||
0x6282: "end of file or record",
|
0x6282: "end of file or record",
|
||||||
|
0x6283: "card locked",
|
||||||
0x63C0: "warning counter 0",
|
0x63C0: "warning counter 0",
|
||||||
0x63C1: "warning counter 1",
|
0x63C1: "warning counter 1",
|
||||||
0x63C2: "warning counter 2",
|
0x63C2: "warning counter 2",
|
||||||
@ -101,6 +104,7 @@ ERROR_CODES = {
|
|||||||
0x63C8: "warning counter 8",
|
0x63C8: "warning counter 8",
|
||||||
0x63C9: "warning counter 9",
|
0x63C9: "warning counter 9",
|
||||||
0x6982: "security status not satisfied",
|
0x6982: "security status not satisfied",
|
||||||
|
0x6882: "Secure messaging not supported",
|
||||||
0x6985: "condition of use not satisfied",
|
0x6985: "condition of use not satisfied",
|
||||||
0x6A80: "incorrect parameter cmd data field",
|
0x6A80: "incorrect parameter cmd data field",
|
||||||
0x6A81: "function not suppported",
|
0x6A81: "function not suppported",
|
||||||
@ -126,6 +130,8 @@ PIV_OIDs = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ApplicationDummy(object):
|
class ApplicationDummy(object):
|
||||||
|
"""Base application"""
|
||||||
|
|
||||||
def __init__(self, aid):
|
def __init__(self, aid):
|
||||||
self.aid = aid
|
self.aid = aid
|
||||||
|
|
||||||
@ -133,13 +139,13 @@ class ApplicationDummy(object):
|
|||||||
return self.aid
|
return self.aid
|
||||||
|
|
||||||
def selectResult(self, fci, status, body):
|
def selectResult(self, fci, status, body):
|
||||||
return 'selectResult(%s, %s, %s)\n' %(fci, status, body.hex())
|
return 'selectResult(fci=%s, status=0x%x) = %s\n' %(fci, status, body.hex())
|
||||||
|
|
||||||
def getData(self, fileId, bytes):
|
def getData(self, fileId, bytes):
|
||||||
return 'getData(0x%x, %s)\n' %(fileId, bytes.hex())
|
return 'getData(status=0x%x) = %s\n' %(fileId, bytes.hex())
|
||||||
|
|
||||||
def getDataResult(self, status, body):
|
def getDataResult(self, status, body):
|
||||||
return 'getDataResult(0x%x, %s)\n' %(status, body.hex())
|
return 'getDataResult(status=0x%x) = %s\n' %(status, body.hex())
|
||||||
|
|
||||||
def mse(self, body):
|
def mse(self, body):
|
||||||
return body.hex()
|
return body.hex()
|
||||||
@ -153,8 +159,72 @@ class ApplicationDummy(object):
|
|||||||
def psoResult(self, status, body):
|
def psoResult(self, status, body):
|
||||||
return body.hex()
|
return body.hex()
|
||||||
|
|
||||||
|
def getResponse(self, status, body):
|
||||||
|
return body.hex()
|
||||||
|
|
||||||
|
def getResponseResult(self, status, body):
|
||||||
|
return body.hex()
|
||||||
|
|
||||||
|
def verify(self, status, body):
|
||||||
|
return "verify(%s)" % body.hex()
|
||||||
|
|
||||||
|
def verifyResult(self, status, body):
|
||||||
|
return "verify(%s)" % body.hex()
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
class ApplicationGpc(object):
|
||||||
|
"""GlobalPlatform application"""
|
||||||
|
|
||||||
|
def __init__(self, aid):
|
||||||
|
self.aid = aid
|
||||||
|
self.lastGetData = None
|
||||||
|
|
||||||
|
def getAID(self):
|
||||||
|
return self.aid
|
||||||
|
|
||||||
|
def selectResult(self, fci, status, body):
|
||||||
|
return 'selectResult(fci=%s, status=0x%x) = %s\n' %(fci, status, body.hex())
|
||||||
|
|
||||||
|
def getData(self, fileId, bytes):
|
||||||
|
tags = {
|
||||||
|
0x42: 'Issuer Identification Number',
|
||||||
|
0x45: 'Card Image Number',
|
||||||
|
0x66: 'Card Data',
|
||||||
|
0x67: 'Card Capability Information' # ???
|
||||||
|
}
|
||||||
|
self.lastGetData = fileId
|
||||||
|
return 'getData(%s)\n' % tags.get(fileId, '<unknown 0x%x>' % fileId)
|
||||||
|
|
||||||
|
def getDataResult(self, status, body):
|
||||||
|
if self.lastGetData == 0x66:
|
||||||
|
# Card Data
|
||||||
|
pass
|
||||||
|
return 'getDataResult(0x%x) = %s\n' %(status, body.hex())
|
||||||
|
|
||||||
|
def mse(self, body):
|
||||||
|
return body.hex()
|
||||||
|
|
||||||
|
def mseResult(self, status, body):
|
||||||
|
return body.hex()
|
||||||
|
|
||||||
|
def pso(self, body):
|
||||||
|
return body.hex()
|
||||||
|
|
||||||
|
def psoResult(self, status, body):
|
||||||
|
return body.hex()
|
||||||
|
|
||||||
|
def getResponse(self, status, body):
|
||||||
|
return body.hex()
|
||||||
|
|
||||||
|
def getResponseResult(self, status, body):
|
||||||
|
return body.hex()
|
||||||
|
|
||||||
|
|
||||||
class ApplicationPIV(object):
|
class ApplicationPIV(object):
|
||||||
|
"""PIV application"""
|
||||||
|
|
||||||
def __init__(self, aid):
|
def __init__(self, aid):
|
||||||
self.lastGet = None
|
self.lastGet = None
|
||||||
self.aid = aid
|
self.aid = aid
|
||||||
@ -171,6 +241,8 @@ class ApplicationPIV(object):
|
|||||||
while len(body) > 2:
|
while len(body) > 2:
|
||||||
tag = body[0]
|
tag = body[0]
|
||||||
tagLen = body[1]
|
tagLen = body[1]
|
||||||
|
if tagLen != 1:
|
||||||
|
pass
|
||||||
if selectT == "FCI":
|
if selectT == "FCI":
|
||||||
if tag == 0x4f:
|
if tag == 0x4f:
|
||||||
ret += "\tpiv version: %s\n" % body[2:2 + tagLen].hex()
|
ret += "\tpiv version: %s\n" % body[2:2 + tagLen].hex()
|
||||||
@ -188,9 +260,9 @@ class ApplicationPIV(object):
|
|||||||
ret += '\tCoexistent tag allocation authority: %s\n' % content
|
ret += '\tCoexistent tag allocation authority: %s\n' % content
|
||||||
|
|
||||||
elif tag == 0x50:
|
elif tag == 0x50:
|
||||||
ret += '\tapplication label\n'
|
ret += '\tapplication label: %s\n' % body[2:2+tagLen].decode('utf8')
|
||||||
elif tag == 0xac:
|
elif tag == 0xac:
|
||||||
ret += '\tCryptographic algorithms supported\n'
|
ret += '\tCryptographic algorithms supported: %s\n' % body[2:2+tagLen].hex()
|
||||||
else:
|
else:
|
||||||
rety += '\tunknown tag 0x%x\n' % tag
|
rety += '\tunknown tag 0x%x\n' % tag
|
||||||
|
|
||||||
@ -202,7 +274,10 @@ class ApplicationPIV(object):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def getData(self, fileId, bytes):
|
def getData(self, fileId, bytes):
|
||||||
ret = "\tfileId=%s\n" % FIDs.get(fileId, "%0.4x" % fileId)
|
ret = "\tfileId=%s(%0.4x)\n" % (FIDs.get(fileId, ""), fileId)
|
||||||
|
|
||||||
|
if len(bytes) < 7:
|
||||||
|
return ret + "\t/!\\ too short !!!!"
|
||||||
|
|
||||||
lc = bytes[4]
|
lc = bytes[4]
|
||||||
tag = bytes[5]
|
tag = bytes[5]
|
||||||
@ -224,13 +299,17 @@ class ApplicationPIV(object):
|
|||||||
tag = bytes[i]
|
tag = bytes[i]
|
||||||
tagLen = bytes[i+1]
|
tagLen = bytes[i+1]
|
||||||
keyStr += "value(tag=0x%x len=%d)"
|
keyStr += "value(tag=0x%x len=%d)"
|
||||||
|
elif lc == 3:
|
||||||
|
ret += "\tDiscovery Object\n"
|
||||||
|
elif lc == 4:
|
||||||
|
ret += "\tBiometric Information Templates (BIT) Group Template\n"
|
||||||
elif lc == 5:
|
elif lc == 5:
|
||||||
if tag == 0x5C:
|
if tag == 0x5C:
|
||||||
tagStr = bytes[7:].hex()
|
tagStr = bytes[7:10].hex()
|
||||||
ret += '\ttag: %s(%s)\n' % (tagStr, PIV_OIDs.get(tagStr, '<unknown>'))
|
ret += '\ttag: %s(%s)\n' % (tagStr, PIV_OIDs.get(tagStr, '<unknown>'))
|
||||||
self.lastGet = tagStr
|
self.lastGet = tagStr
|
||||||
else:
|
else:
|
||||||
ret += "\tunknown key access\n"
|
ret += "\tunknown key access(lc=0x%x)\n" % lc
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@ -259,13 +338,50 @@ class ApplicationPIV(object):
|
|||||||
ret += '\tIssuer Asymmetric Signature: %s\n' % tagBody.hex()
|
ret += '\tIssuer Asymmetric Signature: %s\n' % tagBody.hex()
|
||||||
else:
|
else:
|
||||||
ret += "\tunknown tag=0x%x len=%d content=%s\n" % (tag, tagLen, tagBody.hex())
|
ret += "\tunknown tag=0x%x len=%d content=%s\n" % (tag, tagLen, tagBody.hex())
|
||||||
|
|
||||||
|
elif self.lastGet in ('5fc107',):
|
||||||
|
# Card Capability Container
|
||||||
|
capas = {
|
||||||
|
0xf0: "Card Identifier",
|
||||||
|
0xf1: "Capability Container version number",
|
||||||
|
0xf2: "Capability Grammar version number",
|
||||||
|
0xf3: "Applications CardURL",
|
||||||
|
0xf4: "PKCS#15",
|
||||||
|
0xf5: "Registered Data Model number",
|
||||||
|
0xf6: "Access Control Rule Table",
|
||||||
|
0xf7: "Card APDUs",
|
||||||
|
0xfa: "Redirection Tag",
|
||||||
|
0xfb: "Capability Tuples (CTs)",
|
||||||
|
0xfc: "Status Tuples (STs)",
|
||||||
|
0xfd: "Next CCC",
|
||||||
|
0xe3: "Extended Application CardURL",
|
||||||
|
0xb4: "Security Object Buffer",
|
||||||
|
0xfe: "Error Detection Code"
|
||||||
|
}
|
||||||
|
|
||||||
|
if tag in capas.keys():
|
||||||
|
if tagLen:
|
||||||
|
ret += "\t%s: len=%d %s\n" % (capas[tag], tagLen, tagBody.hex())
|
||||||
else:
|
else:
|
||||||
ret += "\t%s: unknown tag=0x%x len=%d content=%s\n" % (self.lastGet, tag, tagLen, tagBody.hex())
|
ret += "\tunknown capa tag 0x%x: %s\n" % (tag, tagBody.hex())
|
||||||
|
|
||||||
|
elif self.lastGet == '5fc105':
|
||||||
|
# X.509 Certificate for PIV Authentication
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
ret += "\t%s: unimplemented tag=0x%x len=%d content=%s\n" % (self.lastGet, tag, tagLen, tagBody.hex())
|
||||||
|
|
||||||
body = body[2+tagLen:]
|
body = body[2+tagLen:]
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def getResponse(self, status, body):
|
||||||
|
return body.hex()
|
||||||
|
|
||||||
|
def getResponseResult(self, status, body):
|
||||||
|
return body.hex()
|
||||||
|
|
||||||
def mse(self, body):
|
def mse(self, body):
|
||||||
return body.hex()
|
return body.hex()
|
||||||
|
|
||||||
@ -278,9 +394,18 @@ class ApplicationPIV(object):
|
|||||||
def psoResult(self, status, body):
|
def psoResult(self, status, body):
|
||||||
return body.hex()
|
return body.hex()
|
||||||
|
|
||||||
|
def verify(self, status, body):
|
||||||
|
return "verify(%s)" % body.hex()
|
||||||
|
|
||||||
|
def verifyResult(self, status, body):
|
||||||
|
return "verify(%s)" % body.hex()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ApplicationGids(object):
|
class ApplicationGids(object):
|
||||||
|
"""GIDS application"""
|
||||||
|
|
||||||
def __init__(self, aid):
|
def __init__(self, aid):
|
||||||
self.aid = aid
|
self.aid = aid
|
||||||
self.lastDo = None
|
self.lastDo = None
|
||||||
@ -376,7 +501,7 @@ class ApplicationGids(object):
|
|||||||
elif selectT == 'FCI':
|
elif selectT == 'FCI':
|
||||||
return self.parseFci(body)
|
return self.parseFci(body)
|
||||||
else:
|
else:
|
||||||
return '\tselectResult(%s, %s, %s)\n' % (selectT, status, body.hex())
|
return '\tselectResult(fci=%s, status=0x%x) = %s\n' % (selectT, status, body.hex())
|
||||||
|
|
||||||
def getData(self, fileId, bytes):
|
def getData(self, fileId, bytes):
|
||||||
lc = bytes[4]
|
lc = bytes[4]
|
||||||
@ -411,6 +536,12 @@ class ApplicationGids(object):
|
|||||||
def mseResult(self, status, body):
|
def mseResult(self, status, body):
|
||||||
return body.hex()
|
return body.hex()
|
||||||
|
|
||||||
|
def getResponse(self, status, body):
|
||||||
|
return body.hex()
|
||||||
|
|
||||||
|
def getResponseResult(self, status, body):
|
||||||
|
return body.hex()
|
||||||
|
|
||||||
def pso(self, body):
|
def pso(self, body):
|
||||||
return body.hex()
|
return body.hex()
|
||||||
|
|
||||||
@ -420,15 +551,24 @@ class ApplicationGids(object):
|
|||||||
|
|
||||||
|
|
||||||
def createAppByAid(aid):
|
def createAppByAid(aid):
|
||||||
if aid == "a000000308":
|
if aid in ("a000000308", 'a00000030800001000',):
|
||||||
return ApplicationPIV(aid)
|
return ApplicationPIV(aid)
|
||||||
|
|
||||||
elif aid in ('a00000039742544659',):
|
elif aid in ('a00000039742544659',):
|
||||||
return ApplicationGids(aid)
|
return ApplicationGids(aid)
|
||||||
|
|
||||||
|
elif aid in ('a0000001510000',):
|
||||||
|
return ApplicationGpc(aid)
|
||||||
|
|
||||||
return ApplicationDummy(aid)
|
return ApplicationDummy(aid)
|
||||||
|
|
||||||
|
|
||||||
|
def getErrorCode(status):
|
||||||
|
if status & 0x6100:
|
||||||
|
return "%d more bytes" % (status & 0xff)
|
||||||
|
|
||||||
|
return ERROR_CODES.get(status, "<unknown>")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if len(sys.argv) > 1:
|
if len(sys.argv) > 1:
|
||||||
fin = open(sys.argv[1], "r")
|
fin = open(sys.argv[1], "r")
|
||||||
@ -497,7 +637,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
elif p1 == 0x4:
|
elif p1 == 0x4:
|
||||||
aid = bytes[i:i+lc].hex()
|
aid = bytes[i:i+lc].hex()
|
||||||
lastSelect = AIDs.get(aid, '')
|
lastSelect = AIDs.get(aid, '<unknown %s>' % aid)
|
||||||
print("\tselectByAID: %s(%s)" % (aid, lastSelect))
|
print("\tselectByAID: %s(%s)" % (aid, lastSelect))
|
||||||
|
|
||||||
if p2 == 0x00:
|
if p2 == 0x00:
|
||||||
@ -537,6 +677,13 @@ if __name__ == '__main__':
|
|||||||
ret = currentApp.getData(fileId, bytes)
|
ret = currentApp.getData(fileId, bytes)
|
||||||
print("%s" % ret)
|
print("%s" % ret)
|
||||||
|
|
||||||
|
elif cmdName == "GET RESPONSE":
|
||||||
|
#lc = bytes[4]
|
||||||
|
#fileId = p1 * 256 + p2
|
||||||
|
|
||||||
|
#ret = currentApp.getResponse(fileId, bytes)
|
||||||
|
#print("%s" % ret)
|
||||||
|
pass
|
||||||
elif cmdName == "MSE":
|
elif cmdName == "MSE":
|
||||||
ret = currentApp.mse(bytes[5:5+lc])
|
ret = currentApp.mse(bytes[5:5+lc])
|
||||||
print("%s" % ret)
|
print("%s" % ret)
|
||||||
@ -551,9 +698,12 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
# Responses
|
# Responses
|
||||||
|
if not len(bytes):
|
||||||
|
continue
|
||||||
|
|
||||||
status = bytes[-1] + bytes[-2] * 256
|
status = bytes[-1] + bytes[-2] * 256
|
||||||
body = bytes[0:-2]
|
body = bytes[0:-2]
|
||||||
print("status=0x%0.4x(%s)" % (status, ERROR_CODES.get(status, "<unknown>")))
|
print("status=0x%0.4x(%s)" % (status, getErrorCode(status)))
|
||||||
|
|
||||||
if not len(body):
|
if not len(body):
|
||||||
continue
|
continue
|
||||||
@ -567,6 +717,10 @@ if __name__ == '__main__':
|
|||||||
ret = currentApp.mseResult(status, body)
|
ret = currentApp.mseResult(status, body)
|
||||||
elif lastCmd == "PSO":
|
elif lastCmd == "PSO":
|
||||||
ret = currentApp.psoResult(status, body)
|
ret = currentApp.psoResult(status, body)
|
||||||
|
elif lastCmd == "GET RESPONSE":
|
||||||
|
ret = currentApp.getResponseResult(status, body)
|
||||||
|
elif lastCmd == "VERIFY":
|
||||||
|
ret = currentApp.verifyResult(status, body)
|
||||||
|
|
||||||
if ret:
|
if ret:
|
||||||
print("%s" % ret)
|
print("%s" % ret)
|
Loading…
Reference in New Issue
Block a user