Merge pull request #2664 from piotrva/hf-mf-ultimatecard-script-max-rw-blocks
[RRG-proxmark3.git] / client / luascripts / hf_14b_mobib.lua
blobab675d28fdcb6da6010e4fcfbfe4aeba2c18e241
1 local cmds = require('commands')
2 local getopt = require('getopt')
3 local lib14b = require('read14b')
4 local utils = require('utils')
5 local iso7816 = require('7816_error')
6 local ansicolors = require('ansicolors')
8 copyright = ''
9 author = 'Iceman'
10 version = 'v1.0.2'
11 desc = [[
12 This is a script to communicate with a MOBIB tag using the '14b raw' commands
14 example = [[
15 script run hf_14b_mobib
16 script run hf_14b_mobib -b 11223344
19 usage = [[
20 script run hf_14b_mobib -h -b
22 arguments = [[
23 h this helptext
24 b raw bytes to send
27 --[[
28 This script communicates with /armsrc/iso14443b.c,
29 Check there for details about data format and how commands are interpreted on the
30 device-side.
33 -- iceman, todo: return payload from ISO14b APDU is a struct now. iso14b_raw_apdu_response_t
34 local function mobib_parse(result)
35 if result.Length >= 0 then
36 local response_byte = string.sub(result.Data, 0, 1);
37 local datalen = string.sub(result.Data, 2, 5);
38 local d = string.sub(result.Data, 6, datalen * 2);
39 return {
40 response_byte = response_byte,
41 datalen = datalen,
42 data = d
43 }, nil
44 end
45 return nil, "mobib_parse failed"
46 end
47 ---
48 -- A debug printout-function
49 local function dbg(args)
50 if not DEBUG then return end
51 if type(args) == 'table' then
52 local i = 1
53 while args[i] do
54 dbg(args[i])
55 i = i+1
56 end
57 else
58 print('###', args)
59 end
60 end
61 ---
62 -- This is only meant to be used when errors occur
63 local function oops(err)
64 print('ERROR: ', err)
65 lib14b.disconnect()
66 return nil, err
67 end
68 ---
69 -- Usage help
70 local function help()
71 print(copyright)
72 print(author)
73 print(version)
74 print(desc)
75 print(ansicolors.cyan..'Usage'..ansicolors.reset)
76 print(usage)
77 print(ansicolors.cyan..'Arguments'..ansicolors.reset)
78 print(arguments)
79 print(ansicolors.cyan..'Example usage'..ansicolors.reset)
80 print(example)
81 end
83 -- helper function, give current count of items in lua-table.
84 local function tablelen(T)
85 local count = 0
86 for _ in pairs(T) do count = count + 1 end
87 return count
88 end
89 ---
90 -- helper function, gives a sorted table from table t,
91 -- order can be a separate sorting-order function.
92 local function spairs(t, order)
93 -- collect the keys
94 local keys = {}
95 for k in pairs(t) do keys[#keys+1] = k end
97 -- if order function given, sort by it by passing the table and keys a, b,
98 -- otherwise just sort the keys
99 if order then
100 table.sort(keys, function(a,b) return order(t, a, b) end)
101 else
102 table.sort(keys)
105 -- return the iterator function
106 local i = 0
107 return function()
108 i = i + 1
109 if keys[i] then
110 return keys[i], t[keys[i]]
115 -- Sends a usbpackage , "hf 14b raw"
116 -- if it reads the response, it converts it to a lua object "Command" first and the Data is cut to correct length.
117 local function mobib_send_cmd_raw(data, ignoreresponse )
118 local flags = lib14b.ISO14B_COMMAND.ISO14B_APDU
119 data = data or ""
120 -- LEN of data, half the length of the ASCII-string hex string
121 -- 2 bytes flags
122 -- 4 bytes timeout
123 -- 2 bytes raw len
124 -- n bytes raw
125 local flags_str = ('%04x'):format(utils.SwapEndianness(('%04x'):format(flags), 16))
126 local time_str = ('%08x'):format(0)
127 local rawlen_str = ('%04x'):format(utils.SwapEndianness(('%04x'):format(( 8 + #data/2)), 16))
128 local senddata = ('%s%s%s%s'):format(flags_str, time_str, rawlen_str,data)
129 local c = Command:newNG{cmd = cmds.CMD_HF_ISO14443B_COMMAND, data = senddata}
131 local result, err = c:sendNG(ignoreresponse, 2000)
132 if result and result.status == PM3_SUCCESS then
133 return mobib_parse(result)
134 else
135 err = 'No response from card'
137 return result, err
140 -- mobib_card_num : Reads card number from ATR and
141 -- writes it in the tree in decimal format.
142 local function mobib_card_num(card)
143 if not card then return end
144 local card_num = tonumber( card.uid:sub(1,8),16 )
145 print('')
146 print('Card UID ' ..ansicolors.green..card.uid:format('%x')..ansicolors.reset)
147 print('Card Number ' ..ansicolors.green..string.format('%u', card_num)..ansicolors.reset)
148 print('-----------------------')
151 -- analyse CALYPSO apdu status bytes.
152 local function mobib_apdu_status(apdu)
153 -- last two is CRC
154 -- next two is APDU status bytes.
155 local mess = 'FAIL'
156 local sw = apdu:sub( #apdu-7, #apdu-4)
157 desc, err = iso7816.tostring(sw)
158 --print ('SW', sw, desc, err )
159 local status = ( sw == '9000' )
160 return status, desc, err
163 local CLA = '00'
164 local _calypso_cmds = {
165 ['01.SELECT AID 1TIC.ICA'] = CLA..'a4 0400 08 315449432e494341',
166 ['02.Select ICC file a'] = CLA..'a4 0000 02 3f00',
167 ['03.Select ICC file b'] = CLA..'a4 0000 02 0002',
168 ['04.ICC'] = CLA..'b2 0104 1d',
169 ['05.Select Holder file'] = CLA..'a4 0000 02 3f1c',
170 ['06.Holder1'] = CLA..'b2 0104 1d',
171 ['07.Holder2'] = CLA..'b2 0204 1d',
172 ['08.Select EnvHol file a'] = CLA..'a4 0000 00',
173 ['09.Select EnvHol file b'] = CLA..'a4 0000 02 2000',
174 ['10.Select EnvHol file c'] = CLA..'a4 0000 02 2001',
175 ['11.EnvHol1'] = CLA..'b2 0104 1d',
176 ['11.EnvHol2'] = CLA..'b2 0204 1d',
177 ['12.Select EvLog file'] = CLA..'a4 0000 02 2010',
178 ['13.EvLog1'] = CLA..'b2 0104 1d',
179 ['14.EvLog2'] = CLA..'b2 0204 1d',
180 ['15.EvLog3'] = CLA..'b2 0304 1d',
181 ['16.Select ConList file'] = CLA..'a4 0000 02 2050',
182 ['17.ConList'] = CLA..'b2 0104 1d',
183 ['18.Select Contra file'] = CLA..'a4 0000 02 2020',
184 ['19.Contra1'] = CLA..'b2 0104 1d',
185 ['20.Contra2'] = CLA..'b2 0204 1d',
186 ['21.Contra3'] = CLA..'b2 0304 1d',
187 ['22.Contra4'] = CLA..'b2 0404 1d',
188 ['23.Contra5'] = CLA..'b2 0504 1d',
189 ['24.Contra6'] = CLA..'b2 0604 1d',
190 ['25.Contra7'] = CLA..'b2 0704 1d',
191 ['26.Contra8'] = CLA..'b2 0804 1d',
192 ['27.Contra9'] = CLA..'b2 0904 1d',
193 ['28.ContraA'] = CLA..'b2 0a04 1d',
194 ['29.ContraB'] = CLA..'b2 0b04 1d',
195 ['30.ContraC'] = CLA..'b2 0c04 1d',
196 ['31.Select Counter file'] = CLA..'a4 0000 02 2069',
197 ['32.Counter'] = CLA..'b2 0104 1d',
198 ['33.Select LoadLog file a'] = CLA..'a4 0000 00',
199 ['34.Select LoadLog file b'] = CLA..'a4 0000 02 1000',
200 ['35.Select LoadLog file c'] = CLA..'a4 0000 02 1014',
201 ['36.LoadLog'] = CLA..'b2 0104 1d',
202 ['37.Select Purcha file'] = CLA..'a4 0000 02 1015',
203 ['38.Purcha1'] = CLA..'b2 0104 1d',
204 ['39.Purcha2'] = CLA..'b2 0204 1d',
205 ['40.Purcha3'] = CLA..'b2 0304 1d',
206 ['41.Select SpecEv file a'] = CLA..'a4 0000 00',
207 ['42.Select SpecEv file b'] = CLA..'a4 0000 02 2000',
208 ['43.Select SpecEv file c'] = CLA..'a4 0000 02 2040',
209 ['44.SpecEv1'] = CLA..'b2 0104 1d',
210 ['45.SpecEv2'] = CLA..'b2 0204 1d',
211 ['46.SpecEv3'] = CLA..'b2 0304 1d',
212 ['47.SpecEv4'] = CLA..'b2 0404 1d',
216 -- The main entry point
217 function main(args)
219 print( string.rep('--',20) )
220 print( string.rep('--',20) )
221 print()
223 local data, apdu, flags, uid, cid, result, err, card
224 -- Read the parameters
225 for o, a in getopt.getopt(args, 'h') do
226 if o == 'h' then return help() end
227 if o == 'b' then bytes = a end
230 -- lib14b.connect()
232 -- Select 14b tag.
233 card, err = lib14b.waitFor14443b()
234 if not card then return oops(err) end
236 mobib_card_num(card)
237 cid = card.cid
239 for i, apdu in spairs(_calypso_cmds) do
240 print('>> '..ansicolors.yellow..i..ansicolors.reset)
241 apdu = apdu:gsub('%s+', '')
242 obj, err = mobib_send_cmd_raw(apdu , false)
243 if err then
244 print('<< '..err)
245 else
246 if obj.data then
247 local status, desc, err = mobib_apdu_status(obj.data)
248 local d = data:sub(3, (obj.datalen - 8))
249 if status then
250 print('<< '..d..' ('..ansicolors.green..'ok'..ansicolors.reset..')')
251 else
252 print('<< '..d..' '..ansicolors.red..err..ansicolors.reset )
254 else
255 print('<< no answer')
259 lib14b.disconnect()
262 -- a simple selftest function, tries to convert
263 function selftest()
264 DEBUG = true
265 dbg('Performing test')
266 dbg('Tests done')
268 -- Flip the switch here to perform a sanity check.
269 -- It read a nonce in two different ways, as specified in the usage-section
270 if '--test'==args then
271 selftest()
272 else
273 -- Call the main
274 main(args)