2 -- This Lua script is designed to run with Iceman/RRG Proxmark3 fork
3 -- Just copy hf_mf_dump-luxeo.lua to client/luascripts/
4 -- and run "script run hf_mf_dump-luxeo"
7 local cmds
= require('commands')
8 local getopt
= require('getopt')
9 local utils
= require('utils')
10 local lib14a
= require('read14a')
11 local ansicolors
= require('ansicolors')
17 This is a script that tries to dump and decrypt the data of a specific type of Mifare laundromat token.
18 OBS! Tag must be on the antenna.
21 script run hf_mf_dump-luxeo
24 script run hf_mf_dump-luxeo
32 local band
= bit32
.band
34 local bnot
= bit32
.bnot
35 local bxor
= bit32
.bxor
36 local lsh
= bit32
.lshift
37 local rsh
= bit32
.rshift
39 local acgreen
= ansicolors
.bright
..ansicolors
.green
40 local accyan
= ansicolors
.bright
..ansicolors
.cyan
41 local acred
= ansicolors
.red
42 local acyellow
= ansicolors
.bright
..ansicolors
.yellow
43 local acblue
= ansicolors
.bright
..ansicolors
.blue
44 local acmagenta
= ansicolors
.bright
..ansicolors
.magenta
45 local acoff
= ansicolors
.reset
48 -- This is only meant to be used when errors occur
49 local function oops(err
)
51 core
.clearCommandBuffer()
61 print(ansicolors
.cyan
..'Usage'..ansicolors
.reset
)
63 print(ansicolors
.cyan
..'Arguments'..ansicolors
.reset
)
65 print(ansicolors
.cyan
..'Example usage'..ansicolors
.reset
)
70 local function setdevicedebug( status
)
80 local function xteaCrypt(num_rounds
, v
, key
)
83 local delta
= 0x9E3779B9
86 for i
= 0, num_rounds
-1 do
87 -- v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
88 v0
= band(bxor(bxor(lsh(v1
,4), rsh(v1
,5)) + v1
, sum
+ key
[band(sum
,3)]) + v0
, 0xFFFFFFFF)
89 sum
= band(sum
+ delta
, 0xFFFFFFFF)
90 -- v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
91 v1
= band(bxor(bxor(lsh(v0
,4), rsh(v0
,5)) + v0
, sum
+ key
[band(rsh(sum
,11),3)]) + v1
, 0xFFFFFFFF)
97 local function xteaDecrypt(num_rounds
, v
, key
)
100 local delta
= 0x9E3779B9
101 local sum
= band(delta
* num_rounds
, 0xFFFFFFFF)
103 for i
= 0, num_rounds
-1 do
104 -- v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
105 v1
= band(v1
- bxor(bxor(lsh(v0
,4), rsh(v0
,5)) + v0
, sum
+ key
[band(rsh(sum
,11),3)]), 0xFFFFFFFF)
106 sum
= band(sum
- delta
, 0xFFFFFFFF)
107 -- v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
108 v0
= band(v0
- bxor(bxor(lsh(v1
,4), rsh(v1
,5)) + v1
, sum
+ key
[band(sum
,3)]), 0xFFFFFFFF)
114 local function createxteakey(mfuid
)
120 -- Warning ! "it is customary in Lua to START ARRAYS WITH ONE"
121 buid
= utils
.ConvertHexToBytes(mfuid
)
122 uid
[0] = bor(buid
[1], lsh(buid
[2], 8))
123 uid
[1] = bor(buid
[3], lsh(buid
[4], 8))
130 tmpkey
[5] = bxor(uid
[0], 0xA312)
132 tmpkey
[7] = bxor(uid
[1], 0x55AA)
134 xteakey
[0] = bor(lsh(tmpkey
[1], 16), tmpkey
[0])
135 xteakey
[1] = bor(lsh(tmpkey
[3], 16), tmpkey
[2])
136 xteakey
[2] = bor(lsh(tmpkey
[5], 16), tmpkey
[4])
137 xteakey
[3] = bor(lsh(tmpkey
[7], 16), tmpkey
[6])
142 local function getblockdata(response
)
144 return nil, 'No response from device'
146 if response
.Status
== PM3_SUCCESS
then
149 return nil, "Couldn't read block.. ["..response
.Status
.."]"
153 local function readblock(blockno
, key
)
155 local keytype
= '01' -- key B
156 local data
= ('%02x%s%s'):format(blockno
, keytype
, key
)
157 local c
= Command
:newNG
{cmd
= cmds
.CMD_HF_MIFARE_READBL
, data
= data
}
158 local b
, err
= getblockdata(c
:sendNG(false))
159 if not b
then return oops(err
) end
163 local function readtag(mfkey
,xteakey
)
169 -- Read 4 sectors and build table
171 for blockn
= sect
* 4, (sect
* 4) + 2 do
172 local blockdata
= readblock(blockn
, mfkey
)
173 if not blockdata
then return oops('[!] failed reading block') end
174 table.insert(tagdata
, blockdata
)
178 -- Decrypt data and build clear table
179 for key
,value
in ipairs(tagdata
) do
181 v
[0] = utils
.SwapEndianness(value
:sub(1, 8), 32)
182 v
[1] = utils
.SwapEndianness(value
:sub(9, 16), 32)
183 xteaDecrypt(16, v
, xteakey
)
184 vv
[0] = utils
.SwapEndianness(value
:sub(17, 24), 32)
185 vv
[1] = utils
.SwapEndianness(value
:sub(25, 32), 32)
186 xteaDecrypt(16, vv
, xteakey
)
187 clearblockdata
=string.format("%08X%08X%08X%08X",
188 utils
.SwapEndianness(string.format("%08X", v
[0]), 32),
189 utils
.SwapEndianness(string.format("%08X", v
[1]), 32),
190 utils
.SwapEndianness(string.format("%08X", vv
[0]), 32),
191 utils
.SwapEndianness(string.format("%08X", vv
[1]), 32))
192 table.insert(cleardata
, clearblockdata
)
195 return tagdata
,cleardata
200 local function main(args
)
202 -- Arguments for the script
203 for o
, a
in getopt
.getopt(args
, 'h') do
204 if o
== 'h' then return help() end
213 setdevicedebug(false)
216 tag, err
= lib14a
.read(false, true)
221 core
.clearCommandBuffer()
224 if 0x08 ~= tag.sak
then
225 if 0x0400 ~= tag.atqa
then
226 return oops(('[fail] found tag %s :: looking for Mifare S50 1k'):format(tag.name
))
230 xteakey
= createxteakey(tag.uid
)
231 print(acblue
.."UID: "..tag.uid
..acoff
)
232 print(acblue
..string.format("XTEA key: %08X %08X %08X %08X", xteakey
[0], xteakey
[1], xteakey
[2], xteakey
[3])..acoff
)
234 edata
, cdata
= readtag("415A54454B4D", xteakey
)
236 if edata
== nil or cdata
== nil then
237 print("ERROR Reading tag!")
241 print("Ciphered data:")
242 for key
,value
in ipairs(edata
) do
244 if key
% 3 == 0 then print("") end
247 -- compute CRC for each segment
248 crcH
= utils
.SwapEndianness(core
.reveng_runmodel("CRC-16/ARC", cdata
[1]..cdata
[2]..cdata
[3]:sub(1,28), false, '0'),16)
249 crcA
= utils
.SwapEndianness(core
.reveng_runmodel("CRC-16/ARC", cdata
[4]..cdata
[5]..cdata
[6]..cdata
[7]:sub(1,28), false, '0'),16)
250 crcB
= utils
.SwapEndianness(core
.reveng_runmodel("CRC-16/ARC", cdata
[8]..cdata
[9]..cdata
[10]..cdata
[11]:sub(1,28), false, '0'),16)
253 for key
,value
in ipairs(cdata
) do
255 print(value
:sub(1,28)..acmagenta
..value
:sub(29,32)..acoff
)
256 if utils
.SwapEndianness(value
:sub(29,32),16) == crcH
then strcrc
= " OK" else strcrc
= acred
.." CRCERROR !!" end
257 print(acmagenta
.."CRC16/ARC = "..string.format("0x%04X", crcH
)..strcrc
..acoff
)
260 print(acgreen
..value
:sub(1,4)..acoff
..value
:sub(5,16)..accyan
..value
:sub(17,24)..acoff
..value
:sub(25,26)..accyan
..value
:sub(27,28)..acoff
..value
:sub(29,32))
261 versionA
= utils
.SwapEndianness(value
:sub(1,4),16)
262 dateA
= string.format("%d/%02d/%02d %02d:%02d", tonumber(value
:sub(17,18),10)+2000, tonumber(value
:sub(19,20),10),
263 tonumber(string.format("%02X", band(tonumber(value
:sub(21,22),16),0x3f)),10),
264 tonumber(value
:sub(23,24),10), tonumber(value
:sub(27,28),10))
266 print(acgreen
..value
:sub(1,4)..acoff
..value
:sub(5,16)..accyan
..value
:sub(17,24)..acoff
..value
:sub(25,26)..accyan
..value
:sub(27,28)..acoff
..value
:sub(29,32))
267 versionB
= utils
.SwapEndianness(value
:sub(1,4),16)
268 dateB
= string.format("%d/%02d/%02d %02d:%02d", tonumber(value
:sub(17,18),10)+2000, tonumber(value
:sub(19,20),10),
269 tonumber(string.format("%02X", band(tonumber(value
:sub(21,22),16),0x3f)),10),
270 tonumber(value
:sub(23,24),10), tonumber(value
:sub(27,28),10))
272 print(acyellow
..value
:sub(1,4)..acoff
..value
:sub(5,32))
273 creditA
= utils
.SwapEndianness(value
:sub(1,4),16)/100
275 print(acyellow
..value
:sub(1,4)..acoff
..value
:sub(5,32))
276 creditB
= utils
.SwapEndianness(value
:sub(1,4),16)/100
278 print(value
:sub(1,28)..acmagenta
..value
:sub(29,32)..acoff
)
279 print(acgreen
.."Version "..string.format("0x%04X", versionA
)..acoff
)
280 print(acyellow
.."Credit : "..creditA
..acoff
)
281 if utils
.SwapEndianness(value
:sub(29,32),16) == crcA
then strcrc
= " OK" else strcrc
= acred
.." CRCERROR !!" end
282 print(acmagenta
.."CRC16/ARC = "..string.format("0x%04X", crcA
)..strcrc
..acoff
)
283 print(accyan
.."Date: "..dateA
..acoff
)
285 elseif key
== 11 then
286 print(value
:sub(1,28)..acmagenta
..value
:sub(29,32)..acoff
)
287 print(acgreen
.."Version "..string.format("0x%04X", versionB
)..acoff
)
288 print(acyellow
.."Credit : "..creditB
..acoff
)
289 if utils
.SwapEndianness(value
:sub(29,32),16) == crcB
then strcrc
= " OK" else strcrc
= acred
.." CRCERROR !!" end
290 print(acmagenta
.."CRC16/ARC = "..string.format("0x%04X", crcB
)..strcrc
..acoff
)
291 print(accyan
.."Date: "..dateB
..acoff
)