util/paseto.lua
author Kim Alvefur <zash@zash.se>
Thu, 28 Mar 2024 15:26:57 +0100
changeset 13472 98806cac64c3
parent 12979 d10957394a3c
permissions -rw-r--r--
MUC: Switch to official XEP-0317 namespace for Hats (including compat) (thanks nicoco)
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
};