Comparison

util/paseto.lua @ 12713:52eead170bb8

util.paseto: Drop custom wrappers around key objects The PASETO spec recommends - no, *requires* - that implementations enforce type safety for keys, and e.g. do not pass them around as arbitrary byte strings. Typed wrapper objects are recommended. I originally followed this advice when starting the lib. However, key wrapping and type safety is now also a feature of util.crypto. All we're doing is duplicating it unnecessarily with this additional wrapper code.
author Matthew Wild <mwild1@gmail.com>
date Mon, 11 Jul 2022 14:30:39 +0100
parent 12711:9e9f158d6699
child 12716:0b68b021ce46
comparison
equal deleted inserted replaced
12712:719a72f14e90 12713:52eead170bb8
6 local bit = require "util.bitcompat"; 6 local bit = require "util.bitcompat";
7 local s_pack = require "util.struct".pack; 7 local s_pack = require "util.struct".pack;
8 8
9 local s_gsub = string.gsub; 9 local s_gsub = string.gsub;
10 10
11 local pubkey_methods = {};
12 local privkey_methods = {};
13
14 local v4_public_pubkey_mt = { __index = pubkey_methods };
15 local v4_public_privkey_mt = { __index = privkey_methods };
16 local v4_public = {}; 11 local v4_public = {};
17 12
18 local b64url_rep = { ["+"] = "-", ["/"] = "_", ["="] = "", ["-"] = "+", ["_"] = "/" }; 13 local b64url_rep = { ["+"] = "-", ["/"] = "_", ["="] = "", ["-"] = "+", ["_"] = "/" };
19 local function b64url(data) 14 local function b64url(data)
20 return (s_gsub(base64_encode(data), "[+/=]", b64url_rep)); 15 return (s_gsub(base64_encode(data), "[+/=]", b64url_rep));
33 table.insert(o, le64(#part)..part); 28 table.insert(o, le64(#part)..part);
34 end 29 end
35 return table.concat(o); 30 return table.concat(o);
36 end 31 end
37 32
38 function privkey_methods:export()
39 return self.key:private_pem();
40 end
41
42 function pubkey_methods:export()
43 return self.key:public_pem();
44 end
45
46 function v4_public.sign(m, sk, f, i) 33 function v4_public.sign(m, sk, f, i)
47 if getmetatable(sk) ~= v4_public_privkey_mt then
48 error("cannot sign v4.public tokens with this key");
49 end
50 if type(m) ~= "table" then 34 if type(m) ~= "table" then
51 return nil, "PASETO payloads must be a table"; 35 return nil, "PASETO payloads must be a table";
52 end 36 end
53 m = json.encode(m); 37 m = json.encode(m);
54 local h = "v4.public."; 38 local h = "v4.public.";
55 local m2 = pae({ h, m, f or "", i or "" }); 39 local m2 = pae({ h, m, f or "", i or "" });
56 local sig = crypto.ed25519_sign(sk.key, m2); 40 local sig = crypto.ed25519_sign(sk, m2);
57 if not f or f == "" then 41 if not f or f == "" then
58 return h..b64url(m..sig); 42 return h..b64url(m..sig);
59 else 43 else
60 return h..b64url(m..sig).."."..b64url(f); 44 return h..b64url(m..sig).."."..b64url(f);
61 end 45 end
62 end 46 end
63 47
64 function v4_public.verify(tok, pk, expected_f, i) 48 function v4_public.verify(tok, pk, expected_f, i)
65 if getmetatable(pk) ~= v4_public_pubkey_mt then
66 error("cannot verify v4.public tokens with this key");
67 end
68 local h, sm, f = tok:match("^(v4%.public%.)([^%.]+)%.?(.*)$"); 49 local h, sm, f = tok:match("^(v4%.public%.)([^%.]+)%.?(.*)$");
69 if not h then 50 if not h then
70 return nil, "invalid-token-format"; 51 return nil, "invalid-token-format";
71 end 52 end
72 f = f and unb64url(f) or nil; 53 f = f and unb64url(f) or nil;
79 if not raw_sm or #raw_sm <= 64 then 60 if not raw_sm or #raw_sm <= 64 then
80 return nil, "invalid-token-format"; 61 return nil, "invalid-token-format";
81 end 62 end
82 local s, m = raw_sm:sub(-64), raw_sm:sub(1, -65); 63 local s, m = raw_sm:sub(-64), raw_sm:sub(1, -65);
83 local m2 = pae({ h, m, f or "", i or "" }); 64 local m2 = pae({ h, m, f or "", i or "" });
84 local ok = crypto.ed25519_verify(pk.key, m2, s); 65 local ok = crypto.ed25519_verify(pk, m2, s);
85 if not ok then 66 if not ok then
86 return nil, "invalid-token"; 67 return nil, "invalid-token";
87 end 68 end
88 local payload, err = json.decode(m); 69 local payload, err = json.decode(m);
89 if err ~= nil or type(payload) ~= "table" then 70 if err ~= nil or type(payload) ~= "table" then
90 return nil, "json-decode-error"; 71 return nil, "json-decode-error";
91 end 72 end
92 return payload; 73 return payload;
93 end 74 end
94 75
76 v4_public.import_private_key = crypto.import_private_pem;
77 v4_public.import_public_key = crypto.import_public_pem;
95 function v4_public.new_keypair() 78 function v4_public.new_keypair()
96 local key = crypto.generate_ed25519_keypair(); 79 return crypto.generate_ed25519_keypair();
97 return {
98 private_key = setmetatable({
99 key = key;
100 }, v4_public_privkey_mt);
101 public_key = setmetatable({
102 key = key;
103 }, v4_public_pubkey_mt);
104 };
105 end
106
107 function v4_public.import_public_key(pem)
108 local key = crypto.import_public_pem(pem);
109 assert(key:get_type() == "ED25519", "Invalid public key type for v4.public");
110 return setmetatable({
111 key = key;
112 }, v4_public_pubkey_mt);
113 end
114
115 function v4_public.import_private_key(pem)
116 local key = crypto.import_private_pem(pem);
117 assert(key:get_type() == "ED25519", "Invalid private key type for v4.public");
118 return setmetatable({
119 key = key;
120 }, v4_public_privkey_mt);
121 end 80 end
122 81
123 function v4_public.init(private_key_pem, public_key_pem, options) 82 function v4_public.init(private_key_pem, public_key_pem, options)
124 local sign, verify = v4_public.sign, v4_public.verify; 83 local sign, verify = v4_public.sign, v4_public.verify;
125 local public_key = public_key_pem and v4_public.import_public_key(public_key_pem); 84 local public_key = public_key_pem and v4_public.import_public_key(public_key_pem);