2 -- lf_bulk_program.lua - A tool to clone a large number of tags at once.
5 -- The getopt-functionality is loaded from pm3/client/lualibs/getopt.lua
6 -- Have a look there for further details
7 getopt = require('getopt')
8 bit32 = require('bit32')
10 usage = [[ script run lf_bulk_program.lua -f facility -b base_id_num -c count
13 script run lf_bulk_program.lua -f 1 -b 1000 -c 10
15 author = "Brian Redbeard"
17 Perform bulk enrollment of 26 bit H10301 style RFID Tags
18 For more info, check the comments in the code
21 --[[Implement a function to simply visualize the bitstream in a text format
22 --This is especially helpful for troubleshooting bitwise math issues]]--
23 function toBits(num,bits)
24 -- returns a table of bits, most significant first.
25 bits = bits or math.max(1, select(2, math.frexp(num)))
26 local t = {} -- will contain the bits
27 for b = bits, 1, -1 do
28 t[b] = math.fmod(num, 2)
29 num = math.floor((num - t[b]) / 2)
31 return table.concat(t)
34 --[[Likely, I'm an idiot, but I couldn't find any parity functions in Lua
35 This can also be done with a combination of bitwise operations (in fact,
36 is the canonically "correct" way to do it, but my brain doesn't just
37 default to this and so counting some ones is good enough for me]]--
38 local function evenparity(s)
39 local _, count = string.gsub(s, "1", "")
50 local function isempty(s)
51 return s == nil or s == ''
54 --[[The Proxmark3 "clone" functions expect the data to be in hex format so
55 take the card id number and facility ID as arguments and construct the
56 hex. This should be easy enough to extend to non 26bit formats]]--
57 local function cardHex(i,f)
58 fac = bit32.lshift(f,16)
59 id = bit32.bor(i, fac)
62 --As the function defaults to even parity and returns a boolean,
63 --perform a 'not' function to get odd parity
64 high = not evenparity(string.sub(stream,0,12)) and 1 or 0
65 low = evenparity(string.sub(stream,13)) and 1 or 0
66 bits = bit32.bor(bit32.lshift(id,1), low)
67 bits = bit32.bor(bits, bit32.lshift(high,25))
69 --Since the lua library bit32 is (obviously) 32 bits and we need to
70 --encode 36 bits to properly do a 26 bit tag with the preamble we need
71 --to create a higher order and lower order component which we will
72 --then assemble in the return. The math above defines the proper
73 --encoding as per HID/Weigand/etc. These bit flips are due to the
74 --format length check on bit 38 (cmdlfhid.c:64) and
75 --bit 31 (cmdlfhid.c:66).
76 preamble = bit32.bor(0, bit32.lshift(1,5))
77 bits = bit32.bor(bits, bit32.lshift(1,26))
79 return ("%04x%08x"):format(preamble,bits)
83 local function main(args)
85 --I really wish a better getopt function would be brought in supporting
86 --long arguments, but it seems this library was chosen for BSD style
88 for o, a in getopt.getopt(args, 'f:b:c:h') do
91 print("You did not supply a facility code, using 0")
98 print("You must supply the flag -b (base id)")
105 print("You must supply the flag -c (count)")
117 --Due to my earlier complaints about how this specific getopt library
118 --works, specifying ":" does not enforce supplying a value, thus we
119 --need to do these checks all over again.
121 if isempty(baseid) then
122 print("You must supply the flag -b (base id)")
127 if isempty(count) then
128 print("You must supply the flag -c (count)")
133 --If the facility ID is non specified, ensure we code it as zero
134 if isempty(facility) then
135 print("Using 0 for the facility code as -f was not supplied")
139 --The next baseid + count function presents a logic/UX conflict
140 --where users specifying -c 1 (count = 1) would try to program two
141 --tags. This makes it so that -c 0 & -c 1 both code one tag, and all
142 --other values encode the expected amount.
143 if tonumber(count) > 0 then count = count -1 end
145 endid = baseid + count
147 for cardnum = baseid,endid do
148 local card = cardHex(cardnum, facility)
149 print("Press enter to program card "..cardnum..":"..facility.." (hex: "..card..")")
150 --This would be better with "press any key", but we'll take
153 core.console( ('lf hid clone %s'):format(card) )