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 = evenparity(string.sub(stream,1,12)) and 1 or 0
65 low = not evenparity(string.sub(stream,13)) and 1 or 0
67 bits = bit32.bor(bit32.lshift(id,1), low)
68 bits = bit32.bor(bits, bit32.lshift(high,25))
70 --Since the lua library bit32 is (obviously) 32 bits and we need to
71 --encode 36 bits to properly do a 26 bit tag with the preamble we need
72 --to create a higher order and lower order component which we will
73 --then assemble in the return. The math above defines the proper
74 --encoding as per HID/Weigand/etc. These bit flips are due to the
75 --format length check on bit 38 (cmdlfhid.c:64) and
76 --bit 31 (cmdlfhid.c:66).
77 preamble = bit32.bor(0, bit32.lshift(1,5))
78 bits = bit32.bor(bits, bit32.lshift(1,26))
80 return ("%04x%08x"):format(preamble,bits)
84 local function main(args)
86 --I really wish a better getopt function would be brought in supporting
87 --long arguments, but it seems this library was chosen for BSD style
89 for o, a in getopt.getopt(args, 'f:b:c:h') do
92 print("You did not supply a facility code, using 0")
99 print("You must supply the flag -b (base id)")
106 print("You must supply the flag -c (count)")
118 --Due to my earlier complaints about how this specific getopt library
119 --works, specifying ":" does not enforce supplying a value, thus we
120 --need to do these checks all over again.
122 if isempty(baseid) then
123 print("You must supply the flag -b (base id)")
128 if isempty(count) then
129 print("You must supply the flag -c (count)")
134 --If the facility ID is non specified, ensure we code it as zero
135 if isempty(facility) then
136 print("Using 0 for the facility code as -f was not supplied")
140 --The next baseid + count function presents a logic/UX conflict
141 --where users specifying -c 1 (count = 1) would try to program two
142 --tags. This makes it so that -c 0 & -c 1 both code one tag, and all
143 --other values encode the expected amount.
144 if tonumber(count) > 0 then count = count -1 end
146 endid = baseid + count
148 for cardnum = baseid,endid do
149 local card = cardHex(cardnum, facility)
150 print("Press enter to program card "..cardnum..":"..facility.." (hex: "..card..")")
151 --This would be better with "press any key", but we'll take
154 core.console( ('lf hid clone %s'):format(card) )