util/paseto.lua
author Kim Alvefur <zash@zash.se>
Wed, 27 Mar 2024 19:33:11 +0100
changeset 13471 c2a476f4712a
parent 12979 d10957394a3c
permissions -rw-r--r--
util.startup: Fix exiting on pidfile trouble prosody.shutdown() relies on prosody.main_thread, which has not been set yet at this point. Doing a clean shutdown might actually be harmful in case it tears down things set up by the conflicting Prosody, such as the very pidfile we were looking at. Thanks again SigmaTel71 for noticing
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
12979
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12844
diff changeset
     1
local crypto = require "prosody.util.crypto";
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12844
diff changeset
     2
local json = require "prosody.util.json";
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12844
diff changeset
     3
local hashes = require "prosody.util.hashes";
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12844
diff changeset
     4
local base64_encode = require "prosody.util.encodings".base64.encode;
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12844
diff changeset
     5
local base64_decode = require "prosody.util.encodings".base64.decode;
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12844
diff changeset
     6
local secure_equals = require "prosody.util.hashes".equals;
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12844
diff changeset
     7
local bit = require "prosody.util.bitcompat";
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12844
diff changeset
     8
local hex = require "prosody.util.hex";
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12844
diff changeset
     9
local rand = require "prosody.util.random";
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12844
diff changeset
    10
local s_pack = require "prosody.util.struct".pack;
12698
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    11
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    12
local s_gsub = string.gsub;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    13
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    14
local v4_public = {};
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    15
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    16
local b64url_rep = { ["+"] = "-", ["/"] = "_", ["="] = "", ["-"] = "+", ["_"] = "/" };
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    17
local function b64url(data)
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    18
	return (s_gsub(base64_encode(data), "[+/=]", b64url_rep));
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    19
end
12842
2e71b76ac299 util.paseto: Stricter base64 decoding, as per spec
Matthew Wild <mwild1@gmail.com>
parents: 12720
diff changeset
    20
2e71b76ac299 util.paseto: Stricter base64 decoding, as per spec
Matthew Wild <mwild1@gmail.com>
parents: 12720
diff changeset
    21
local valid_tails = {
2e71b76ac299 util.paseto: Stricter base64 decoding, as per spec
Matthew Wild <mwild1@gmail.com>
parents: 12720
diff changeset
    22
	nil; -- Always invalid
2e71b76ac299 util.paseto: Stricter base64 decoding, as per spec
Matthew Wild <mwild1@gmail.com>
parents: 12720
diff changeset
    23
	"^.[AQgw]$"; -- b??????00
2e71b76ac299 util.paseto: Stricter base64 decoding, as per spec
Matthew Wild <mwild1@gmail.com>
parents: 12720
diff changeset
    24
	"^..[AQgwEUk0IYo4Mcs8]$"; -- b????0000
2e71b76ac299 util.paseto: Stricter base64 decoding, as per spec
Matthew Wild <mwild1@gmail.com>
parents: 12720
diff changeset
    25
}
2e71b76ac299 util.paseto: Stricter base64 decoding, as per spec
Matthew Wild <mwild1@gmail.com>
parents: 12720
diff changeset
    26
12698
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    27
local function unb64url(data)
12842
2e71b76ac299 util.paseto: Stricter base64 decoding, as per spec
Matthew Wild <mwild1@gmail.com>
parents: 12720
diff changeset
    28
	local rem = #data%4;
2e71b76ac299 util.paseto: Stricter base64 decoding, as per spec
Matthew Wild <mwild1@gmail.com>
parents: 12720
diff changeset
    29
	if data:sub(-1,-1) == "=" or rem == 1 or (rem > 1 and not data:sub(-rem):match(valid_tails[rem])) then
2e71b76ac299 util.paseto: Stricter base64 decoding, as per spec
Matthew Wild <mwild1@gmail.com>
parents: 12720
diff changeset
    30
		return nil;
2e71b76ac299 util.paseto: Stricter base64 decoding, as per spec
Matthew Wild <mwild1@gmail.com>
parents: 12720
diff changeset
    31
	end
12698
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    32
	return base64_decode(s_gsub(data, "[-_]", b64url_rep).."==");
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    33
end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    34
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    35
local function le64(n)
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    36
	return s_pack("<I8", bit.band(n, 0x7F));
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    37
end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    38
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    39
local function pae(parts)
12720
0b68b021ce46 util.paseto: Do strict type check in pae() function
Kim Alvefur <zash@zash.se>
parents: 12717
diff changeset
    40
	if type(parts) ~= "table" then
0b68b021ce46 util.paseto: Do strict type check in pae() function
Kim Alvefur <zash@zash.se>
parents: 12717
diff changeset
    41
		error("bad argument #1 to 'pae' (table expected, got "..type(parts)..")");
0b68b021ce46 util.paseto: Do strict type check in pae() function
Kim Alvefur <zash@zash.se>
parents: 12717
diff changeset
    42
	end
12698
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    43
	local o = { le64(#parts) };
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    44
	for _, part in ipairs(parts) do
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    45
		table.insert(o, le64(#part)..part);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    46
	end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    47
	return table.concat(o);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    48
end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    49
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    50
function v4_public.sign(m, sk, f, i)
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    51
	if type(m) ~= "table" then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    52
		return nil, "PASETO payloads must be a table";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    53
	end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    54
	m = json.encode(m);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    55
	local h = "v4.public.";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    56
	local m2 = pae({ h, m, f or "", i or "" });
12717
52eead170bb8 util.paseto: Drop custom wrappers around key objects
Matthew Wild <mwild1@gmail.com>
parents: 12715
diff changeset
    57
	local sig = crypto.ed25519_sign(sk, m2);
12698
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    58
	if not f or f == "" then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    59
		return h..b64url(m..sig);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    60
	else
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    61
		return h..b64url(m..sig).."."..b64url(f);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    62
	end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    63
end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    64
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    65
function v4_public.verify(tok, pk, expected_f, i)
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    66
	local h, sm, f = tok:match("^(v4%.public%.)([^%.]+)%.?(.*)$");
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    67
	if not h then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    68
		return nil, "invalid-token-format";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    69
	end
12713
b3f7c77c1f08 util.paseto: Fix to decode footer before comparison
Matthew Wild <mwild1@gmail.com>
parents: 12698
diff changeset
    70
	f = f and unb64url(f) or nil;
12698
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    71
	if expected_f then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    72
		if not f or not secure_equals(expected_f, f) then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    73
			return nil, "invalid-footer";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    74
		end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    75
	end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    76
	local raw_sm = unb64url(sm);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    77
	if not raw_sm or #raw_sm <= 64 then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    78
		return nil, "invalid-token-format";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    79
	end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    80
	local s, m = raw_sm:sub(-64), raw_sm:sub(1, -65);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    81
	local m2 = pae({ h, m, f or "", i or "" });
12717
52eead170bb8 util.paseto: Drop custom wrappers around key objects
Matthew Wild <mwild1@gmail.com>
parents: 12715
diff changeset
    82
	local ok = crypto.ed25519_verify(pk, m2, s);
12698
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    83
	if not ok then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    84
		return nil, "invalid-token";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    85
	end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    86
	local payload, err = json.decode(m);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    87
	if err ~= nil or type(payload) ~= "table" then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    88
		return nil, "json-decode-error";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    89
	end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    90
	return payload;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    91
end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    92
12717
52eead170bb8 util.paseto: Drop custom wrappers around key objects
Matthew Wild <mwild1@gmail.com>
parents: 12715
diff changeset
    93
v4_public.import_private_key = crypto.import_private_pem;
52eead170bb8 util.paseto: Drop custom wrappers around key objects
Matthew Wild <mwild1@gmail.com>
parents: 12715
diff changeset
    94
v4_public.import_public_key = crypto.import_public_pem;
12698
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    95
function v4_public.new_keypair()
12717
52eead170bb8 util.paseto: Drop custom wrappers around key objects
Matthew Wild <mwild1@gmail.com>
parents: 12715
diff changeset
    96
	return crypto.generate_ed25519_keypair();
12698
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    97
end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    98
12715
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
    99
function v4_public.init(private_key_pem, public_key_pem, options)
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   100
	local sign, verify = v4_public.sign, v4_public.verify;
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   101
	local public_key = public_key_pem and v4_public.import_public_key(public_key_pem);
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   102
	local private_key = private_key_pem and v4_public.import_private_key(private_key_pem);
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   103
	local default_footer = options and options.default_footer;
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   104
	local default_assertion = options and options.default_implicit_assertion;
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   105
	return private_key and function (token, token_footer, token_assertion)
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   106
		return sign(token, private_key, token_footer or default_footer, token_assertion or default_assertion);
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   107
	end, public_key and function (token, expected_footer, token_assertion)
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   108
		return verify(token, public_key, expected_footer or default_footer, token_assertion or default_assertion);
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   109
	end;
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   110
end
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   111
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   112
function v4_public.new_signer(private_key_pem, options)
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   113
	return (v4_public.init(private_key_pem, nil, options));
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   114
end
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   115
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   116
function v4_public.new_verifier(public_key_pem, options)
12843
7db1c1da7bfd util.paseto: Fix omitted parameter
Matthew Wild <mwild1@gmail.com>
parents: 12842
diff changeset
   117
	return (select(2, v4_public.init(nil, public_key_pem, options)));
12715
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   118
end
9e9f158d6699 util.paseto: Export similar API to new util.jwt for ease and consistency
Matthew Wild <mwild1@gmail.com>
parents: 12714
diff changeset
   119
12844
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   120
local v3_local = { _key_mt = {} };
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   121
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   122
local function v3_local_derive_keys(k, n)
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   123
	local tmp = hashes.hkdf_hmac_sha384(48, k, nil, "paseto-encryption-key"..n);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   124
	local Ek = tmp:sub(1, 32);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   125
	local n2 = tmp:sub(33);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   126
	local Ak = hashes.hkdf_hmac_sha384(48, k, nil, "paseto-auth-key-for-aead"..n);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   127
	return Ek, Ak, n2;
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   128
end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   129
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   130
function v3_local.encrypt(m, k, f, i)
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   131
	assert(#k == 32)
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   132
	if type(m) ~= "table" then
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   133
		return nil, "PASETO payloads must be a table";
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   134
	end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   135
	m = json.encode(m);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   136
	local h = "v3.local.";
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   137
	local n = rand.bytes(32);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   138
	local Ek, Ak, n2 = v3_local_derive_keys(k, n);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   139
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   140
	local c = crypto.aes_256_ctr_encrypt(Ek, n2, m);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   141
	local m2 = pae({ h, n, c, f or "", i or "" });
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   142
	local t = hashes.hmac_sha384(Ak, m2);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   143
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   144
	if not f or f == "" then
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   145
		return h..b64url(n..c..t);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   146
	else
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   147
		return h..b64url(n..c..t).."."..b64url(f);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   148
	end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   149
end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   150
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   151
function v3_local.decrypt(tok, k, expected_f, i)
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   152
	assert(#k == 32)
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   153
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   154
	local h, sm, f = tok:match("^(v3%.local%.)([^%.]+)%.?(.*)$");
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   155
	if not h then
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   156
		return nil, "invalid-token-format";
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   157
	end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   158
	f = f and unb64url(f) or nil;
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   159
	if expected_f then
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   160
		if not f or not secure_equals(expected_f, f) then
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   161
			return nil, "invalid-footer";
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   162
		end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   163
	end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   164
	local m = unb64url(sm);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   165
	if not m or #m <= 80 then
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   166
		return nil, "invalid-token-format";
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   167
	end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   168
	local n, c, t = m:sub(1, 32), m:sub(33, -49), m:sub(-48);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   169
	local Ek, Ak, n2 = v3_local_derive_keys(k, n);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   170
	local preAuth = pae({ h, n, c, f or "", i or "" });
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   171
	local t2 = hashes.hmac_sha384(Ak, preAuth);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   172
	if not secure_equals(t, t2) then
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   173
		return nil, "invalid-token";
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   174
	end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   175
	local m2 = crypto.aes_256_ctr_decrypt(Ek, n2, c);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   176
	if not m2 then
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   177
		return nil, "invalid-token";
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   178
	end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   179
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   180
	local payload, err = json.decode(m2);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   181
	if err ~= nil or type(payload) ~= "table" then
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   182
		return nil, "json-decode-error";
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   183
	end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   184
	return payload;
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   185
end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   186
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   187
function v3_local.new_key()
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   188
	return "secret-token:paseto.v3.local:"..hex.encode(rand.bytes(32));
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   189
end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   190
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   191
function v3_local.init(key, options)
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   192
	local encoded_key = key:match("^secret%-token:paseto%.v3%.local:(%x+)$");
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   193
	if not encoded_key or #encoded_key ~= 64 then
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   194
		return error("invalid key for v3.local");
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   195
	end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   196
	local raw_key = hex.decode(encoded_key);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   197
	local default_footer = options and options.default_footer;
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   198
	local default_assertion = options and options.default_implicit_assertion;
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   199
	return function (token, token_footer, token_assertion)
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   200
		return v3_local.encrypt(token, raw_key, token_footer or default_footer, token_assertion or default_assertion);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   201
	end, function (token, token_footer, token_assertion)
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   202
		return v3_local.decrypt(token, raw_key, token_footer or default_footer, token_assertion or default_assertion);
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   203
	end;
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   204
end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   205
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   206
function v3_local.new_signer(key, options)
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   207
	return (v3_local.init(key, options));
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   208
end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   209
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   210
function v3_local.new_verifier(key, options)
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   211
	return (select(2, v3_local.init(key, options)));
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   212
end
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   213
12698
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   214
return {
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   215
	pae = pae;
12844
33d902b093f0 util.paseto: Add support for v3.local tokens
Matthew Wild <mwild1@gmail.com>
parents: 12843
diff changeset
   216
	v3_local = v3_local;
12698
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   217
	v4_public = v4_public;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
   218
};