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_magicwrite -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 local versionstr
= 'unknown'
195 if version
== '0004030101000B03' then versionstr
= 'UL EV1 48b'
196 elseif version
== '0004030101000E03' then versionstr
= 'UL EV1 128b'
197 elseif version
== '0004040101000B03' then versionstr
= 'NTAG 210'
198 elseif version
== '0004040101000E03' then versionstr
= 'NTAG 212'
199 elseif version
== '0004040201000F03' then versionstr
= 'NTAG 213'
200 elseif version
== '0004040201001103' then versionstr
= 'NTAG 215'
201 elseif version
== '0004040201001303' then versionstr
= 'NTAG 216'
202 elseif version
== '0004040502011303' then versionstr
= 'NTAG I2C 1K'
203 elseif version
== '0004040502011503' then versionstr
= 'NTAG I2C 2K'
204 elseif version
== '0004040502021303' then versionstr
= 'NTAG I2C 1K PLUS'
205 elseif version
== '0004040502021503' then versionstr
= 'NTAG I2C 2K PLUS'
206 elseif version
== '0004040401000F03' then versionstr
= 'NTAG 213F'
207 elseif version
== '0004040401001303' then versionstr
= 'NTAG 216F'
210 print('Magic NTAG 21* Configuration')
211 print(' - Type ', typestr
, '(genuine cardtype)')
212 print(' - Password', pwd
)
213 print(' - Pack ', pack
)
214 print(' - Version ', version
, '(' .. versionstr
.. ')')
215 print(' - Signature', signature1
..signature2
)
221 -- Write SIGNATURE data
222 local function write_signature(data
)
225 if data
== nil then return nil, 'empty data string' end
226 if #data
== 0 then return nil, 'empty data string' end
227 if #data
~= 64 then return nil, 'data wrong length. Should be 32 hex bytes' end
229 local info
= connect()
230 if not info
then return false, "Can't select card" end
232 print('Writing new signature')
235 local cmd
= 'A2F%d%s'
237 for i
= 1, #data
, 8 do
241 if resp
== '04' then lib14a
.disconnect(); return nil, 'Failed to write signature' end
249 local function write_pwd(pwd
)
251 if pwd
== nil then return nil, 'empty PWD string' end
252 if #pwd
== 0 then return nil, 'empty PWD string' end
253 if #pwd
~= 8 then return nil, 'PWD wrong length. Should be 4 hex bytes' end
255 local info
= connect()
256 if not info
then return false, "Can't select card" end
258 print('Writing new PWD ', pwd
)
260 local resp
= send('A2F0'..pwd
)
263 return nil, 'Failed to write password'
270 local function write_pack(pack
)
271 -- PACK string checks
272 if pack
== nil then return nil, 'empty PACK string' end
273 if #pack
== 0 then return nil, 'empty PACK string' end
274 if #pack
~= 4 then return nil, 'PACK wrong length. Should be 4 hex bytes' end
276 local info
= connect()
277 if not info
then return false, "Can't select card" end
279 print('Writing new PACK', pack
)
281 local resp
= send('A2F1'..pack
..'0000')
284 return nil, 'Failed to write pack'
291 local function write_otp(block3
)
294 if block3
== nil then return nil, 'empty OTP string' end
295 if #block3
== 0 then return nil, 'empty OTP string' end
296 if #block3
~= 8 then return nil, 'OTP wrong length. Should be 4 hex bytes' end
298 local info
= connect()
299 if not info
then return false, "Can't select card" end
301 print('Writing new OTP ', block3
)
303 local resp
= send('A203'..block3
)
306 return nil, 'Failed to write OTP'
312 -- Writes a UID with bcc1, bcc2. Needs a magic tag.
313 local function write_uid(uid
)
315 if uid
== nil then return nil, 'empty uid string' end
316 if #uid
== 0 then return nil, 'empty uid string' end
317 if #uid
~= 14 then return nil, 'uid wrong length. Should be 7 hex bytes' end
319 local info
= connect()
320 if not info
then return false, "Can't select card" end
322 print('Writing new UID ', uid
)
324 local uidbytes
= utils
.ConvertHexToBytes(uid
)
325 local bcc1
= bxor(bxor(bxor(uidbytes
[1], uidbytes
[2]), uidbytes
[3]), 0x88)
326 local bcc2
= bxor(bxor(bxor(uidbytes
[4], uidbytes
[5]), uidbytes
[6]), uidbytes
[7])
327 local block0
= string.format('%02X%02X%02X%02X', uidbytes
[1], uidbytes
[2], uidbytes
[3], bcc1
)
328 local block1
= string.format('%02X%02X%02X%02X', uidbytes
[4], uidbytes
[5], uidbytes
[6], uidbytes
[7])
329 local block2
= string.format('%02X%02X%02X%02X', bcc2
, 0x48, 0x00, 0x00)
332 resp
= send('A200'..block0
)
333 resp
= send('A201'..block1
)
334 resp
= send('A202'..block2
)
338 return nil, 'Failed to write new uid'
344 -- Write VERSION data,
345 -- make sure you have correct version data
346 local function write_version(data
)
347 -- version string checks
348 if data
== nil then return nil, 'empty version string' end
349 if #data
== 0 then return nil, 'empty version string' end
350 if #data
~= 16 then return nil, 'version wrong length. Should be 8 hex bytes' end
352 local info
= connect()
353 if not info
then return false, "Can't select card" end
355 print('Writing new version', data
)
357 local b1
= data
:sub(1,8)
358 local b2
= data
:sub(9,16)
360 resp
= send('A2FA'..b1
)
361 resp
= send('A2FB'..b2
)
364 return nil, 'Failed to write version'
370 -- write TYPE which card is based on.
371 -- 00 = 213, 01 = 215, 02 = 216
372 local function write_type(data
)
373 -- type string checks
374 if data
== nil then return nil, 'empty type string' end
375 if #data
== 0 then return nil, 'empty type string' end
376 if #data
~= 2 then return nil, 'type wrong length. Should be 1 hex byte' end
378 local info
= connect()
379 if not info
then return false, "Can't select card" end
380 print('Writing new type', data
)
382 local resp
= send('A2FC'..data
..'000000')
385 return nil, 'Failed to write type'
391 -- Set tag type. Predefinde version data together with magic type set.
392 -- Since cmd always gives 10 bytes len (data+crc) we can impersonate the following types
393 -- we only truly be three types NTAG 213,215 and 216
394 local function set_type(tagtype
)
397 if type(tagtype
) == 'string' then tagtype
= tonumber(tagtype
, 10) end
398 if tagtype
== nil then return nil, 'empty tagtype' end
401 print('Setting: UL-EV1 48')
402 write_otp('00000000') -- Setting OTP to default 00 00 00 00
403 write_version('0004030101000b03') -- UL-EV1 (48) 00 04 03 01 01 00 0b 03
404 write_type('00') -- based on NTAG213..
406 -- Setting UL-Ev1 default config bl 16,17
411 elseif tagtype
== 2 then
412 print('Setting: UL-EV1 128')
413 write_otp('00000000') -- Setting OTP to default 00 00 00 00
414 write_version('0004030101000e03') -- UL-EV1 (128) 00 04 03 01 01 00 0e 03
417 -- Setting UL-Ev1 default config bl 37,38
421 elseif tagtype
== 3 then
422 print('Setting: NTAG 210')
423 write_version('0004040101000b03') -- NTAG210 00 04 04 01 01 00 0b 03
426 -- Setting NTAG210 default CC block456
434 elseif tagtype
== 4 then
435 print('Setting: NTAG 212')
436 write_version('0004040101000E03') -- NTAG212 00 04 04 01 01 00 0E 03
439 -- Setting NTAG212 default CC block456
447 elseif tagtype
== 5 then
448 print('Setting: NTAG 213')
449 write_version('0004040201000F03') -- NTAG213 00 04 04 02 01 00 0f 03
452 -- Setting NTAG213 default CC block456
460 elseif tagtype
== 6 then
461 print('Setting: NTAG 215')
462 write_version('0004040201001103') -- NTAG215 00 04 04 02 01 00 11 03
465 -- Setting NTAG215 default CC block456
473 elseif tagtype
== 7 then
474 print('Setting: NTAG 216')
475 write_version('0004040201001303') -- NTAG216 00 04 04 02 01 00 13 03
478 -- Setting NTAG216 default CC block456
486 elseif tagtype
== 8 then
487 print('Setting: NTAG I2C 1K')
488 write_version('0004040502011303') -- NTAG_I2C_1K 00 04 04 05 02 01 13 03
491 -- Setting NTAG I2C 1K default CC block456
496 elseif tagtype
== 9 then
497 print('Setting: NTAG I2C 2K')
498 write_version('0004040502011503') -- NTAG_I2C_2K 00 04 04 05 02 01 15 03
501 -- Setting NTAG I2C 2K default CC block456
506 elseif tagtype
== 10 then
507 print('Setting: NTAG I2C plus 1K')
508 write_version('0004040502021303') -- NTAG_I2C_1K 00 04 04 05 02 02 13 03
511 -- Setting NTAG I2C 1K default CC block456
516 elseif tagtype
== 11 then
517 print('Setting: NTAG I2C plus 2K')
518 write_version('0004040502021503') -- NTAG_I2C_2K 00 04 04 05 02 02 15 03
521 -- Setting NTAG I2C 2K default CC block456
526 elseif tagtype
== 12 then
527 print('Setting: NTAG 213F')
528 write_version('0004040401000F03') -- NTAG213F 00 04 04 04 01 00 0f 03
531 -- Setting NTAG213 default CC block456
539 elseif tagtype
== 13 then
540 print('Setting: NTAG 216F')
541 write_version('0004040401001303') -- NTAG216F 00 04 04 04 01 00 13 03
544 -- Setting NTAG216 default CC block456
556 return nil, 'Failed to set type'
563 local function wipe()
565 local info
= connect()
566 if not info
then return false, "Can't select card" end
569 local cmd_empty
= 'A2%02X00000000'
570 local cmd_cfg1
= 'A2%02X000000FF'
571 local cmd_cfg2
= 'A2%02X00050000'
576 --configuration block 0
577 if b
== 0x29 or b
== 0x83 or b
== 0xe3 then
578 local cmd
= (cmd_cfg1
):format(b
)
580 --configuration block 1
581 elseif b
== 0x2a or b
== 0x84 or b
== 0xe4 then
582 local cmd
= (cmd_cfg2
):format(b
)
585 resp
= send(cmd_empty
:format(b
))
587 if resp
== '04' or #resp
== 0 then
588 io
.write('\nwrote block '..b
, ' failed\n')
599 if err
then return nil, "Tag locked down, "..err_lock
end
601 print('setting default values...')
605 -- set NTAG213 default values
606 err
, msg
= set_type(5)
607 if err
== nil then return err
, msg
end
610 err
, msg
= write_uid('04112233445566')
611 if err
== nil then return err
, msg
end
614 err
, msg
= write_pwd('FFFFFFFF')
615 if err
== nil then return err
, msg
end
618 err
, msg
= write_pack('0000')
619 if err
== nil then return err
, msg
end
624 -- The main entry point
627 print( string.rep('--',20) )
628 print( string.rep('--',20) )
633 if #args
== 0 then return help() end
635 -- Read the parameters
636 for o
, a
in getopt
.getopt(args
, 'hck:u:t:p:a:s:o:v:w') do
639 if o
== "h" then return help() end
642 if o
== 'k' then err
, msg
= set_password(a
) end
645 if o
== "c" then err
, msg
= read_config() end
648 if o
== "w" then err
, msg
= wipe() end
651 if o
== "u" then err
, msg
= write_uid(a
) end
653 -- write type/version
654 if o
== "t" then err
, msg
= set_type(a
) end
657 if o
== "p" then err
, msg
= write_pwd(a
) end
660 if o
== "a" then err
, msg
= write_pack(a
) end
663 if o
== "s" then err
, msg
= write_signature(a
) end
666 if o
== "o" then err
, msg
= write_otp(a
) end
669 if o
== "v" then err
, msg
= write_version(a
) end
671 if err
== nil then return oops(msg
) end