3 This is a library to read 14443b tags. It can be used something like this
5 local reader = require('read14b')
6 result, err = reader.select1443b()
14 -- Loads the commands-library
15 local cmds = require('commands')
16 local utils = require('utils')
17 local TIMEOUT = 10000 -- Shouldn't take longer than 2 seconds
19 local function parse1443b_reqb(data)
22 local pcb = data:sub(1,2)
23 local uid = data:sub(3,10)
24 local pps = data:sub(11,18)
25 local ats = data:sub(19,24)
26 local crc = data:sub(25,29)
27 return { pcb = pcb, uid = uid, pps = pps, ats = ats, crc = crc, cid = '' }
30 local function parse1443b_attrib(data)
33 local attrib = data:sub(1,2)
34 local crc = data:sub(3,7)
35 return { attrib = attrib, crc = crc }
39 --- Sends a USBpacket to the device
40 -- @param command - the usb packet to send
41 -- @param readresponse - if set to true, we read the device answer packet
42 -- which is usually recipe for fail. If not sent, the host will wait 2s for a
43 -- response of type CMD_ACK
44 -- @return packet,nil if successfull
45 -- nil, errormessage if unsuccessfull
46 local function sendToDevice(cmd, readresponse)
47 core.clearCommandBuffer()
48 local err = core.SendCommand(cmd:getBytes())
53 if readresponse == 0 then return '',nil end
54 local response = core.WaitForResponseTimeout(cmds.CMD_ACK, TIMEOUT)
55 if response == nil then return nil, nil end
58 --- Picks out and displays the data read from a tag
59 -- Specifically, takes a usb packet, converts to a Command
60 -- (as in commands.lua), takes the data-array and
61 -- reads the number of bytes specified in arg1 (arg0 in c-struct)
62 -- and displays the data
63 -- @param usbpacket the data received from the device
64 local function showData(usbpacket)
65 local response = Command.parse(usbpacket)
66 local len = tonumber(response.arg1) * 2
67 local data = string.sub(response.data, 0, len);
72 -- Sends a usbpackage , "hf 14b raw" and the 14bCrc is added to the rawdata before sending
73 local function sendRaw(rawdata, readresponse, addcrc)
75 local rawdata_crc = rawdata
76 if ( addcrc == 1) then
77 rawdata_crc = utils.Crc14b(rawdata)
79 print(">> ", rawdata_crc)
81 local command = Command:new{cmd = cmds.CMD_ISO_14443B_COMMAND,
82 arg1 = #rawdata_crc/2, -- LEN of data, which is half the length of the ASCII-string rawdata
83 arg2 = readresponse, -- read response
84 arg3 = 1, -- leave power on
85 data = rawdata_crc} -- raw data bytes
86 return sendToDevice(command, readresponse)
89 -- This function does a connect and retrieves some info
90 -- @return if successfull: an table containing card info
91 -- @return if unsuccessfull : nil, error
93 -- void SendRawCommand14443B(uint32_t datalen, uint32_t recv, uint8_t powerfield, uint8_t data[])
94 local function select1443b()
96 local result, infoReqb, infoAttrib, infoPong, err, resp, len, data
97 local goodReqbResponse = false
103 -- command (REQB/WUPB)
105 -- AFI application family identifier ( 00 == all sorts)
107 -- bit 0-1-2 | N slots ( 0 = 1, 1 = 2, 2 = 4, 3 = 8, 4 == 16)
108 -- bit 3 | (1== WUPB, 0 == REQB)
109 -- bit 4-5-6-7 | AFI application family identifier
110 local result, err = sendRaw('050000', 1, 1)
112 resp = Command.parse( result )
113 len = tonumber(resp.arg1) * 2
114 local data = string.sub(resp.data, 0, len)
115 if ( resp.arg1 == 14 ) then
116 --print ('DATA ::', data)
117 infoReqb, err = parse1443b_reqb(data)
118 --print(infoReqb.pcb, infoReqb.uid, infoReqb.pps, infoReqb.ats, infoReqb.crc)
119 goodReqbResponse = true
120 break -- break while loop. REQB got a good response
124 -- send some strange 0A/0C
134 if goodReqbResponse == false then
135 err = "No response from card"
140 -- result, err = sendRaw('05', 1, 1)
143 -- resp = Command.parse( result )
145 -- return nil, "iso14443b card - SLOT MARKER failed"
147 -- len = tonumber(resp.arg1) * 2
148 -- data = string.sub(resp.data, 0, len)
149 -- infoAttrib, err = parse1443b_attrib(data)
150 -- print( infoAttrib.attrib, infoAttrib.crc)
152 -- err ="No response from card"
159 result, err = sendRaw('1D'..infoReqb.uid..'000801'..cid, 1, 1)
162 resp = Command.parse( result )
163 if resp.arg1 == 0 then
164 return nil, "iso14443b card - ATTRIB failed"
166 len = tonumber(resp.arg1) * 2
167 data = string.sub(resp.data, 0, len)
168 infoAttrib, err = parse1443b_attrib(data)
169 infoReqb.cid = infoAttrib.attrib:sub(2,2)
171 err ="No response from card"
176 --PING / PONG - Custom Anticollison for Navigo.
177 local ping = ('BA00')
178 result, err = sendRaw(ping, 1, 1)
180 resp = Command.parse( result )
182 return nil, "iso14443b card - PING/PONG failed"
186 err = "No response from card"
195 -- Waits for a mifare card to be placed within the vicinity of the reader.
196 -- @return if successfull: an table containing card info
197 -- @return if unsuccessfull : nil, error
198 local function waitFor14443b()
199 print("Waiting for card... press any key to quit")
200 while not core.ukbhit() do
201 res, err = select1443b()
202 if res then return res end
203 -- err means that there was no response from card
205 return nil, "Aborted by user"
208 local function disconnect(uid)
210 local halt = ('50'..uid) -- 50 UID0 UID1 UID2 UID3 CRC1 CRC2
211 result, err = sendRaw(halt, 1, 1)
213 resp = Command.parse( result )
214 showData(result) -- expected answer is 00 CRC1 CRC2
216 err = "No response from card"
221 -- shutdown raw command / pm3 device.
222 local command = Command:new{ cmd = cmds.CMD_ISO_14443B_COMMAND, arg1 = 0, arg2 = 0, arg3 = 0 }
223 -- We can ignore the response here, no ACK is returned for this command
224 -- Check /armsrc/iso14443b.c, SendRawCommand14443B() for details
225 return sendToDevice(command, 0)
229 select1443b = select1443b,
230 select = select1443b,
231 waitFor14443b = waitFor14443b,
232 sendToDevice = sendToDevice,
233 disconnect = disconnect,