Mercurial > prosody-hg
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 |
| parents | 9e9f158d6699 |
| children | 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); |
