Comparison

util/paseto.lua @ 12840:33d902b093f0

util.paseto: Add support for v3.local tokens
author Matthew Wild <mwild1@gmail.com>
date Fri, 13 Jan 2023 14:38:05 +0000
parent 12839:7db1c1da7bfd
child 12975:d10957394a3c
comparison
equal deleted inserted replaced
12839:7db1c1da7bfd 12840:33d902b093f0
1 local crypto = require "util.crypto"; 1 local crypto = require "util.crypto";
2 local json = require "util.json"; 2 local json = require "util.json";
3 local hashes = require "util.hashes";
3 local base64_encode = require "util.encodings".base64.encode; 4 local base64_encode = require "util.encodings".base64.encode;
4 local base64_decode = require "util.encodings".base64.decode; 5 local base64_decode = require "util.encodings".base64.decode;
5 local secure_equals = require "util.hashes".equals; 6 local secure_equals = require "util.hashes".equals;
6 local bit = require "util.bitcompat"; 7 local bit = require "util.bitcompat";
8 local hex = require "util.hex";
9 local rand = require "util.random";
7 local s_pack = require "util.struct".pack; 10 local s_pack = require "util.struct".pack;
8 11
9 local s_gsub = string.gsub; 12 local s_gsub = string.gsub;
10 13
11 local v4_public = {}; 14 local v4_public = {};
112 115
113 function v4_public.new_verifier(public_key_pem, options) 116 function v4_public.new_verifier(public_key_pem, options)
114 return (select(2, v4_public.init(nil, public_key_pem, options))); 117 return (select(2, v4_public.init(nil, public_key_pem, options)));
115 end 118 end
116 119
120 local v3_local = { _key_mt = {} };
121
122 local function v3_local_derive_keys(k, n)
123 local tmp = hashes.hkdf_hmac_sha384(48, k, nil, "paseto-encryption-key"..n);
124 local Ek = tmp:sub(1, 32);
125 local n2 = tmp:sub(33);
126 local Ak = hashes.hkdf_hmac_sha384(48, k, nil, "paseto-auth-key-for-aead"..n);
127 return Ek, Ak, n2;
128 end
129
130 function v3_local.encrypt(m, k, f, i)
131 assert(#k == 32)
132 if type(m) ~= "table" then
133 return nil, "PASETO payloads must be a table";
134 end
135 m = json.encode(m);
136 local h = "v3.local.";
137 local n = rand.bytes(32);
138 local Ek, Ak, n2 = v3_local_derive_keys(k, n);
139
140 local c = crypto.aes_256_ctr_encrypt(Ek, n2, m);
141 local m2 = pae({ h, n, c, f or "", i or "" });
142 local t = hashes.hmac_sha384(Ak, m2);
143
144 if not f or f == "" then
145 return h..b64url(n..c..t);
146 else
147 return h..b64url(n..c..t).."."..b64url(f);
148 end
149 end
150
151 function v3_local.decrypt(tok, k, expected_f, i)
152 assert(#k == 32)
153
154 local h, sm, f = tok:match("^(v3%.local%.)([^%.]+)%.?(.*)$");
155 if not h then
156 return nil, "invalid-token-format";
157 end
158 f = f and unb64url(f) or nil;
159 if expected_f then
160 if not f or not secure_equals(expected_f, f) then
161 return nil, "invalid-footer";
162 end
163 end
164 local m = unb64url(sm);
165 if not m or #m <= 80 then
166 return nil, "invalid-token-format";
167 end
168 local n, c, t = m:sub(1, 32), m:sub(33, -49), m:sub(-48);
169 local Ek, Ak, n2 = v3_local_derive_keys(k, n);
170 local preAuth = pae({ h, n, c, f or "", i or "" });
171 local t2 = hashes.hmac_sha384(Ak, preAuth);
172 if not secure_equals(t, t2) then
173 return nil, "invalid-token";
174 end
175 local m2 = crypto.aes_256_ctr_decrypt(Ek, n2, c);
176 if not m2 then
177 return nil, "invalid-token";
178 end
179
180 local payload, err = json.decode(m2);
181 if err ~= nil or type(payload) ~= "table" then
182 return nil, "json-decode-error";
183 end
184 return payload;
185 end
186
187 function v3_local.new_key()
188 return "secret-token:paseto.v3.local:"..hex.encode(rand.bytes(32));
189 end
190
191 function v3_local.init(key, options)
192 local encoded_key = key:match("^secret%-token:paseto%.v3%.local:(%x+)$");
193 if not encoded_key or #encoded_key ~= 64 then
194 return error("invalid key for v3.local");
195 end
196 local raw_key = hex.decode(encoded_key);
197 local default_footer = options and options.default_footer;
198 local default_assertion = options and options.default_implicit_assertion;
199 return function (token, token_footer, token_assertion)
200 return v3_local.encrypt(token, raw_key, token_footer or default_footer, token_assertion or default_assertion);
201 end, function (token, token_footer, token_assertion)
202 return v3_local.decrypt(token, raw_key, token_footer or default_footer, token_assertion or default_assertion);
203 end;
204 end
205
206 function v3_local.new_signer(key, options)
207 return (v3_local.init(key, options));
208 end
209
210 function v3_local.new_verifier(key, options)
211 return (select(2, v3_local.init(key, options)));
212 end
213
117 return { 214 return {
118 pae = pae; 215 pae = pae;
216 v3_local = v3_local;
119 v4_public = v4_public; 217 v4_public = v4_public;
120 }; 218 };