2023-01-28 11:04:14 +00:00
|
|
|
|
2023-01-28 16:25:39 +00:00
|
|
|
-- big-endian uint64 of a number
|
2023-09-27 10:57:15 +00:00
|
|
|
function fediauth.write_uint64_be(v)
|
2023-10-02 08:23:32 +00:00
|
|
|
local b1 = bit.band( bit.rshift(v, 56), 0xFF )
|
|
|
|
local b2 = bit.band( bit.rshift(v, 48), 0xFF )
|
|
|
|
local b3 = bit.band( bit.rshift(v, 40), 0xFF )
|
|
|
|
local b4 = bit.band( bit.rshift(v, 32), 0xFF )
|
|
|
|
local b5 = bit.band( bit.rshift(v, 24), 0xFF )
|
|
|
|
local b6 = bit.band( bit.rshift(v, 16), 0xFF )
|
|
|
|
local b7 = bit.band( bit.rshift(v, 8), 0xFF )
|
|
|
|
local b8 = bit.band( bit.rshift(v, 0), 0xFF )
|
2023-01-28 11:04:14 +00:00
|
|
|
return string.char(b1, b2, b3, b4, b5, b6, b7, b8)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- prepare paddings
|
|
|
|
-- https://en.wikipedia.org/wiki/HMAC
|
|
|
|
local i_pad = ""
|
|
|
|
local o_pad = ""
|
2023-10-02 11:48:18 +00:00
|
|
|
i_pad = string.char(0x36):rep(64)
|
|
|
|
o_pad = string.char(0x5c):rep(64)
|
2023-01-28 11:04:14 +00:00
|
|
|
|
|
|
|
-- hmac generation
|
2023-09-27 10:57:15 +00:00
|
|
|
function fediauth.hmac(key, message)
|
2023-01-28 11:04:14 +00:00
|
|
|
local i_key_pad = ""
|
|
|
|
for i=1,64 do
|
2023-10-02 08:23:32 +00:00
|
|
|
i_key_pad = i_key_pad .. string.char(bit.bxor(string.byte(key, i) or 0x00, string.byte(i_pad, i)))
|
2023-01-28 11:04:14 +00:00
|
|
|
end
|
2023-01-28 16:25:39 +00:00
|
|
|
assert(#i_key_pad == 64)
|
2023-01-28 11:04:14 +00:00
|
|
|
|
|
|
|
local o_key_pad = ""
|
|
|
|
for i=1,64 do
|
2023-10-02 08:23:32 +00:00
|
|
|
o_key_pad = o_key_pad .. string.char(bit.bxor(string.byte(key, i) or 0x00, string.byte(o_pad, i)))
|
2023-01-28 11:04:14 +00:00
|
|
|
end
|
2023-01-28 16:25:39 +00:00
|
|
|
assert(#o_key_pad == 64)
|
2023-01-28 11:04:14 +00:00
|
|
|
|
|
|
|
-- concat message
|
|
|
|
local first_msg = i_key_pad
|
|
|
|
for i=1,#message do
|
2023-01-28 16:25:39 +00:00
|
|
|
first_msg = first_msg .. string.char(string.byte(message, i))
|
2023-01-28 11:04:14 +00:00
|
|
|
end
|
2023-01-28 16:25:39 +00:00
|
|
|
assert(#first_msg == 64+8)
|
2023-01-28 11:04:14 +00:00
|
|
|
|
|
|
|
-- hash first message
|
|
|
|
local hash_sum_1 = minetest.sha1(first_msg, true)
|
2023-01-28 16:25:39 +00:00
|
|
|
assert(#hash_sum_1 == 20)
|
2023-01-28 11:04:14 +00:00
|
|
|
|
2023-01-28 16:26:35 +00:00
|
|
|
-- concat first message to second
|
2023-01-28 11:04:14 +00:00
|
|
|
local second_msg = o_key_pad
|
|
|
|
for i=1,#hash_sum_1 do
|
2023-01-28 16:25:39 +00:00
|
|
|
second_msg = second_msg .. string.char(string.byte(hash_sum_1, i))
|
2023-01-28 11:04:14 +00:00
|
|
|
end
|
2023-01-28 16:25:39 +00:00
|
|
|
assert(#second_msg == 64+20)
|
|
|
|
|
|
|
|
local hmac = minetest.sha1(second_msg, true)
|
|
|
|
assert(#hmac == 20)
|
|
|
|
|
|
|
|
return hmac
|
|
|
|
end
|
|
|
|
|
2023-01-28 18:37:27 +00:00
|
|
|
local function left_pad(str, s, len)
|
2023-10-02 11:48:18 +00:00
|
|
|
return s:rep(#str - len) .. str
|
2023-01-28 18:37:27 +00:00
|
|
|
end
|
|
|
|
|
2023-09-27 10:57:15 +00:00
|
|
|
function fediauth.generate_tfediauth(secret_b32, unix_time)
|
|
|
|
local key = fediauth.basexx.from_base32(secret_b32)
|
2023-01-28 18:37:27 +00:00
|
|
|
unix_time = unix_time or os.time()
|
|
|
|
|
|
|
|
local tx = 30
|
|
|
|
local ct = math.floor(unix_time / tx)
|
2023-09-27 10:57:15 +00:00
|
|
|
local counter = fediauth.write_uint64_be(ct)
|
2023-01-28 18:37:27 +00:00
|
|
|
local valid_seconds = ((ct * tx) + tx) - unix_time
|
|
|
|
|
2023-09-27 10:57:15 +00:00
|
|
|
local hmac = fediauth.hmac(key, counter)
|
2023-01-28 11:04:14 +00:00
|
|
|
|
2023-01-28 16:25:39 +00:00
|
|
|
-- https://www.rfc-editor.org/rfc/rfc4226#section-5.4
|
2023-10-02 08:23:32 +00:00
|
|
|
local offset = bit.band(string.byte(hmac, #hmac), 0xF)
|
2023-01-28 16:25:39 +00:00
|
|
|
local value = 0
|
2023-10-02 08:23:32 +00:00
|
|
|
value = bit.bxor(value, string.byte(hmac, offset+4))
|
|
|
|
value = bit.bxor(value, bit.lshift(string.byte(hmac, offset+3), 8))
|
|
|
|
value = bit.bxor(value, bit.lshift(string.byte(hmac, offset+2), 16))
|
|
|
|
value = bit.bxor(value, bit.lshift(bit.band(string.byte(hmac, offset+1), 0x7F), 24))
|
2023-01-28 18:37:27 +00:00
|
|
|
local code = value % 10^6
|
|
|
|
local padded_code = left_pad("" .. code, "0", 6)
|
|
|
|
|
|
|
|
return padded_code, valid_seconds
|
|
|
|
end
|
|
|
|
|
2023-09-27 10:57:15 +00:00
|
|
|
function fediauth.generate_secret()
|
2023-01-28 18:47:01 +00:00
|
|
|
local buf = minetest.sha1("" .. math.random(10000), true)
|
2023-10-02 11:48:18 +00:00
|
|
|
local s = buf:sub(1, 20)
|
2023-01-28 18:47:01 +00:00
|
|
|
return s
|
2023-01-29 11:13:50 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- get or generate per-player secret b32 ecoded
|
2023-09-27 10:57:15 +00:00
|
|
|
function fediauth.get_player_secret_b32(name)
|
|
|
|
local secret_b32 = fediauth.storage:get_string(name .. "_secret")
|
2023-01-29 11:13:50 +00:00
|
|
|
if secret_b32 == "" then
|
2023-09-27 10:57:15 +00:00
|
|
|
secret_b32 = fediauth.basexx.to_base32(fediauth.generate_secret())
|
|
|
|
fediauth.storage:set_string(name .. "_secret", secret_b32)
|
2023-01-29 11:13:50 +00:00
|
|
|
end
|
|
|
|
return secret_b32
|
2023-01-30 18:36:03 +00:00
|
|
|
end
|
|
|
|
|
2023-09-27 10:57:15 +00:00
|
|
|
-- returns true if the player is fediauth enabled _and_ set up properly
|
|
|
|
function fediauth.is_player_enabled(name)
|
|
|
|
local has_secret = fediauth.storage:get_string(name .. "_secret") ~= ""
|
|
|
|
local has_priv = minetest.check_player_privs(name, "fediauth_enabled")
|
2023-01-30 18:36:03 +00:00
|
|
|
|
|
|
|
return has_secret and has_priv
|
2023-02-08 20:40:47 +00:00
|
|
|
end
|
|
|
|
|
2023-09-27 10:57:15 +00:00
|
|
|
function fediauth.is_player_bypassed(name)
|
|
|
|
local has_priv = minetest.check_player_privs(name, "fediauth_bypass")
|
|
|
|
|
|
|
|
return has_priv
|
|
|
|
end
|
|
|
|
|
|
|
|
function fediauth.check_code(secret_b32, code, time)
|
2023-02-08 20:40:47 +00:00
|
|
|
time = time or os.time()
|
2023-09-27 10:57:15 +00:00
|
|
|
for _, t_offset in ipairs({0, -30, 30, -60, 60}) do
|
|
|
|
local expected_code = fediauth.generate_tfediauth(secret_b32, time + t_offset)
|
2023-02-08 20:40:47 +00:00
|
|
|
if expected_code == code then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
2023-09-27 10:57:15 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function fediauth.give_code(secret_b32, time)
|
|
|
|
time = time or os.time()
|
|
|
|
local codeseq = {}
|
|
|
|
for _, t_offset in ipairs({60, 30, 0, -30, -60}) do
|
|
|
|
local expected_code = fediauth.generate_tfediauth(secret_b32, time + t_offset)
|
|
|
|
table.insert(codeseq, expected_code)
|
|
|
|
end
|
|
|
|
return codeseq
|
|
|
|
end
|
2023-09-27 19:47:29 +00:00
|
|
|
|
2023-09-30 12:52:34 +00:00
|
|
|
function fediauth.is_home_instance(domain)
|
|
|
|
local current_instance_service = minetest.settings:get("fediauth.instance")
|
|
|
|
return current_instance_service == domain
|
|
|
|
end
|
|
|
|
|
2023-09-29 00:50:39 +00:00
|
|
|
function fediauth.check_for_restricted_instance(domain)
|
|
|
|
local restricted_instances = minetest.settings:get("fediauth.restricted_instances") or {}
|
|
|
|
if type(restricted_instances) == "string" then
|
|
|
|
restricted_instances = restricted_instances:split(",")
|
|
|
|
end
|
|
|
|
for _, instance in ipairs(restricted_instances) do
|
|
|
|
if instance == domain then
|
|
|
|
minetest.log("action", "[fediauth] domain restricted: '" .. domain .. "'")
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
2023-09-27 19:47:29 +00:00
|
|
|
end
|
2023-09-29 00:50:39 +00:00
|
|
|
|