Diff

mod_auth_token/token_auth_utils.lib.lua @ 2956:d0ca211e1b0e

New HMAC token authentication module for Prosody.
author JC Brand <jc@opkode.com>
date Tue, 27 Mar 2018 10:48:04 +0200
child 3472:ac1f63cdb6d6
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_auth_token/token_auth_utils.lib.lua	Tue Mar 27 10:48:04 2018 +0200
@@ -0,0 +1,67 @@
+local base64 = require "util.encodings".base64;
+local digest = require "openssl.digest";
+local hmac = require "openssl.hmac";
+local luatz = require "luatz";
+local otp = require "otp";
+
+local DIGEST_TYPE = "SHA256";
+local OTP_DEVIATION = 1;
+local OTP_DIGITS = 8;
+local OTP_INTERVAL = 30;
+
+local nonce_cache = {};
+
+function check_nonce(jid, otp, nonce)
+	-- We cache all nonces used per OTP, to ensure that a token cannot be used
+	-- more than once.
+	--
+	-- We assume that the OTP is valid in the current time window. This is the
+	-- case because we only call check_nonce *after* the OTP has been verified.
+	--
+	-- We only store one OTP per JID, so if a new OTP comes in, we wipe the
+	-- previous OTP and its cached nonces.
+	if nonce_cache[jid] == nil or nonce_cache[jid][otp] == nil then
+		nonce_cache[jid] = {}
+		nonce_cache[jid][otp] = {}
+		nonce_cache[jid][otp][nonce] = true
+		return true;
+	end
+	if nonce_cache[jid][otp][nonce] == true then
+		return false;
+	else
+		nonce_cache[jid][otp][nonce] = true;
+		return true;
+	end
+end
+
+
+function verify_token(username, password, realm, otp_seed, token_secret, log)
+	local totp = otp.new_totp_from_key(otp_seed, OTP_DIGITS, OTP_INTERVAL)
+	local token = string.match(password, "(%d+) ")
+	local otp = token:sub(1,8)
+	local nonce = token:sub(9)
+	local signature = base64.decode(string.match(password, " (.+)"))
+	local jid = username.."@"..realm
+
+	if totp:verify(otp, OTP_DEVIATION, luatz.gmtime(luatz.time())) then
+		-- log("debug", "**** THE OTP WAS VERIFIED ****** ");
+		local hmac_ctx = hmac.new(token_secret, DIGEST_TYPE)
+		if signature == hmac_ctx:final(otp..nonce..jid) then
+			-- log("debug", "**** THE KEY WAS VERIFIED ****** ");
+			if check_nonce(jid, otp, nonce) then
+				-- log("debug", "**** THE NONCE WAS VERIFIED ****** ");
+				return true;
+			end
+		end
+	end
+	-- log("debug", "**** VERIFICATION FAILED ****** ");
+	return false;
+end
+
+return {
+	OTP_DEVIATION = OTP_DIGITS,
+	OTP_DIGITS = OTP_DIGITS,
+	OTP_INTERVAL = OTP_INTERVAL,
+	DIGEST_TYPE = DIGEST_TYPE,
+	verify_token = verify_token;
+}