]>
Commit | Line | Data |
---|---|---|
1 | ||
2 | -- Ability to read what card is there | |
3 | local getopt = require('getopt') | |
4 | local cmds = require('commands') | |
5 | local taglib = require('taglib') | |
6 | ||
7 | local desc = | |
8 | [[This script will automatically recognize and dump full content of a NFC NDEF Initialized tag; non-initialized tags will be ignored. | |
9 | ||
10 | Arguments: | |
11 | -d debug logging on | |
12 | -h this help | |
13 | ||
14 | ]] | |
15 | local example = "script run xxx" | |
16 | local author = "Martin Holst Swende & Asper" | |
17 | ||
18 | ||
19 | ||
20 | ||
21 | --- | |
22 | -- PrintAndLog | |
23 | function prlog(...) | |
24 | -- TODO; replace this with a call to the proper PrintAndLog | |
25 | print(...) | |
26 | end | |
27 | --- | |
28 | -- Usage help | |
29 | function help() | |
30 | prlog(desc) | |
31 | prlog("Example usage") | |
32 | prlog(example) | |
33 | end | |
34 | ||
35 | function debug(...) | |
36 | if DEBUG then | |
37 | prlog("debug:", ...) | |
38 | end | |
39 | end | |
40 | ||
41 | --- | |
42 | -- This is only meant to be used when errors occur | |
43 | function oops(err) | |
44 | prlog("ERROR: ",err) | |
45 | return nil,err | |
46 | end | |
47 | ||
48 | local function show(data) | |
49 | if DEBUG then | |
50 | local formatString = ("H%d"):format(string.len(data)) | |
51 | local _,hexdata = bin.unpack(formatString, data) | |
52 | debug("Hexdata" , hexdata) | |
53 | end | |
54 | end | |
55 | --- Fire up a connection with a tag, return uid | |
56 | -- @return UID if successfull | |
57 | -- @return nil, errormessage if unsuccessfull | |
58 | ||
59 | local function open() | |
60 | debug("Opening connection") | |
61 | core.clearCommandBuffer() | |
62 | local x = string.format("hf 14a raw -r -p -s") | |
63 | debug(x) | |
64 | core.console(x) | |
65 | debug("done") | |
66 | data, err = waitCmd(true) | |
67 | if err then return oops(err) end | |
68 | show(data) | |
69 | local formatString = ("H%d"):format(string.len(data)) | |
70 | local _,uid = bin.unpack(formatString, data) | |
71 | return uid | |
72 | end | |
73 | --- Shut down tag communication | |
74 | -- return no return values | |
75 | local function close() | |
76 | debug("Closing connection") | |
77 | core.clearCommandBuffer() | |
78 | local x = string.format("hf 14a raw -r") | |
79 | debug(x) | |
80 | core.console(x) | |
81 | debug("done") | |
82 | --data, err = waitCmd(true) | |
83 | --data, err = waitCmd(false) | |
84 | ||
85 | end | |
86 | ||
87 | ||
88 | ---_ Gets data from a block | |
89 | -- @return {block, block+1, block+2, block+3} if successfull | |
90 | -- @return nil, errormessage if unsuccessfull | |
91 | local function getBlock(block) | |
92 | local data, err | |
93 | ||
94 | core.clearCommandBuffer() | |
95 | ||
96 | local x = string.format("hf 14a raw -r -c -p 30 %02x", block) | |
97 | debug(x) | |
98 | core.console(x) | |
99 | debug("done") | |
100 | -- By now, there should be an ACK waiting from the device, since | |
101 | -- we used the -r flag (don't read response). | |
102 | ||
103 | data, err = waitCmd(false) | |
104 | if err then return oops(err) end | |
105 | show(data) | |
106 | ||
107 | if string.len(data) < 18 then | |
108 | return nil, ("Expected at least 18 bytes, got %d - this tag is not NDEF-compliant"):format(string.len(data)) | |
109 | end | |
110 | -- Now, parse out the block data | |
111 | -- 0534 00B9 049C AD7F 4A00 0000 E110 1000 2155 | |
112 | -- b0b0 b0b0 b1b1 b1b1 b2b2 b2b2 b3b3 b3b3 CRCC | |
113 | b0 = string.sub(data,1,4) | |
114 | b1 = string.sub(data,5,8) | |
115 | b2 = string.sub(data,9,12) | |
116 | b3 = string.sub(data,13,16) | |
117 | return {b0,b1,b2,b3} | |
118 | end | |
119 | ||
120 | ||
121 | --- This function is a lua-implementation of | |
122 | -- cmdhf14a.c:waitCmd(uint8_t iSelect) | |
123 | function waitCmd(iSelect) | |
124 | local response = core.WaitForResponseTimeout(cmds.CMD_ACK,1000) | |
125 | if response then | |
126 | local count,cmd,arg0,arg1,arg2 = bin.unpack('LLLL',response) | |
127 | ||
128 | local iLen = arg0 | |
129 | if iSelect then iLen = arg1 end | |
130 | debug(("Received %i octets (arg0:%d, arg1:%d)"):format(iLen, arg0, arg1)) | |
131 | if iLen == 0 then return nil, "No response from tag" end | |
132 | local recv = string.sub(response,count, iLen+count-1) | |
133 | return recv | |
134 | end | |
135 | return nil, "No response from device" | |
136 | end | |
137 | ||
138 | ||
139 | ||
140 | local function main( args) | |
141 | debug("script started") | |
142 | local err, data, data2,k,v,i | |
143 | -- Read the parameters | |
144 | for o, a in getopt.getopt(args, 'hd') do | |
145 | if o == "h" then help() return end | |
146 | if o == "d" then DEBUG = true end | |
147 | end | |
148 | ||
149 | -- Info contained within the tag (block 0 example) | |
150 | -- 0534 00B9 049C AD7F 4A00 0000 E110 1000 2155 | |
151 | -- b0b0 b0b0 b1b1 b1b1 b2b2 b2b2 b3b3 b3b3 CRCC | |
152 | -- MM?? ???? ???? ???? ???? ???? NNVV SS?? ---- | |
153 | -- M = Manufacturer info | |
154 | -- N = NDEF-Structure-Compliant (if value is E1) | |
155 | -- V = NFC Forum Specification version (if 10 = v1.0) | |
156 | ||
157 | -- First, 'connect' (fire up the field) and get the uid | |
158 | local uidHexstr = open() | |
159 | ||
160 | -- First, get blockt 3 byte 2 | |
161 | local blocks, err = getBlock(0) | |
162 | if err then return oops(err) end | |
163 | -- Block 3 contains number of blocks | |
164 | local b3chars = {string.byte(blocks[4], 1,4)} | |
165 | local numBlocks = b3chars[3] * 2 + 6 | |
166 | prlog("Number of blocks:", numBlocks) | |
167 | ||
168 | -- NDEF compliant? | |
169 | if b3chars[1] ~= 0xE1 then | |
170 | return oops("This tag is not NDEF-Complian") | |
171 | end | |
172 | ||
173 | local ndefVersion = b3chars[2] | |
174 | ||
175 | -- Block 1, byte 1 contains manufacturer info | |
176 | local bl1_b1 = string.byte(blocks[1], 1) | |
177 | local manufacturer = taglib.lookupManufacturer(bl1_b1) | |
178 | ||
179 | -- Reuse existing info | |
180 | local blockData = {blocks[1],blocks[2],blocks[3],blocks[4]} | |
181 | ||
182 | ||
183 | --[[ Due to the infineon my-d move bug | |
184 | (if I send 30 0F i receive block0f+block00+block01+block02 insted of block0f+block10+block11+block12) | |
185 | the only way to avoid this is to send the read command as many times as block numbers | |
186 | removing bytes from 5 to 18 from each answer. | |
187 | --]] | |
188 | prlog("Dumping data...please wait") | |
189 | for i=4,numBlocks-1,1 do | |
190 | blocks, err = getBlock(i) | |
191 | if err then return oops(err) end | |
192 | table.insert(blockData,blocks[1]) | |
193 | end | |
194 | -- Deactivate field | |
195 | close() | |
196 | -- Print results | |
197 | prlog(string.format("Tag manufacturer: %s", manufacturer)) | |
198 | prlog(string.format("Tag UID: %s", uidHexstr)) | |
199 | prlog(string.format("Tag NDEF version: 0x%02x", ndefVersion)) | |
200 | ||
201 | for k,v in ipairs(blockData) do | |
202 | prlog(string.format("Block %02x: %02x %02x %02x %02x",k-1, string.byte(v, 1,4))) | |
203 | end | |
204 | end | |
205 | main(args) |