1 local cmds = require('commands')
 
   2 local getopt = require('getopt')
 
   3 local bin = require('bin')
 
   4 local lib14a = require('read14a')
 
   5 local utils = require('utils')
 
   6 local md5 = require('md5')
 
   7 local dumplib = require('html_dumplib')
 
   8 local toyNames = require('default_toys')
 
  11         1. script run tnp3dump
 
  12         2. script run tnp3dump -n
 
  13         3. script run tnp3dump -k aabbccddeeff
 
  14         4. script run tnp3dump -k aabbccddeeff -n
 
  15         5. script run tnp3dump -o myfile 
 
  16         6. script run tnp3dump -n -o myfile 
 
  17         7. script run tnp3dump -k aabbccddeeff -n -o myfile 
 
  20 usage = "script run tnp3dump -k <key> -n -o <filename>"
 
  22 This script will try to dump the contents of a Mifare TNP3xxx card.
 
  23 It will need a valid KeyA in order to find the other keys and decode the card.
 
  26         -k <key>       : Sector 0 Key A.
 
  27         -n             : Use the nested cmd to find all keys
 
  28         -o             : filename for the saved dumps
 
  31 local HASHCONSTANT = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20'
 
  33 local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds
 
  34 local DEBUG = false -- the debug flag
 
  38 -- A debug printout-function
 
  44     if type(args) == "table" then
 
  55 -- This is only meant to be used when errors occur
 
  63         print("Example usage")
 
  69         print( string.rep('--',20) )
 
  70         print( string.rep('--',20) )
 
  75 local function readdumpkeys(infile)
 
  76          t = infile:read("*all")
 
  78          local len,hex = bin.unpack(("H%d"):format(len),t)
 
  82 local function waitCmd()
 
  83         local response = core.WaitForResponseTimeout(cmds.CMD_ACK,TIMEOUT)
 
  85                 local count,cmd,arg0 = bin.unpack('LL',response)
 
  87                         local count,arg1,arg2,data = bin.unpack('LLH511',response,count)
 
  90                         return nil, "Couldn't read block.." 
 
  93         return nil, "No response from device"
 
  96 local function computeCrc16(s)
 
  97         local hash = core.crc16(utils.ConvertHexToAscii(s))     
 
 101 local function reverseCrcBytes(crc)
 
 102         crc2 = crc:sub(3,4)..crc:sub(1,2)
 
 103         return tonumber(crc2,16)
 
 106 local function main(args)
 
 108         print( string.rep('--',20) )
 
 109         print( string.rep('--',20) )
 
 114         local useNested = false
 
 115         local cmdReadBlockString = 'hf mf rdbl %d A %s'
 
 116         local input = "dumpkeys.bin"
 
 117         local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M%S");
 
 119         -- Arguments for the script
 
 120         for o, a in getopt.getopt(args, 'hk:no:') do
 
 121                 if o == "h" then return help() end              
 
 122                 if o == "k" then keyA = a end
 
 123                 if o == "n" then useNested = true end
 
 124                 if o == "o" then outputTemplate = a end         
 
 127         -- validate input args.
 
 128         keyA =  keyA or '4b0b20107ccb'
 
 129         if #(keyA) ~= 12 then
 
 130                 return oops( string.format('Wrong length of write key (was %d) expected 12', #keyA))
 
 134         local cmdSetDbgOff = "hf mf dbg 0"
 
 135         core.console( cmdSetDbgOff) 
 
 137         result, err = lib14a.read1443a(false)
 
 142         core.clearCommandBuffer()
 
 144         if 0x01 ~= result.sak then -- NXP MIFARE TNP3xxx
 
 145                 return oops('This is not a TNP3xxx tag. aborting.')
 
 149         print((' Found tag : %s'):format(result.name))
 
 150         print(('Using keyA : %s'):format(keyA))
 
 152         --Trying to find the other keys
 
 154           core.console( ('hf mf nested 1 0 A %s d'):format(keyA) )
 
 157         core.clearCommandBuffer()
 
 160         print('Loading dumpkeys.bin')
 
 161         local hex, err = utils.ReadDumpFile(input)
 
 166         local akeys = hex:sub(0,12*16)
 
 169         cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 0,arg2 = 0,arg3 = 0, data = keyA}
 
 170         err = core.SendCommand(cmd:getBytes())
 
 171         if err then return oops(err) end
 
 172         local block0, err = waitCmd()
 
 173         if err then return oops(err) end
 
 176         cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 1,arg2 = 0,arg3 = 0, data = keyA}
 
 177         err = core.SendCommand(cmd:getBytes())
 
 178         if err then return oops(err) end
 
 179         local block1, err = waitCmd()
 
 180         if err then return oops(err) end
 
 187         print('Reading card data')
 
 188         core.clearCommandBuffer()
 
 191         io.write('Decrypting blocks > ')
 
 192         for blockNo = 0, numBlocks-1, 1 do
 
 194                 if core.ukbhit() then
 
 195                         print("aborted by user")
 
 199                 pos = (math.floor( blockNo / 4 ) * 12)+1
 
 200                 key = akeys:sub(pos, pos + 11 )
 
 201                 cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = blockNo ,arg2 = 0,arg3 = 0, data = key}
 
 202                 local err = core.SendCommand(cmd:getBytes())
 
 203                 if err then return oops(err) end
 
 204                 local blockdata, err = waitCmd()
 
 205                 if err then return oops(err) end                
 
 207                 if  blockNo%4 ~= 3 then
 
 209                                 -- Block 0-7 not encrypted
 
 210                                 blocks[blockNo+1] = ('%02d  :: %s'):format(blockNo,blockdata) 
 
 212                                 local base = ('%s%s%02x%s'):format(block0, block1, blockNo, HASHCONSTANT)       
 
 213                                 local baseStr = utils.ConvertHexToAscii(base)
 
 214                                 local md5hash = md5.sumhexa(baseStr)
 
 215                                 local aestest = core.aes(md5hash, blockdata)
 
 217                                 local hex = utils.ConvertAsciiToBytes(aestest)
 
 218                                 hex = utils.ConvertBytesToHex(hex)
 
 220                                 -- blocks with zero not encrypted.
 
 221                                 if string.find(blockdata, '^0+$') then
 
 222                                         blocks[blockNo+1] = ('%02d  :: %s'):format(blockNo,blockdata) 
 
 224                                         blocks[blockNo+1] = ('%02d  :: %s'):format(blockNo,hex)
 
 225                                         io.write( blockNo..',')
 
 229                         -- Sectorblocks, not encrypted
 
 230                         blocks[blockNo+1] = ('%02d  :: %s%s'):format(blockNo,key,blockdata:sub(13,32)) 
 
 235         core.clearCommandBuffer()
 
 241         for _,s in pairs(blocks) do
 
 242                 local slice = s:sub(8,#s)
 
 243                 local str = utils.ConvertBytesToAscii(
 
 244                                  utils.ConvertHexToBytes(slice)
 
 246                 emldata = emldata..slice..'\n'
 
 247                 for c in (str):gmatch('.') do
 
 248                         bindata[#bindata+1] = c
 
 252         -- Write dump to files
 
 254                 local foo = dumplib.SaveAsBinary(bindata, outputTemplate..'.bin')
 
 255                 print(("Wrote a BIN dump to the file %s"):format(foo))
 
 256                 local bar = dumplib.SaveAsText(emldata, outputTemplate..'.eml')
 
 257                 print(("Wrote a EML dump to the file %s"):format(bar))
 
 260         local uid = block0:sub(1,8)
 
 261         local itemtype = block1:sub(1,4)
 
 262         local cardid = block1:sub(9,24)
 
 265         print( string.rep('--',20) )
 
 266         print( (' ITEM TYPE : 0x%s - %s'):format(itemtype, toyNames[itemtype]) )
 
 267         print( ('       UID : 0x%s'):format(uid) )
 
 268         print( ('    CARDID : 0x%s'):format(cardid ) )  
 
 269         print( string.rep('--',20) )