Software /
code /
prosody
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 |
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 }; |