3 local cmds
= require('commands')
4 local getopt
= require('getopt')
5 local utils
= require('utils')
6 local lib14a
= require('read14a')
7 local json
= require('dkjson')
8 local toys
= require('default_toys_di')
9 local ansicolors
= require('ansicolors')
15 This is a script to dump and decrypt the data of a specific type of Mifare Mini token.
16 The dump is decrypted. If a raw dump is wanted, use the -r parameter
19 script run hf_mf_mini_dumpdecrypt
22 script run hf_mf_mini_dumpdecrypt -t
24 -- Generate raw dump, into json.
25 script run hf_mf_mini_dumpdecrypt -r
28 script run hf_mf_mini_dumpdecrypt -i dumpdata.json
31 script run hf_mf_mini_dumpdecrypt -h -t -r -d -e -v -i dumpdata.json
40 i dumpdata.json load json dump file
45 local band
= bit32
.band
47 local bnot
= bit32
.bnot
48 local bxor
= bit32
.bxor
49 local lsh
= bit32
.lshift
50 local rsh
= bit32
.rshift
54 local FOO
= 'AF62D2EC0491968CC52A1A7165F865FE'
55 local BAR
= '286329204469736E65792032303133'
56 local MIS
= '0A14FD0507FF4BCD026BA83F0A3B89A9'
57 local outputTemplate
= os
.date("toydump_%Y-%m-%d_%H%M%S");
62 local CHECKSUM_OFFSET
= 12; -- +1???
65 -- A debug printout-function
66 local function dbg(args
)
67 if not DEBUG
then return end
68 if type(args
) == 'table' then
79 -- This is only meant to be used when errors occur
80 local function oops(err
)
82 core
.clearCommandBuffer()
92 print(ansicolors
.cyan
..'Usage'..ansicolors
.reset
)
94 print(ansicolors
.cyan
..'Arguments'..ansicolors
.reset
)
96 print(ansicolors
.cyan
..'Example usage'..ansicolors
.reset
)
101 local function print_info(tagdata
)
102 --got table with data.
103 local h
= tagdata
[2]:sub(1,8)
104 local t
= tostring( tonumber( h
, 16 ) )
105 local item
= toys
.Find(t
)
106 print( ("Modelid : %s , %s, v.%s.0"):format(t
, item
[3], item
[2]))
110 -- called: data is string (32 hex digits)
112 local function getChecksum(data
)
113 local chksum
= data
:sub(25,32)
114 return tonumber(chksum
,16)
117 -- calculate checksum
118 -- called: data is bytes (24 hex digits)
120 local function calculateChecksum(data
)
125 _tbl
[1] = { 0x77073096 }
126 _tbl
[2] = { 0xEE0E612C }
127 _tbl
[3] = { 0x990951BA }
128 _tbl
[4] = { 0x76DC419 }
129 _tbl
[5] = { 0x706AF48F }
130 _tbl
[6] = { 0xE963A535 }
131 _tbl
[7] = { 0x9E6495A3 }
132 _tbl
[8] = { 0xEDB8832 }
133 _tbl
[9] = { 0x79DCB8A4 }
134 _tbl
[10] = { 0xE0D5E91E }
135 _tbl
[11] = { 0x97D2D988 }
136 _tbl
[12] = { 0x9B64C2B }
137 _tbl
[13] = { 0x7EB17CBD }
138 _tbl
[14] = { 0xE7B82D07 }
139 _tbl
[15] = { 0x90BF1D91 }
140 _tbl
[16] = { 0x1DB71064 }
141 _tbl
[17] = { 0x6AB020F2 }
142 _tbl
[18] = { 0xF3B97148 }
143 _tbl
[19] = { 0x84BE41DE }
144 _tbl
[20] = { 0x1ADAD47D }
145 _tbl
[21] = { 0x6DDDE4EB }
146 _tbl
[22] = { 0xF4D4B551 }
147 _tbl
[23] = { 0x83D385C7 }
148 _tbl
[24] = { 0x136C9856 }
149 _tbl
[25] = { 0x646BA8C0 }
150 _tbl
[26] = { 0xFD62F97A }
151 _tbl
[27] = { 0x8A65C9EC }
152 _tbl
[28] = { 0x14015C4F }
153 _tbl
[29] = { 0x63066CD9 }
154 _tbl
[30] = { 0xFA0F3D63 }
155 _tbl
[31] = { 0x8D080DF5 }
156 _tbl
[32] = { 0x3B6E20C8 }
157 _tbl
[33] = { 0x4C69105E }
158 _tbl
[34] = { 0xD56041E4 }
159 _tbl
[35] = { 0xA2677172 }
160 _tbl
[36] = { 0x3C03E4D1 }
161 _tbl
[37] = { 0x4B04D447 }
162 _tbl
[38] = { 0xD20D85FD }
163 _tbl
[39] = { 0xA50AB56B }
164 _tbl
[40] = { 0x35B5A8FA }
165 _tbl
[41] = { 0x42B2986C }
166 _tbl
[42] = { 0xDBBBC9D6 }
167 _tbl
[43] = { 0xACBCF940 }
168 _tbl
[44] = { 0x32D86CE3 }
169 _tbl
[45] = { 0x45DF5C75 }
170 _tbl
[46] = { 0xDCD60DCF }
171 _tbl
[47] = { 0xABD13D59 }
172 _tbl
[48] = { 0x26D930AC }
173 _tbl
[49] = { 0x51DE003A }
174 _tbl
[50] = { 0xC8D75180 }
175 _tbl
[51] = { 0xBFD06116 }
176 _tbl
[52] = { 0x21B4F4B5 }
177 _tbl
[53] = { 0x56B3C423 }
178 _tbl
[54] = { 0xCFBA9599 }
179 _tbl
[55] = { 0xB8BDA50F }
180 _tbl
[56] = { 0x2802B89E }
181 _tbl
[57] = { 0x5F058808 }
182 _tbl
[58] = { 0xC60CD9B2 }
183 _tbl
[59] = { 0xB10BE924 }
184 _tbl
[60] = { 0x2F6F7C87 }
185 _tbl
[61] = { 0x58684C11 }
186 _tbl
[62] = { 0xC1611DAB }
187 _tbl
[63] = { 0xB6662D3D }
188 _tbl
[64] = { 0x76DC4190 }
189 _tbl
[65] = { 0x1DB7106 }
190 _tbl
[66] = { 0x98D220BC }
191 _tbl
[67] = { 0xEFD5102A }
192 _tbl
[68] = { 0x71B18589 }
193 _tbl
[69] = { 0x6B6B51F }
194 _tbl
[70] = { 0x9FBFE4A5 }
195 _tbl
[71] = { 0xE8B8D433 }
196 _tbl
[72] = { 0x7807C9A2 }
197 _tbl
[73] = { 0xF00F934 }
198 _tbl
[74] = { 0x9609A88E }
199 _tbl
[75] = { 0xE10E9818 }
200 _tbl
[76] = { 0x7F6A0DBB }
201 _tbl
[77] = { 0x86D3D2D }
202 _tbl
[78] = { 0x91646C97 }
203 _tbl
[79] = { 0xE6635C01 }
204 _tbl
[80] = { 0x6B6B51F4 }
205 _tbl
[81] = { 0x1C6C6162 }
206 _tbl
[82] = { 0x856530D8 }
207 _tbl
[83] = { 0xF262004E }
208 _tbl
[84] = { 0x6C0695ED }
209 _tbl
[85] = { 0x1B01A57B }
210 _tbl
[86] = { 0x8208F4C1 }
211 _tbl
[87] = { 0xF50FC457 }
212 _tbl
[88] = { 0x65B0D9C6 }
213 _tbl
[89] = { 0x12B7E950 }
214 _tbl
[90] = { 0x8BBEB8EA }
215 _tbl
[91] = { 0xFCB9887C }
216 _tbl
[92] = { 0x62DD1DDF }
217 _tbl
[93] = { 0x15DA2D49 }
218 _tbl
[94] = { 0x8CD37CF3 }
219 _tbl
[95] = { 0xFBD44C65 }
220 _tbl
[96] = { 0x4DB26158 }
221 _tbl
[97] = { 0x3AB551CE }
222 _tbl
[98] = { 0xA3BC0074 }
223 _tbl
[99] = { 0xD4BB30E2 }
224 _tbl
[100] = { 0x4ADFA541 }
225 _tbl
[101] = { 0x3DD895D7 }
226 _tbl
[102] = { 0xA4D1C46D }
227 _tbl
[103] = { 0xD3D6F4FB }
228 _tbl
[104] = { 0x4369E96A }
229 _tbl
[105] = { 0x346ED9FC }
230 _tbl
[106] = { 0xAD678846 }
231 _tbl
[107] = { 0xDA60B8D0 }
232 _tbl
[108] = { 0x44042D73 }
233 _tbl
[109] = { 0x33031DE5 }
234 _tbl
[110] = { 0xAA0A4C5F }
235 _tbl
[111] = { 0xDD0D7CC9 }
236 _tbl
[112] = { 0x5005713C }
237 _tbl
[113] = { 0x270241AA }
238 _tbl
[114] = { 0xBE0B1010 }
239 _tbl
[115] = { 0xC90C2086 }
240 _tbl
[116] = { 0x5768B525 }
241 _tbl
[117] = { 0x206F85B3 }
242 _tbl
[118] = { 0xB966D409 }
243 _tbl
[119] = { 0xCE61E49F }
244 _tbl
[120] = { 0x5EDEF90E }
245 _tbl
[121] = { 0x29D9C998 }
246 _tbl
[122] = { 0xB0D09822 }
247 _tbl
[123] = { 0xC7D7A8B4 }
248 _tbl
[124] = { 0x59B33D17 }
249 _tbl
[125] = { 0x2EB40D81 }
250 _tbl
[126] = { 0xB7BD5C3B }
251 _tbl
[127] = { 0xC0BA6CAD }
252 _tbl
[128] = { 0xEDB88320 }
253 _tbl
[129] = { 0x9ABFB3B6 }
254 _tbl
[130] = { 0x3B6E20C }
255 _tbl
[131] = { 0x74B1D29A }
256 _tbl
[132] = { 0xEAD54739 }
257 _tbl
[133] = { 0x9DD277AF }
258 _tbl
[134] = { 0x4DB2615 }
259 _tbl
[135] = { 0x73DC1683 }
260 _tbl
[136] = { 0xE3630B12 }
261 _tbl
[137] = { 0x94643B84 }
262 _tbl
[138] = { 0xD6D6A3E }
263 _tbl
[139] = { 0x7A6A5AA8 }
264 _tbl
[140] = { 0xE40ECF0B }
265 _tbl
[141] = { 0x9309FF9D }
266 _tbl
[142] = { 0xA00AE27 }
267 _tbl
[143] = { 0x7D079EB1 }
268 _tbl
[144] = { 0xF00F9344 }
269 _tbl
[145] = { 0x8708A3D2 }
270 _tbl
[146] = { 0x1E01F268 }
271 _tbl
[147] = { 0x6906C2FE }
272 _tbl
[148] = { 0xF762575D }
273 _tbl
[149] = { 0x806567CB }
274 _tbl
[150] = { 0x196C3671 }
275 _tbl
[151] = { 0x6E6B06E7 }
276 _tbl
[152] = { 0xFED41B76 }
277 _tbl
[153] = { 0x89D32BE0 }
278 _tbl
[154] = { 0x10DA7A5A }
279 _tbl
[155] = { 0x67DD4ACC }
280 _tbl
[156] = { 0xF9B9DF6F }
281 _tbl
[157] = { 0x8EBEEFF9 }
282 _tbl
[158] = { 0x17B7BE43 }
283 _tbl
[159] = { 0x60B08ED5 }
284 _tbl
[160] = { 0xD6D6A3E8 }
285 _tbl
[161] = { 0xA1D1937E }
286 _tbl
[162] = { 0x38D8C2C4 }
287 _tbl
[163] = { 0x4FDFF252 }
288 _tbl
[164] = { 0xD1BB67F1 }
289 _tbl
[165] = { 0xA6BC5767 }
290 _tbl
[166] = { 0x3FB506DD }
291 _tbl
[167] = { 0x48B2364B }
292 _tbl
[168] = { 0xD80D2BDA }
293 _tbl
[169] = { 0xAF0A1B4C }
294 _tbl
[170] = { 0x36034AF6 }
295 _tbl
[171] = { 0x41047A60 }
296 _tbl
[172] = { 0xDF60EFC3 }
297 _tbl
[173] = { 0xA867DF55 }
298 _tbl
[174] = { 0x316E8EEF }
299 _tbl
[175] = { 0x4669BE79 }
300 _tbl
[176] = { 0xCB61B38C }
301 _tbl
[177] = { 0xBC66831A }
302 _tbl
[178] = { 0x256FD2A0 }
303 _tbl
[179] = { 0x5268E236 }
304 _tbl
[180] = { 0xCC0C7795 }
305 _tbl
[181] = { 0xBB0B4703 }
306 _tbl
[182] = { 0x220216B9 }
307 _tbl
[183] = { 0x5505262F }
308 _tbl
[184] = { 0xC5BA3BBE }
309 _tbl
[185] = { 0xB2BD0B28 }
310 _tbl
[186] = { 0x2BB45A92 }
311 _tbl
[187] = { 0x5CB36A04 }
312 _tbl
[188] = { 0xC2D7FFA7 }
313 _tbl
[189] = { 0xB5D0CF31 }
314 _tbl
[190] = { 0x2CD99E8B }
315 _tbl
[191] = { 0x5BDEAE1D }
316 _tbl
[192] = { 0x9B64C2B0 }
317 _tbl
[193] = { 0xEC63F226 }
318 _tbl
[194] = { 0x756AA39C }
319 _tbl
[195] = { 0x26D930A }
320 _tbl
[196] = { 0x9C0906A9 }
321 _tbl
[197] = { 0xEB0E363F }
322 _tbl
[198] = { 0x72076785 }
323 _tbl
[199] = { 0x5005713 }
324 _tbl
[200] = { 0x95BF4A82 }
325 _tbl
[201] = { 0xE2B87A14 }
326 _tbl
[202] = { 0x7BB12BAE }
327 _tbl
[203] = { 0xCB61B38 }
328 _tbl
[204] = { 0x92D28E9B }
329 _tbl
[205] = { 0xE5D5BE0D }
330 _tbl
[206] = { 0x7CDCEFB7 }
331 _tbl
[207] = { 0xBDBDF21 }
332 _tbl
[208] = { 0x86D3D2D4 }
333 _tbl
[209] = { 0xF1D4E242 }
334 _tbl
[210] = { 0x68DDB3F8 }
335 _tbl
[211] = { 0x1FDA836E }
336 _tbl
[212] = { 0x81BE16CD }
337 _tbl
[213] = { 0xF6B9265B }
338 _tbl
[214] = { 0x6FB077E1 }
339 _tbl
[215] = { 0x18B74777 }
340 _tbl
[216] = { 0x88085AE6 }
341 _tbl
[217] = { 0xFF0F6A70 }
342 _tbl
[218] = { 0x66063BCA }
343 _tbl
[219] = { 0x11010B5C }
344 _tbl
[220] = { 0x8F659EFF }
345 _tbl
[221] = { 0xF862AE69 }
346 _tbl
[222] = { 0x616BFFD3 }
347 _tbl
[223] = { 0x166CCF45 }
348 _tbl
[224] = { 0xA00AE278 }
349 _tbl
[225] = { 0xD70DD2EE }
350 _tbl
[226] = { 0x4E048354 }
351 _tbl
[227] = { 0x3903B3C2 }
352 _tbl
[228] = { 0xA7672661 }
353 _tbl
[229] = { 0xD06016F7 }
354 _tbl
[230] = { 0x4969474D }
355 _tbl
[231] = { 0x3E6E77DB }
356 _tbl
[232] = { 0xAED16A4A }
357 _tbl
[233] = { 0xD9D65ADC }
358 _tbl
[234] = { 0x40DF0B66 }
359 _tbl
[235] = { 0x37D83BF0 }
360 _tbl
[236] = { 0xA9BCAE53 }
361 _tbl
[237] = { 0xDEBB9EC5 }
362 _tbl
[238] = { 0x47B2CF7F }
363 _tbl
[239] = { 0x30B5FFE9 }
364 _tbl
[240] = { 0xBDBDF21C }
365 _tbl
[241] = { 0xCABAC28A }
366 _tbl
[242] = { 0x53B39330 }
367 _tbl
[243] = { 0x24B4A3A6 }
368 _tbl
[244] = { 0xBAD03605 }
369 _tbl
[245] = { 0xCDD70693 }
370 _tbl
[246] = { 0x54DE5729 }
371 _tbl
[247] = { 0x23D967BF }
372 _tbl
[248] = { 0xB3667A2E }
373 _tbl
[249] = { 0xC4614AB8 }
374 _tbl
[250] = { 0x5D681B02 }
375 _tbl
[251] = { 0x2A6F2B94 }
376 _tbl
[252] = { 0xB40BBE37 }
377 _tbl
[253] = { 0xC30C8EA1 }
378 _tbl
[254] = { 0x5A05DF1B }
379 _tbl
[255] = { 0x2D02EF8D }
384 for i
,item
in pairs(data
) do
385 local tmp
= band(ret
, 0xFF)
386 local index
= band( bxor(tmp
, item
), 0xFF)
387 ret
= bxor(rsh(ret
,8), _tbl
[index
][1])
393 -- called: data is string, ( >= 24 hex digits )
394 -- returns: string, (data concat new checksum)
395 local function updateChecksum(data
)
396 local part
= data
:sub(1,24)
397 local chksum
= calculateChecksum( utils
.ConvertHexToBytes(part
))
398 return string.format("%s%X", part
, chksum
)
402 local function keygen(uid
)
403 local data
= MIS
..uid
..BAR
404 local hash
= utils
.ConvertAsciiToBytes(utils
.Sha1Hex(data
))
405 return string.format("%02X%02X%02X%02X%02X%02X",
414 --- encode 'table' into a json formatted string
416 local function convert_to_json( obj
)
417 if type(obj
) == "table" then
418 return json
.encode (obj
, { indent
= true })
420 return oops('[fail] input object must be a lua-TABLE')
424 local function save_json(filename
, data
)
425 jsondata
= convert_to_json(data
)
426 filename
= filename
or 'dumpdata.json'
427 local f
= io
.open(filename
, "w")
428 if not f
then return oops(string.format("Could not write to file %s", tostring(filename
))) end
433 --- loads a json formatted text file with
435 -- @param filename the file containing the json-dump (defaults to dumpdata.json)
436 local function load_json(filename
)
437 filename
= filename
or 'dumpdata.json'
438 local f
= io
.open(filename
, "rb")
439 if not f
then return oops(string.format("Could not read file %s", tostring(filename
))) end
442 local t
= f
:read("*all")
445 local obj
, pos
, err
= json
.decode(t
, 1, nil)
446 if err
then return oops(string.format("importing json file failed. %s", err
)) end
448 dbg(string.format('loaded file %s', input
))
451 -- local len, hex = bin.unpack( ("H%d"):format(#t), t)
454 -- Generate encryption key
455 local function create_key(uid
)
457 local sha
= utils
.Sha1Hex( FOO
..BAR
..uid
)
458 sha
= utils
.ConvertBytesToHex( utils
.ConvertAsciiToBytes(sha
:sub(1,16)) )
459 key
= utils
.SwapEndiannessStr( sha
:sub(1,8) , 32 )
460 key
= key
..utils
.SwapEndiannessStr( sha
:sub(9,16), 32 )
461 key
= key
..utils
.SwapEndiannessStr( sha
:sub(17,24), 32 )
462 key
= key
..utils
.SwapEndiannessStr( sha
:sub(25,32), 32 )
466 -- decode response and get the blockdata from a normal mifare read command
467 local function getblockdata(response
)
469 return nil, 'No response from device'
471 if response
.Status
== PM3_SUCCESS
then
474 return nil, "Couldn't read block.. ["..response
.Status
.."]"
478 local function readblock( blockno
, key
)
481 local data
= ('%02x%s%s'):format(blockno
, keytype
, key
)
482 local c
= Command
:newNG
{cmd
= cmds
.CMD_HF_MIFARE_READBL
, data
= data
}
483 local b
, err
= getblockdata(c
:sendNG(false))
484 if not b
then return oops(err
) end
487 --- reads all blocks from tag
489 local function readtag(mfkey
, aeskey
)
493 for blockNo
= 0, numBlocks
-1 do
495 if core
.kbd_enter_pressed() then
496 print("[fail] aborted by user")
500 -- read block from tag.
501 local blockdata
= readblock(blockNo
, mfkey
)
502 if not blockdata
then return oops('[!] failed reading block') end
505 -- the following blocks is NOT encrypted
506 -- block 0 (manufacturing) and 18
507 -- block with all zeros
509 if blockNo
== 0 or blockNo
== 18 then
511 elseif blockNo
%4 ~= 3 then
513 if not string.find(blockdata
, '^0+$') then
515 local decrypted
, err
= core
.aes128_decrypt_ecb(aeskey
, blockdata
)
516 if err
then dbg(err
) end
517 blockdata
= utils
.ConvertAsciiToHex(decrypted
)
521 -- Sectorblocks, not encrypted, but we add our known key to it since it is normally zeros.
522 blockdata
= mfkey
..blockdata
:sub(13,20)..mfkey
524 table.insert(tagdata
, blockdata
)
529 -- simple selftest of functionality
530 local function selftest()
531 local testdata
= '000F42430D0A14000001D11F'..'5D738517'
532 local chksum
= getChecksum(testdata
)
533 local calc
= calculateChecksum( utils
.ConvertHexToBytes(testdata
:sub(1,24)))
534 print ( testdata
:sub(1,24) )
535 print ( ('%x - %x'):format(chksum
, calc
))
537 local isValid
= false
538 local validStr
= "FAIL"
539 if calc
== chksum
then
543 local newtestdata
= updateChecksum(testdata
)
544 local revalidated
= "FAIL"
545 if newtestdata
== testdata
then
548 print ('TESTDATA :: '..testdata
)
549 print ('DATA :: '..testdata
:sub(1,24))
550 print (('VALID CHKSUM :: %s'):format(validStr
))
551 print (('UPDATE CHKSUM :: %s'):format(revalidated
))
553 local testkey
= keygen('0456263a873a80')
554 print ('TEST KEY :: '..testkey
)
555 print ('VALID KEY :: 29564af75805')
557 local function setdevicedebug( status
)
567 -- The main entry point
573 local cmd
, tag, err
, blockNo
, mfkey
574 local shall_validate
= false
575 local shall_dec
= false
576 local shall_enc
= false
581 -- Read the parameters
582 for o
, a
in getopt
.getopt(args
, 'htdevi:') do
583 if o
== 'h' then help() return end
584 if o
== 't' then return selftest() end
585 if o
== 'd' then shall_dec
= true end
586 if o
== 'e' then shall_enc
= true end
587 if o
== 'v' then shall_validate
= true end
588 if o
== 'i' then input
= load_json(a
) end
592 setdevicedebug(false)
595 tag, err
= lib14a
.read(false, true)
600 core
.clearCommandBuffer()
603 if 0x09 ~= tag.sak
then
604 if 0x4400 ~= tag.atqa
then
605 return oops(('[fail] found tag %s :: looking for Mifare Mini 0.3k'):format(tag.name
))
608 dbg ('[ok] found '..tag.name
)
611 mfkey
= keygen(tag.uid
)
612 dbg('[ok] using mf keyA : '.. mfkey
)
615 aeskey
= create_key(tag.uid
)
616 dbg('[ok] using AES key : '.. aeskey
)
618 -- read tag data, complete, enc/dec
619 tagdata
= readtag(mfkey
, aeskey
)
620 dbg('[ok] read card data')
626 res
= save_json(nil, tagdata
)
627 if not res
then return oops('[fail] saving json file') end
629 dbg('[ok] read card data')