mirror of
https://github.com/RfidResearchGroup/ChameleonMini.git
synced 2026-05-12 11:20:37 -07:00
Decode Basic I-Block and MF DESFire Ev1 APP Data in SniffingMode
EV1: AuthAES, Select APP, GetAPPIDs, ReadData and decode I-Block (cherry picked from commit 5a14e1b)
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import binascii
|
||||
import crcmod
|
||||
from enum import Enum
|
||||
|
||||
from Chameleon.MFDESFire import MFDESFireDecode
|
||||
from Chameleon.utils import TrafficSource
|
||||
|
||||
# Parameters for CRC_A
|
||||
CRC_INIT = 0x6363
|
||||
POLY = 0x11021
|
||||
@@ -12,6 +16,76 @@ class ReaderCMD(Enum):
|
||||
RATS = 1
|
||||
PPS = 2
|
||||
|
||||
|
||||
|
||||
class BlockData:
|
||||
@staticmethod
|
||||
def isBlockData(byteCount, data):
|
||||
if(byteCount >= 3 and (data[0]& 0xE6) in ReaderTrafficTypes["PCB"]):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __init__(self, byteCount, data, source):
|
||||
self.byteCount = byteCount
|
||||
self.data = data
|
||||
self.PCB = data[0]
|
||||
self.type = ReaderTrafficTypes["PCB"][self.PCB & 0xE6]
|
||||
self.CID = None
|
||||
self.NAD = None
|
||||
self.INF = None
|
||||
self.source = source
|
||||
self.CRCChecked = CRC_A_check(data)
|
||||
self.CardApplicationDecoder = MFDESFireDecode
|
||||
|
||||
if self.CRCChecked:
|
||||
|
||||
hasCID = self.PCB & 0x08 # PCB b4 indicate CID
|
||||
hasNAD = 0
|
||||
|
||||
if (self.type == "IBlock"):
|
||||
hasNAD = self.PCB & 0x04 # IBlock PCB b2 indicate NAD
|
||||
|
||||
byteNext = 1
|
||||
# CID
|
||||
if (hasCID):
|
||||
self.CID = self.data[byteNext]
|
||||
byteNext += 1
|
||||
|
||||
# NAD
|
||||
if (hasNAD):
|
||||
self.NAD = self.data[byteNext]
|
||||
byteNext += 1
|
||||
|
||||
# INF field not empty
|
||||
if(byteNext < byteCount -2):
|
||||
self.INF = self.data[byteNext: byteCount-2]
|
||||
|
||||
|
||||
def decode(self):
|
||||
note = ""
|
||||
# Prologue
|
||||
# PCB
|
||||
note += self.type + " "
|
||||
|
||||
# CID
|
||||
if (self.CID != None):
|
||||
note += "CID:" + hex(self.CID) + " "
|
||||
|
||||
# NAD
|
||||
if (self.NAD != None):
|
||||
note += "NAD:" + hex(self.NAD) + " "
|
||||
|
||||
# INF
|
||||
if (self.INF != None):
|
||||
note += self.CardApplicationDecoder(self.INF, self.source)
|
||||
# EDC CRC check
|
||||
if not self.CRCChecked:
|
||||
note += " WRONG CRC "
|
||||
|
||||
|
||||
return note
|
||||
|
||||
readerCMD = ReaderCMD.NONE
|
||||
|
||||
ReaderTrafficTypes = {
|
||||
@@ -40,7 +114,16 @@ ReaderTrafficTypes = {
|
||||
0x7: "FSD:128 ",
|
||||
0x8: "FSD:256 "
|
||||
},
|
||||
|
||||
"PCB":{
|
||||
# IBlock 000X XX1X
|
||||
0x02: "IBlock", # 000X X01X
|
||||
0x08: "IBlock", # 000X X11X
|
||||
# RBlock 101X X01X
|
||||
0xA2: "RBlock", # 101X X01X
|
||||
# SBlock 11XX X010
|
||||
0xC2: "SBlock", # 110X X010
|
||||
0xE2: "SBlock" # 111X X010
|
||||
}
|
||||
}
|
||||
|
||||
CardTrafficTypes = {
|
||||
@@ -119,7 +202,7 @@ def parseReader_4(data):
|
||||
(data[1] & 0xf0 >> 8) in ReaderTrafficTypes["FSDI"])):
|
||||
note += "RATS - "
|
||||
note += ReaderTrafficTypes["FSDI"][data[1] >> 8]
|
||||
note += "CID:" + str(data[1] & 0x0f) + " "
|
||||
note += "CID:" + hex(data[1] & 0x0f) + " "
|
||||
if (not CRC_A_check(data)):
|
||||
note += " WRONG CRC "
|
||||
else:
|
||||
@@ -137,14 +220,12 @@ def parseReader_4(data):
|
||||
note += "CID:" + str(data[1] & 0x0f) + " "
|
||||
note += "DSI:" + str(pow(2, (data[2] >> 2) & 0x03)) + " "
|
||||
note += "DRI:" + str(pow(2, data[2] & 0x03)) + " "
|
||||
# BLOCK S-block PCB DESELECT
|
||||
# Without CID
|
||||
elif (byteCount == 3 and data[0] == 0xc2):
|
||||
note += "DESEL"
|
||||
# With CID
|
||||
elif (byteCount == 4 and data[0] == 0xca and data[1] & 0x30 == 0x00):
|
||||
note += "DESEL - "
|
||||
note += "CID:" + str(data[1] & 0x0f) + " "
|
||||
|
||||
# Half-duplex block transmission
|
||||
# PCB bit mask: 0b11100110
|
||||
elif (BlockData.isBlockData(byteCount, data)):
|
||||
blockData = BlockData(byteCount,data, TrafficSource.Reader)
|
||||
note = blockData.decode()
|
||||
|
||||
return note
|
||||
|
||||
@@ -209,9 +290,12 @@ def parseCard_4(data):
|
||||
# Check CRC_A
|
||||
if not CRC_A_check(data):
|
||||
note += " WRONG CRC "
|
||||
# Application Data
|
||||
elif (BlockData.isBlockData(byteCount, data)):
|
||||
blockData = BlockData(byteCount,data, TrafficSource.Card)
|
||||
note = blockData.decode()
|
||||
|
||||
|
||||
elif ():
|
||||
pass
|
||||
return note
|
||||
|
||||
def parseReader(data):
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
from Chameleon.utils import TrafficSource
|
||||
from binascii import hexlify
|
||||
|
||||
lastCMD = 0x00
|
||||
|
||||
StatusCode = {
|
||||
0x00 : "OPERATION_OK",
|
||||
0x0C : "NO_CHANGES",
|
||||
0x0E : "ERR_OUT_OF_EEPROM",
|
||||
0x1C : "ILLEGAL_CMD_CODE",
|
||||
0x1E : "ERR_INTEGRITY",
|
||||
0x40 : "NO_SUCH_KEY",
|
||||
0x7E : "ERR_LENGTH",
|
||||
0x9D : "PERMISSION_DENIED",
|
||||
0x9E : "ERR_PARAMETER",
|
||||
0xA0 : "APP_NOT_FOUND",
|
||||
0xA1 : "ERR_APP_INTEGRITY",
|
||||
0xAE : "ERR_AUTH",
|
||||
0xAF : "ADDITIONAL_FRAME",
|
||||
0xBE : "ERR_BOUNDARY",
|
||||
0xC1 : "ERR_PICC_INTEGRITY",
|
||||
0xCA : "CMD_ABORTED",
|
||||
0xCD : "ERR_PICC_DISABLED",
|
||||
0xCE : "ERR_COUNT",
|
||||
0xDE : "ERR_DUPLICATE",
|
||||
0xEE : "ERR_EEPROM",
|
||||
0xF0 : "FILE_NOT_FOUND",
|
||||
0xF1 : "ERR_FILE_INTEGRITY"
|
||||
}
|
||||
|
||||
|
||||
def decodeSelectAPP(data):
|
||||
if len(data) == 4:
|
||||
return "AID: 0x"+ hexlify(data[1:4]).decode()
|
||||
else:
|
||||
return "Decode Fail"
|
||||
|
||||
def decodeGetAPPID(data):
|
||||
if len(data) == 1:
|
||||
return ""
|
||||
else:
|
||||
return "Decode Fail"
|
||||
|
||||
def decodeRespGetAPPID (data):
|
||||
note = "APPIDs: |"
|
||||
dataLen = len(data)
|
||||
|
||||
for i in range (0,int((dataLen-1)/3)):
|
||||
note += "0x"+hexlify(data[3*i: 3*(i+1)]).decode()+"|"
|
||||
|
||||
return note
|
||||
|
||||
|
||||
def decodeAuthAES(data):
|
||||
if len(data) == 2:
|
||||
return "KeyNo:"+hex(data[1])
|
||||
else:
|
||||
return "Decode Fail"
|
||||
|
||||
def decodeRespAuthAES(data):
|
||||
return ""
|
||||
|
||||
|
||||
def decodeReadData(data):
|
||||
if len(data) == 8:
|
||||
fileNo = data[1]
|
||||
offSet = data[2:5]
|
||||
length = data[5:8]
|
||||
return "FileNo:"+hex(fileNo) + " OffSet:0x"+hexlify(offSet).decode() + " len:0x"+hexlify(length).decode()
|
||||
else:
|
||||
return "Decode Fail"
|
||||
|
||||
def decodeRespReadData(data):
|
||||
return "Data:0x"+hexlify(data[1:]).decode()
|
||||
|
||||
def decodeAdiFrame(data):
|
||||
return ""
|
||||
|
||||
def decodeRespAdiFrame(data):
|
||||
return ""
|
||||
|
||||
def decodeDummy(data):
|
||||
return ""
|
||||
|
||||
MFDESFireCMDTypes = {
|
||||
0x5A : {"name": "SelectApp ", "CMDdecoder":decodeSelectAPP, "RespDecoder": decodeDummy},
|
||||
0x6A : {"name": "GetAPPID ", "CMDdecoder":decodeGetAPPID, "RespDecoder": decodeRespGetAPPID},
|
||||
0xAA : {"name": "AuthAES ", "CMDdecoder":decodeAuthAES, "RespDecoder": decodeRespAuthAES},
|
||||
0xBD : {"name": "ReadData ", "CMDdecoder":decodeReadData, "RespDecoder": decodeRespReadData},
|
||||
0xAF : {"name": "AdditionalFrame", "CMDdecoder":decodeAdiFrame, "RespDecoder": decodeRespAdiFrame}
|
||||
}
|
||||
|
||||
|
||||
def MFDESFireDecode(data, source):
|
||||
note = ""
|
||||
global lastCMD
|
||||
if (source == TrafficSource.Reader):
|
||||
cmd = data[0]
|
||||
if cmd in MFDESFireCMDTypes :
|
||||
lastCMD = cmd
|
||||
note += "CMD:" + MFDESFireCMDTypes[cmd]["name"]
|
||||
note += MFDESFireCMDTypes[cmd]["CMDdecoder"](data)
|
||||
|
||||
elif (source == TrafficSource.Card):
|
||||
status = data[0]
|
||||
# Decode status code
|
||||
if status in StatusCode:
|
||||
note += StatusCode[status] + " "
|
||||
# If status Ok, decode data
|
||||
if status == 0x00 and lastCMD in MFDESFireCMDTypes:
|
||||
note += MFDESFireCMDTypes[lastCMD]["RespDecoder"](data)
|
||||
|
||||
return note
|
||||
@@ -0,0 +1,5 @@
|
||||
from enum import Enum
|
||||
|
||||
class TrafficSource(Enum):
|
||||
Reader = 0
|
||||
Card = 1
|
||||
Reference in New Issue
Block a user