1 local cmds
= require('commands')
2 local getopt
= require('getopt')
3 local lib14a
= require('read14a')
4 local utils
= require('utils')
5 local ansicolors
= require('ansicolors')
8 local DEBUG
= false -- the debug flag
9 local bxor
= bit32
.bxor
11 local err_lock
= 'use -k or change cfg0 block'
13 copyright
= 'Copyright (c) 2017 IceSQL AB. All rights reserved.'
14 author
= 'Christian Herrmann'
16 desc
= 'This script enables easy programming of a MAGIC NTAG 21* card'
18 -- read magic tag configuration
19 ]]..ansicolors
.yellow
..[[script run hf_mfu_magicwrite -c ]]..ansicolors
.reset
..[[
22 ]]..ansicolors
.yellow
..[[script run hf_mfu_magicwrite -u 04112233445566 ]]..ansicolors
.reset
..[[
25 ]]..ansicolors
.yellow
..[[script run hf_mfu_magicwrite -p 11223344 -a 8080 ]]..ansicolors
.reset
..[[
27 -- set version to NTAG213
28 ]]..ansicolors
.yellow
..[[script run hf_mfu_magicwrite -v 0004040201000f03 ]]..ansicolors
.reset
..[[
31 ]]..ansicolors
.yellow
..[[script run hf_mfu_magicwrite -s 1122334455667788990011223344556677889900112233445566778899001122 ]]..ansicolors
.reset
..[[
34 ]]..ansicolors
.yellow
..[[script run hf_mfu_magicwrite -w ]]..ansicolors
.reset
..[[
36 -- wipe a locked down tag by giving the password
37 ]]..ansicolors
.yellow
..[[script run hf_mfu_magicwrite -k ffffffff -w ]]..ansicolors
.reset
..[[
41 script run hf_mfu_easywrite -h -k <passwd> -c -w -u <uid> -t <type> -p <passwd> -a <pack> -s <signature> -o <otp> -v <version>
45 -c read magic configuration
46 -u UID (14 hexsymbols), set UID on tag
47 -t tag type to impersonate
61 -p password (8 hexsymbols), set password on tag.
62 -a pack ( 4 hexsymbols), set pack on tag.
63 -s signature data (64 hexsymbols), set signature data on tag.
64 -o OTP data (8 hexsymbols), set `One-Time Programmable` data on tag.
65 -v version data (16 hexsymbols), set version data on tag.
66 -w wipe tag. You can specify password if the tag has been locked down. Fills tag with zeros and put default values for NTAG213 (like -t 5)
67 -k pwd to use with the wipe option
70 -- A debug printout-function
71 local function dbg(args
)
72 if not DEBUG
then return end
73 if type(args
) == 'table' then
83 -- This is only meant to be used when errors occur
84 local function oops(err
)
86 core
.clearCommandBuffer()
96 print(ansicolors
.cyan
..'Usage'..ansicolors
.reset
)
98 print(ansicolors
.cyan
..'Arguments'..ansicolors
.reset
)
100 print(ansicolors
.cyan
..'Example usage'..ansicolors
.reset
)
104 -- set the global password variable
105 local function set_password(pwd
)
106 if pwd
== nil then _password
= nil; return true, 'Ok' end
107 if #pwd
~= 8 then return nil, 'password wrong length. Must be 4 hex bytes' end
108 if #pwd
== 0 then _password
= nil end
112 --- Picks out and displays the data read from a tag
113 -- Specifically, takes a usb packet, converts to a Command
114 -- (as in commands.lua), takes the data-array and
115 -- reads the number of bytes specified in arg1 (arg0 in c-struct)
116 -- @param usbpacket the data received from the device
117 local function getResponseData(usbpacket
)
118 local resp
= Command
.parse(usbpacket
)
119 local len
= tonumber(resp
.arg1
) * 2
120 return string.sub(tostring(resp
.data
), 0, len
);
124 local function sendRaw(rawdata
, options
)
126 local flags
= lib14a
.ISO14A_COMMAND
.ISO14A_NO_DISCONNECT
127 + lib14a
.ISO14A_COMMAND
.ISO14A_RAW
128 + lib14a
.ISO14A_COMMAND
.ISO14A_APPEND_CRC
130 local c
= Command
:newMIX
{cmd
= cmds
.CMD_HF_ISO14443A_READER
,
132 -- arg2 contains the length, which is half the length of the ASCII-string rawdata
133 arg2
= string.len(rawdata
)/2,
136 return c
:sendMIX(options
.ignore_response
)
140 local function send(payload
)
141 local usb
, err
= sendRaw(payload
,{ignore_response
= false})
142 if err
then return oops(err
) end
143 return getResponseData(usb
)
146 -- select tag and if password is set, authenticate
147 local function connect()
148 core
.clearCommandBuffer()
150 -- First of all, connect
151 info
, err
= lib14a
.read(true, true)
156 core
.clearCommandBuffer()
158 --authenticate if needed using global variable
160 send('1B'.._password
)
165 -- Read magic configuration
166 local function read_config()
167 local info
= connect()
168 if not info
then return false, "Can't select card" end
171 local pwd
= send("30F0"):sub(1,8)
173 -- 04 response indicates that blocks has been locked down.
174 if pwd
== '04' then lib14a
.disconnect(); return nil, "can't read configuration, "..err_lock
end
177 local pack
= send("30F1"):sub(1,4)
180 local signature1
= send('30F2'):sub(1,32)
181 local signature2
= send('30F6'):sub(1,32)
184 local version
= send('30FA'):sub(1,16)
186 local cardtype
= send('30FC'):sub(1,2)
189 if cardtype
== '00' then typestr
= 'NTAG 213'
190 elseif cardtype
== '01' then typestr
= 'NTAG 215'
191 elseif cardtype
== '02' then typestr
= 'NTAG 216'
194 print('Magic NTAG 21* Configuration')
195 print(' - Type ', typestr
, '(genuine cardtype)')
196 print(' - Password', pwd
)
197 print(' - Pack ', pack
)
198 print(' - Version ', version
)
199 print(' - Signature', signature1
..signature2
)
205 -- Write SIGNATURE data
206 local function write_signature(data
)
209 if data
== nil then return nil, 'empty data string' end
210 if #data
== 0 then return nil, 'empty data string' end
211 if #data
~= 64 then return nil, 'data wrong length. Should be 32 hex bytes' end
213 local info
= connect()
214 if not info
then return false, "Can't select card" end
216 print('Writing new signature')
219 local cmd
= 'A2F%d%s'
221 for i
= 1, #data
, 8 do
225 if resp
== '04' then lib14a
.disconnect(); return nil, 'Failed to write signature' end
233 local function write_pwd(pwd
)
235 if pwd
== nil then return nil, 'empty PWD string' end
236 if #pwd
== 0 then return nil, 'empty PWD string' end
237 if #pwd
~= 8 then return nil, 'PWD wrong length. Should be 4 hex bytes' end
239 local info
= connect()
240 if not info
then return false, "Can't select card" end
242 print('Writing new PWD ', pwd
)
244 local resp
= send('A2F0'..pwd
)
247 return nil, 'Failed to write password'
254 local function write_pack(pack
)
255 -- PACK string checks
256 if pack
== nil then return nil, 'empty PACK string' end
257 if #pack
== 0 then return nil, 'empty PACK string' end
258 if #pack
~= 4 then return nil, 'PACK wrong length. Should be 4 hex bytes' end
260 local info
= connect()
261 if not info
then return false, "Can't select card" end
263 print('Writing new PACK', pack
)
265 local resp
= send('A2F1'..pack
..'0000')
268 return nil, 'Failed to write pack'
275 local function write_otp(block3
)
278 if block3
== nil then return nil, 'empty OTP string' end
279 if #block3
== 0 then return nil, 'empty OTP string' end
280 if #block3
~= 8 then return nil, 'OTP wrong length. Should be 4 hex bytes' end
282 local info
= connect()
283 if not info
then return false, "Can't select card" end
285 print('Writing new OTP ', block3
)
287 local resp
= send('A203'..block3
)
290 return nil, 'Failed to write OTP'
296 -- Writes a UID with bcc1, bcc2. Needs a magic tag.
297 local function write_uid(uid
)
299 if uid
== nil then return nil, 'empty uid string' end
300 if #uid
== 0 then return nil, 'empty uid string' end
301 if #uid
~= 14 then return nil, 'uid wrong length. Should be 7 hex bytes' end
303 local info
= connect()
304 if not info
then return false, "Can't select card" end
306 print('Writing new UID ', uid
)
308 local uidbytes
= utils
.ConvertHexToBytes(uid
)
309 local bcc1
= bxor(bxor(bxor(uidbytes
[1], uidbytes
[2]), uidbytes
[3]), 0x88)
310 local bcc2
= bxor(bxor(bxor(uidbytes
[4], uidbytes
[5]), uidbytes
[6]), uidbytes
[7])
311 local block0
= string.format('%02X%02X%02X%02X', uidbytes
[1], uidbytes
[2], uidbytes
[3], bcc1
)
312 local block1
= string.format('%02X%02X%02X%02X', uidbytes
[4], uidbytes
[5], uidbytes
[6], uidbytes
[7])
313 local block2
= string.format('%02X%02X%02X%02X', bcc2
, 0x48, 0x00, 0x00)
316 resp
= send('A200'..block0
)
317 resp
= send('A201'..block1
)
318 resp
= send('A202'..block2
)
322 return nil, 'Failed to write new uid'
328 -- Write VERSION data,
329 -- make sure you have correct version data
330 local function write_version(data
)
331 -- version string checks
332 if data
== nil then return nil, 'empty version string' end
333 if #data
== 0 then return nil, 'empty version string' end
334 if #data
~= 16 then return nil, 'version wrong length. Should be 8 hex bytes' end
336 local info
= connect()
337 if not info
then return false, "Can't select card" end
339 print('Writing new version', data
)
341 local b1
= data
:sub(1,8)
342 local b2
= data
:sub(9,16)
344 resp
= send('A2FA'..b1
)
345 resp
= send('A2FB'..b2
)
348 return nil, 'Failed to write version'
354 -- writen TYPE which card is based on.
355 -- 00 = 213, 01 = 215, 02 = 216
356 local function write_type(data
)
357 -- type string checks
358 if data
== nil then return nil, 'empty type string' end
359 if #data
== 0 then return nil, 'empty type string' end
360 if #data
~= 2 then return nil, 'type wrong length. Should be 1 hex byte' end
362 local info
= connect()
363 if not info
then return false, "Can't select card" end
364 print('Writing new type', data
)
366 local resp
= send('A2FC'..data
..'000000')
369 return nil, 'Failed to write type'
375 -- Set tag type. Predefinde version data together with magic type set.
376 -- Since cmd always gives 10 bytes len (data+crc) we can impersonate the following types
377 -- we only truely be three types NTAG 213,215 and 216
378 local function set_type(tagtype
)
381 if type(tagtype
) == 'string' then tagtype
= tonumber(tagtype
, 10) end
382 if tagtype
== nil then return nil, 'empty tagtype' end
385 print('Setting: UL-EV1 48')
386 write_otp('00000000') -- Setting OTP to default 00 00 00 00
387 write_version('0004030101000b03') -- UL-EV1 (48) 00 04 03 01 01 00 0b 03
388 write_type('00') -- based on NTAG213..
390 -- Setting UL-Ev1 default config bl 16,17
395 elseif tagtype
== 2 then
396 print('Setting: UL-EV1 128')
397 write_otp('00000000') -- Setting OTP to default 00 00 00 00
398 write_version('0004030101000e03') -- UL-EV1 (128) 00 04 03 01 01 00 0e 03
401 -- Setting UL-Ev1 default config bl 37,38
405 elseif tagtype
== 3 then
406 print('Setting: NTAG 210')
407 write_version('0004040101000b03') -- NTAG210 00 04 04 01 01 00 0b 03
410 -- Setting NTAG210 default CC block456
418 elseif tagtype
== 4 then
419 print('Setting: NTAG 212')
420 write_version('0004040101000E03') -- NTAG212 00 04 04 01 01 00 0E 03
423 -- Setting NTAG212 default CC block456
431 elseif tagtype
== 5 then
432 print('Setting: NTAG 213')
433 write_version('0004040201000F03') -- NTAG213 00 04 04 02 01 00 0f 03
436 -- Setting NTAG213 default CC block456
444 elseif tagtype
== 6 then
445 print('Setting: NTAG 215')
446 write_version('0004040201001103') -- NTAG215 00 04 04 02 01 00 11 03
449 -- Setting NTAG215 default CC block456
457 elseif tagtype
== 7 then
458 print('Setting: NTAG 216')
459 write_version('0004040201001303') -- NTAG216 00 04 04 02 01 00 13 03
462 -- Setting NTAG216 default CC block456
470 elseif tagtype
== 8 then
471 print('Setting: NTAG I2C 1K')
472 write_version('0004040502011303') -- NTAG_I2C_1K 00 04 04 05 02 01 13 03
475 -- Setting NTAG I2C 1K default CC block456
480 elseif tagtype
== 9 then
481 print('Setting: NTAG I2C 2K')
482 write_version('0004040502011503') -- NTAG_I2C_2K 00 04 04 05 02 01 15 03
485 -- Setting NTAG I2C 2K default CC block456
490 elseif tagtype
== 10 then
491 print('Setting: NTAG I2C plus 1K')
492 write_version('0004040502021303') -- NTAG_I2C_1K 00 04 04 05 02 02 13 03
495 -- Setting NTAG I2C 1K default CC block456
500 elseif tagtype
== 11 then
501 print('Setting: NTAG I2C plus 2K')
502 write_version('0004040502021503') -- NTAG_I2C_2K 00 04 04 05 02 02 15 03
505 -- Setting NTAG I2C 2K default CC block456
510 elseif tagtype
== 12 then
511 print('Setting: NTAG 213F')
512 write_version('0004040401000F03') -- NTAG213F 00 04 04 04 01 00 0f 03
515 -- Setting NTAG213 default CC block456
523 elseif tagtype
== 13 then
524 print('Setting: NTAG 216F')
525 write_version('0004040401001303') -- NTAG216F 00 04 04 04 01 00 13 03
528 -- Setting NTAG216 default CC block456
540 return nil, 'Failed to set type'
547 local function wipe()
549 local info
= connect()
550 if not info
then return false, "Can't select card" end
553 local cmd_empty
= 'A2%02X00000000'
554 local cmd_cfg1
= 'A2%02X000000FF'
555 local cmd_cfg2
= 'A2%02X00050000'
560 --configuration block 0
561 if b
== 0x29 or b
== 0x83 or b
== 0xe3 then
562 local cmd
= (cmd_cfg1
):format(b
)
564 --configuration block 1
565 elseif b
== 0x2a or b
== 0x84 or b
== 0xe4 then
566 local cmd
= (cmd_cfg2
):format(b
)
569 resp
= send(cmd_empty
:format(b
))
571 if resp
== '04' or #resp
== 0 then
572 io
.write('\nwrote block '..b
, ' failed\n')
583 if err
then return nil, "Tag locked down, "..err_lock
end
585 print('setting default values...')
589 -- set NTAG213 default values
590 err
, msg
= set_type(5)
591 if err
== nil then return err
, msg
end
594 err
, msg
= write_uid('04112233445566')
595 if err
== nil then return err
, msg
end
598 err
, msg
= write_pwd('FFFFFFFF')
599 if err
== nil then return err
, msg
end
602 err
, msg
= write_pack('0000')
603 if err
== nil then return err
, msg
end
608 -- The main entry point
611 print( string.rep('--',20) )
612 print( string.rep('--',20) )
617 if #args
== 0 then return help() end
619 -- Read the parameters
620 for o
, a
in getopt
.getopt(args
, 'hck:u:t:p:a:s:o:v:w') do
623 if o
== "h" then return help() end
626 if o
== 'k' then err
, msg
= set_password(a
) end
629 if o
== "c" then err
, msg
= read_config() end
632 if o
== "w" then err
, msg
= wipe() end
635 if o
== "u" then err
, msg
= write_uid(a
) end
637 -- write type/version
638 if o
== "t" then err
, msg
= set_type(a
) end
641 if o
== "p" then err
, msg
= write_pwd(a
) end
644 if o
== "a" then err
, msg
= write_pack(a
) end
647 if o
== "s" then err
, msg
= write_signature(a
) end
650 if o
== "o" then err
, msg
= write_otp(a
) end
653 if o
== "v" then err
, msg
= write_version(a
) end
655 if err
== nil then return oops(msg
) end