1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
local crypto = require "util.crypto";
local json = require "util.json";
local base64_encode = require "util.encodings".base64.encode;
local base64_decode = require "util.encodings".base64.decode;
local secure_equals = require "util.hashes".equals;
local bit = require "util.bitcompat";
local s_pack = require "util.struct".pack;
local s_gsub = string.gsub;
local pubkey_methods = {};
local privkey_methods = {};
local v4_public_pubkey_mt = { __index = pubkey_methods };
local v4_public_privkey_mt = { __index = privkey_methods };
local v4_public = {};
local b64url_rep = { ["+"] = "-", ["/"] = "_", ["="] = "", ["-"] = "+", ["_"] = "/" };
local function b64url(data)
return (s_gsub(base64_encode(data), "[+/=]", b64url_rep));
end
local function unb64url(data)
return base64_decode(s_gsub(data, "[-_]", b64url_rep).."==");
end
local function le64(n)
return s_pack("<I8", bit.band(n, 0x7F));
end
local function pae(parts)
local o = { le64(#parts) };
for _, part in ipairs(parts) do
table.insert(o, le64(#part)..part);
end
return table.concat(o);
end
function privkey_methods:export()
return self.key:private_pem();
end
function pubkey_methods:export()
return self.key:public_pem();
end
function v4_public.sign(m, sk, f, i)
if getmetatable(sk) ~= v4_public_privkey_mt then
error("cannot sign v4.public tokens with this key");
end
if type(m) ~= "table" then
return nil, "PASETO payloads must be a table";
end
m = json.encode(m);
local h = "v4.public.";
local m2 = pae({ h, m, f or "", i or "" });
local sig = crypto.ed25519_sign(sk.key, m2);
if not f or f == "" then
return h..b64url(m..sig);
else
return h..b64url(m..sig).."."..b64url(f);
end
end
function v4_public.verify(tok, pk, expected_f, i)
if getmetatable(pk) ~= v4_public_pubkey_mt then
error("cannot verify v4.public tokens with this key");
end
local h, sm, f = tok:match("^(v4%.public%.)([^%.]+)%.?(.*)$");
if not h then
return nil, "invalid-token-format";
end
if expected_f then
if not f or not secure_equals(expected_f, f) then
return nil, "invalid-footer";
end
end
local raw_sm = unb64url(sm);
if not raw_sm or #raw_sm <= 64 then
return nil, "invalid-token-format";
end
local s, m = raw_sm:sub(-64), raw_sm:sub(1, -65);
local m2 = pae({ h, m, f or "", i or "" });
local ok = crypto.ed25519_verify(pk.key, m2, s);
if not ok then
return nil, "invalid-token";
end
local payload, err = json.decode(m);
if err ~= nil or type(payload) ~= "table" then
return nil, "json-decode-error";
end
return payload;
end
function v4_public.new_keypair()
local key = crypto.generate_ed25519_keypair();
return {
private_key = setmetatable({
key = key;
}, v4_public_privkey_mt);
public_key = setmetatable({
key = key;
}, v4_public_pubkey_mt);
};
end
function v4_public.import_public_key(pem)
local key = crypto.import_public_pem(pem);
return setmetatable({
key = key;
}, v4_public_pubkey_mt);
end
function v4_public.import_private_key(pem)
local key = crypto.import_private_pem(pem);
return setmetatable({
key = key;
}, v4_public_privkey_mt);
end
return {
pae = pae;
v4_public = v4_public;
};
|