|
1 local _M = {}; |
|
2 |
|
3 local aeslua = require "aeslua"; |
|
4 local ciphermode = require "aeslua.ciphermode"; |
|
5 local crc16 = require "crc16"; |
|
6 |
|
7 local modhex2hex; |
|
8 do |
|
9 local modhex_map = {}; |
|
10 local modhex_chars = "cbdefghijklnrtuv"; |
|
11 for i=1,16 do |
|
12 modhex_map[modhex_chars:sub(i,i)] = ("%x"):format(i-1); |
|
13 end |
|
14 function modhex2hex(modhex) |
|
15 if modhex then |
|
16 return (modhex:gsub(".", modhex_map)); |
|
17 end |
|
18 end |
|
19 end |
|
20 _M.modhex2hex = modhex2hex; |
|
21 |
|
22 local function hex2bin(hex) |
|
23 if hex then |
|
24 return (hex:gsub("..", function (c) return string.char(tonumber(c, 16)); end)); |
|
25 end |
|
26 end |
|
27 _M.hex2bin = hex2bin; |
|
28 |
|
29 local function bin2hex(bin) |
|
30 if bin then |
|
31 return (bin:gsub(".", function (b) return ("%02x"):format(b:byte()); end)); |
|
32 end |
|
33 end |
|
34 _M.bin2hex = bin2hex; |
|
35 |
|
36 local otp_parser = {}; |
|
37 local otp_parser_mt = { __index = otp_parser }; |
|
38 |
|
39 function _M.new_fetch_key_hex(fetchkeyhex) |
|
40 return function (...) return hex2bin(fetchkeyhex(...)); end |
|
41 end |
|
42 |
|
43 function _M.new_otp_parser(config) |
|
44 return setmetatable({ |
|
45 config = config, |
|
46 }, otp_parser_mt); |
|
47 end |
|
48 |
|
49 function otp_parser:parse(otp, key) |
|
50 -- Password is any extra data before the real OTP |
|
51 local password; |
|
52 if #otp > 32 + self.config.prefix_length then |
|
53 password = otp:sub(1, #otp - (32 + self.config.prefix_length)); |
|
54 otp = otp:sub(#password+1); |
|
55 end |
|
56 local prefix = otp:sub(1, self.config.prefix_length); |
|
57 local token = otp:sub(#prefix+1); |
|
58 if not key then |
|
59 key = self.config.fetch_key(prefix); |
|
60 else |
|
61 key = hex2bin(key); |
|
62 end |
|
63 if not key then return false, "no-key"; end |
|
64 key = {key:byte(1,#key)} |
|
65 local decrypted = ciphermode.decryptString(key, hex2bin(modhex2hex(token)), ciphermode.decryptCBC); |
|
66 if not decrypted then return false, "decrypt-failed"; end |
|
67 -- Extract private UID |
|
68 local uid = decrypted:sub(1,6); |
|
69 -- Build insertion counter (2 bytes) |
|
70 local use1, use2 = decrypted:sub(7,8):byte(1,2); |
|
71 local use_ctr = use1 + math.pow(2, 8)*use2; |
|
72 -- Build timestamp (3 bytes) |
|
73 local time1, time2, time3 = decrypted:sub(9,11):byte(1,3); |
|
74 local timestamp = time1 + math.pow(2,8)*time2 + math.pow(2, 16)*time3; |
|
75 -- Extract session counter (1 byte) |
|
76 local session_ctr = decrypted:sub(12, 12):byte(); |
|
77 |
|
78 if crc16.hash(decrypted) ~= 0xf0b8 then |
|
79 return false, "invalid-checksum"; |
|
80 end |
|
81 |
|
82 -- Return parsed fields |
|
83 return true, { |
|
84 password = password; |
|
85 public_id = prefix; |
|
86 token = token; |
|
87 private_id = bin2hex(uid); |
|
88 session_counter = session_ctr; |
|
89 use_counter = use_ctr; |
|
90 timestamp = timestamp; |
|
91 }; |
|
92 end |
|
93 |
|
94 function _M.new_authenticator(config) |
|
95 local parser = _M.new_otp_parser(config); |
|
96 local function authenticate(self, otp, key, device_info, userdata) |
|
97 -- Parse OTP, get device data (from config callback) |
|
98 local ok, ret = parser:parse(otp, key); |
|
99 if not ok then return ok, ret; end |
|
100 |
|
101 if not device_info then |
|
102 device_info = config.fetch_device_info(ret); |
|
103 end |
|
104 |
|
105 if ret.use_counter < (device_info.use_counter or 0) |
|
106 or ((ret.use_counter == (device_info.use_counter or 0)) |
|
107 and (ret.session_counter <= (device_info.session_counter or 0))) then |
|
108 return false, "otp-already-used"; |
|
109 end |
|
110 |
|
111 local authed, err = config.check_credentials(ret, device_info, userdata); |
|
112 if not authed then return authed, err; end |
|
113 |
|
114 device_info.use_counter = ret.use_counter; |
|
115 device_info.session_counter = ret.session_counter; |
|
116 |
|
117 config.store_device_info(device_info, userdata); |
|
118 |
|
119 return true, ret; |
|
120 end |
|
121 return { authenticate = authenticate }; |
|
122 end |
|
123 |
|
124 return _M; |