Annotate

util/paseto.lua @ 12705:008a7097fdc5

util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime) To avoid every user of the library needing to add and verify expiry info, this is now handled by util.jwt itself (if not overridden or disabled). Issuing tokens that are valid forever is bad practice and rarely desired, and the default token lifetime is now 3600s (1 hour).
author Matthew Wild <mwild1@gmail.com>
date Mon, 11 Jul 2022 13:28:29 +0100
parent 12694:26a004c96ef8
child 12709:b3f7c77c1f08
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
12694
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1 local crypto = require "util.crypto";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
2 local json = require "util.json";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
3 local base64_encode = require "util.encodings".base64.encode;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
4 local base64_decode = require "util.encodings".base64.decode;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
5 local secure_equals = require "util.hashes".equals;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
6 local bit = require "util.bitcompat";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
7 local s_pack = require "util.struct".pack;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
8
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
9 local s_gsub = string.gsub;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
10
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
11 local pubkey_methods = {};
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
12 local privkey_methods = {};
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_pubkey_mt = { __index = pubkey_methods };
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
15 local v4_public_privkey_mt = { __index = privkey_methods };
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
16 local v4_public = {};
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
17
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
18 local b64url_rep = { ["+"] = "-", ["/"] = "_", ["="] = "", ["-"] = "+", ["_"] = "/" };
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
19 local function b64url(data)
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
20 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
21 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
22 local function unb64url(data)
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
23 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
24 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
25
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
26 local function le64(n)
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
27 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
28 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
29
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
30 local function pae(parts)
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
31 local o = { le64(#parts) };
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
32 for _, part in ipairs(parts) do
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
33 table.insert(o, le64(#part)..part);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
34 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
35 return table.concat(o);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
36 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
37
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
38 function privkey_methods:export()
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
39 return self.key:private_pem();
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
40 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
41
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
42 function pubkey_methods:export()
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
43 return self.key:public_pem();
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
44 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
45
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
46 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
47 if getmetatable(sk) ~= v4_public_privkey_mt then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
48 error("cannot sign v4.public tokens with this key");
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
49 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
50 if type(m) ~= "table" then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
51 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
52 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
53 m = json.encode(m);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
54 local h = "v4.public.";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
55 local m2 = pae({ h, m, f or "", i or "" });
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
56 local sig = crypto.ed25519_sign(sk.key, m2);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
57 if not f or f == "" then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
58 return h..b64url(m..sig);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
59 else
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
60 return h..b64url(m..sig).."."..b64url(f);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
61 end
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
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
64 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
65 if getmetatable(pk) ~= v4_public_pubkey_mt then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
66 error("cannot verify v4.public tokens with this key");
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
67 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
68 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
69 if not h then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
70 return nil, "invalid-token-format";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
71 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
72 if expected_f then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
73 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
74 return nil, "invalid-footer";
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 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
77 local raw_sm = unb64url(sm);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
78 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
79 return nil, "invalid-token-format";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
80 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
81 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
82 local m2 = pae({ h, m, f or "", i or "" });
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
83 local ok = crypto.ed25519_verify(pk.key, m2, s);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
84 if not ok then
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
85 return nil, "invalid-token";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
86 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
87 local payload, err = json.decode(m);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
88 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
89 return nil, "json-decode-error";
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
90 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
91 return payload;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
92 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
93
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
94 function v4_public.new_keypair()
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
95 local key = crypto.generate_ed25519_keypair();
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
96 return {
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
97 private_key = setmetatable({
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
98 key = key;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
99 }, v4_public_privkey_mt);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
100 public_key = setmetatable({
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
101 key = key;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
102 }, v4_public_pubkey_mt);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
103 };
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
104 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
105
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
106 function v4_public.import_public_key(pem)
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
107 local key = crypto.import_public_pem(pem);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
108 return setmetatable({
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
109 key = key;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
110 }, v4_public_pubkey_mt);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
111 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
112
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
113 function v4_public.import_private_key(pem)
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
114 local key = crypto.import_private_pem(pem);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
115 return setmetatable({
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
116 key = key;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
117 }, v4_public_privkey_mt);
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
118 end
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
119
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
120 return {
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
121 pae = pae;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
122 v4_public = v4_public;
26a004c96ef8 util.paseto: Implementation of PASETO v4.public tokens
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
123 };