util/paseto.lua
changeset 12844 33d902b093f0
parent 12843 7db1c1da7bfd
child 12979 d10957394a3c
equal deleted inserted replaced
12843:7db1c1da7bfd 12844: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 };