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