util/jwt.lua
author Kim Alvefur <zash@zash.se>
Tue, 14 May 2024 17:07:47 +0200
changeset 13494 6f840763fc73
parent 12979 d10957394a3c
permissions -rw-r--r--
net.server_epoll: Add support for systemd socket activation Allows creating listening sockets and accepting client connections before Prosody starts. This is unlike normal Prosody dynamic resource management, where ports may added and removed at any time, and the ports defined by the config. Weird things happen if these are closed (e.g. due to reload) so here we prevent closing and ensure sockets are reused when opened again.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
10664
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
     1
local s_gsub = string.gsub;
12979
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12742
diff changeset
     2
local crypto = require "prosody.util.crypto";
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12742
diff changeset
     3
local json = require "prosody.util.json";
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12742
diff changeset
     4
local hashes = require "prosody.util.hashes";
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12742
diff changeset
     5
local base64_encode = require "prosody.util.encodings".base64.encode;
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12742
diff changeset
     6
local base64_decode = require "prosody.util.encodings".base64.decode;
d10957394a3c util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents: 12742
diff changeset
     7
local secure_equals = require "prosody.util.hashes".equals;
10664
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
     8
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
     9
local b64url_rep = { ["+"] = "-", ["/"] = "_", ["="] = "", ["-"] = "+", ["_"] = "/" };
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    10
local function b64url(data)
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    11
	return (s_gsub(base64_encode(data), "[+/=]", b64url_rep));
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    12
end
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    13
local function unb64url(data)
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    14
	return base64_decode(s_gsub(data, "[-_]", b64url_rep).."==");
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    15
end
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    16
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    17
local jwt_pattern = "^(([A-Za-z0-9-_]+)%.([A-Za-z0-9-_]+))%.([A-Za-z0-9-_]+)$"
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    18
local function decode_jwt(blob, expected_alg)
10664
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    19
	local signed, bheader, bpayload, signature = string.match(blob, jwt_pattern);
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    20
	if not signed then
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    21
		return nil, "invalid-encoding";
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    22
	end
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    23
	local header = json.decode(unb64url(bheader));
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    24
	if not header or type(header) ~= "table" then
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    25
		return nil, "invalid-header";
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    26
	elseif header.alg ~= expected_alg then
10664
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    27
		return nil, "unsupported-algorithm";
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    28
	end
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    29
	return signed, signature, bpayload;
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    30
end
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    31
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    32
local function new_static_header(algorithm_name)
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    33
	return b64url('{"alg":"'..algorithm_name..'","typ":"JWT"}') .. '.';
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    34
end
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    35
12710
108b1758bd8d util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents: 12709
diff changeset
    36
local function decode_raw_payload(raw_payload)
108b1758bd8d util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents: 12709
diff changeset
    37
	local payload, err = json.decode(unb64url(raw_payload));
108b1758bd8d util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents: 12709
diff changeset
    38
	if err ~= nil then
108b1758bd8d util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents: 12709
diff changeset
    39
		return nil, "json-decode-error";
108b1758bd8d util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents: 12709
diff changeset
    40
	elseif type(payload) ~= "table" then
108b1758bd8d util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents: 12709
diff changeset
    41
		return nil, "invalid-payload-type";
108b1758bd8d util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents: 12709
diff changeset
    42
	end
108b1758bd8d util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents: 12709
diff changeset
    43
	return true, payload;
108b1758bd8d util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents: 12709
diff changeset
    44
end
108b1758bd8d util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents: 12709
diff changeset
    45
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    46
-- HS*** family
12708
31a2bd84191d util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents: 12706
diff changeset
    47
local function new_hmac_algorithm(name)
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    48
	local static_header = new_static_header(name);
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    49
12708
31a2bd84191d util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents: 12706
diff changeset
    50
	local hmac = hashes["hmac_sha"..name:sub(-3)];
31a2bd84191d util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents: 12706
diff changeset
    51
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    52
	local function sign(key, payload)
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    53
		local encoded_payload = json.encode(payload);
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    54
		local signed = static_header .. b64url(encoded_payload);
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    55
		local signature = hmac(key, signed);
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    56
		return signed .. "." .. b64url(signature);
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    57
	end
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    58
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    59
	local function verify(key, blob)
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    60
		local signed, signature, raw_payload = decode_jwt(blob, name);
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    61
		if not signed then return nil, signature; end -- nil, err
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    62
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    63
		if not secure_equals(b64url(hmac(key, signed)), signature) then
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    64
			return false, "signature-mismatch";
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    65
		end
12710
108b1758bd8d util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents: 12709
diff changeset
    66
108b1758bd8d util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents: 12709
diff changeset
    67
		return decode_raw_payload(raw_payload);
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    68
	end
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    69
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    70
	local function load_key(key)
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    71
		assert(type(key) == "string", "key must be string (long, random, secure)");
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    72
		return key;
10664
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
    73
	end
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    74
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    75
	return { sign = sign, verify = verify, load_key = load_key };
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    76
end
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    77
12703
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
    78
local function new_crypto_algorithm(name, key_type, c_sign, c_verify, sig_encode, sig_decode)
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    79
	local static_header = new_static_header(name);
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    80
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    81
	return {
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    82
		sign = function (private_key, payload)
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    83
			local encoded_payload = json.encode(payload);
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    84
			local signed = static_header .. b64url(encoded_payload);
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    85
12703
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
    86
			local signature = c_sign(private_key, signed);
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
    87
			if sig_encode then
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
    88
				signature = sig_encode(signature);
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
    89
			end
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    90
12703
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
    91
			return signed.."."..b64url(signature);
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    92
		end;
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    93
12703
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
    94
		verify = function (public_key, blob)
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    95
			local signed, signature, raw_payload = decode_jwt(blob, name);
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    96
			if not signed then return nil, signature; end -- nil, err
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
    97
12703
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
    98
			signature = unb64url(signature);
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
    99
			if sig_decode and signature then
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   100
				signature = sig_decode(signature);
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   101
			end
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   102
			if not signature then
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   103
				return false, "signature-mismatch";
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   104
			end
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   105
12703
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   106
			local verify_ok = c_verify(public_key, signed, signature);
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   107
			if not verify_ok then
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   108
				return false, "signature-mismatch";
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   109
			end
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   110
12710
108b1758bd8d util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents: 12709
diff changeset
   111
			return decode_raw_payload(raw_payload);
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   112
		end;
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   113
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   114
		load_public_key = function (public_key_pem)
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   115
			local key = assert(crypto.import_public_pem(public_key_pem));
12703
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   116
			assert(key:get_type() == key_type, "incorrect key type");
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   117
			return key;
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   118
		end;
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   119
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   120
		load_private_key = function (private_key_pem)
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   121
			local key = assert(crypto.import_private_pem(private_key_pem));
12703
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   122
			assert(key:get_type() == key_type, "incorrect key type");
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   123
			return key;
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   124
		end;
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   125
	};
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   126
end
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   127
12703
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   128
-- RS***, PS***
12708
31a2bd84191d util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents: 12706
diff changeset
   129
local rsa_sign_algos = { RS = "rsassa_pkcs1", PS = "rsassa_pss" };
31a2bd84191d util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents: 12706
diff changeset
   130
local function new_rsa_algorithm(name)
31a2bd84191d util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents: 12706
diff changeset
   131
	local family, digest_bits = name:match("^(..)(...)$");
31a2bd84191d util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents: 12706
diff changeset
   132
	local c_sign = crypto[rsa_sign_algos[family].."_sha"..digest_bits.."_sign"];
31a2bd84191d util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents: 12706
diff changeset
   133
	local c_verify = crypto[rsa_sign_algos[family].."_sha"..digest_bits.."_verify"];
12703
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   134
	return new_crypto_algorithm(name, "rsaEncryption", c_sign, c_verify);
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   135
end
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   136
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   137
-- ES***
12739
445f7bd6ffc4 util.crypto, util.jwt: Generate consistent signature sizes (via padding)
Matthew Wild <mwild1@gmail.com>
parents: 12711
diff changeset
   138
local function new_ecdsa_algorithm(name, c_sign, c_verify, sig_bytes)
12703
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   139
	local function encode_ecdsa_sig(der_sig)
12739
445f7bd6ffc4 util.crypto, util.jwt: Generate consistent signature sizes (via padding)
Matthew Wild <mwild1@gmail.com>
parents: 12711
diff changeset
   140
		local r, s = crypto.parse_ecdsa_signature(der_sig, sig_bytes);
12703
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   141
		return r..s;
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   142
	end
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   143
12742
62100f31eb8a util.jwt: More robust ECDSA signature parsing, fail early on unexpected length
Matthew Wild <mwild1@gmail.com>
parents: 12740
diff changeset
   144
	local expected_sig_length = sig_bytes*2;
12703
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   145
	local function decode_ecdsa_sig(jwk_sig)
12742
62100f31eb8a util.jwt: More robust ECDSA signature parsing, fail early on unexpected length
Matthew Wild <mwild1@gmail.com>
parents: 12740
diff changeset
   146
		if #jwk_sig ~= expected_sig_length then
62100f31eb8a util.jwt: More robust ECDSA signature parsing, fail early on unexpected length
Matthew Wild <mwild1@gmail.com>
parents: 12740
diff changeset
   147
			return nil;
62100f31eb8a util.jwt: More robust ECDSA signature parsing, fail early on unexpected length
Matthew Wild <mwild1@gmail.com>
parents: 12740
diff changeset
   148
		end
62100f31eb8a util.jwt: More robust ECDSA signature parsing, fail early on unexpected length
Matthew Wild <mwild1@gmail.com>
parents: 12740
diff changeset
   149
		return crypto.build_ecdsa_signature(jwk_sig:sub(1, sig_bytes), jwk_sig:sub(sig_bytes+1));
12703
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   150
	end
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   151
	return new_crypto_algorithm(name, "id-ecPublicKey", c_sign, c_verify, encode_ecdsa_sig, decode_ecdsa_sig);
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   152
end
b3d0c1457584 util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents: 12700
diff changeset
   153
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   154
local algorithms = {
12708
31a2bd84191d util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents: 12706
diff changeset
   155
	HS256 = new_hmac_algorithm("HS256"), HS384 = new_hmac_algorithm("HS384"), HS512 = new_hmac_algorithm("HS512");
12739
445f7bd6ffc4 util.crypto, util.jwt: Generate consistent signature sizes (via padding)
Matthew Wild <mwild1@gmail.com>
parents: 12711
diff changeset
   156
	ES256 = new_ecdsa_algorithm("ES256", crypto.ecdsa_sha256_sign, crypto.ecdsa_sha256_verify, 32);
12740
ad4ab01f9b11 util.jwt: Add support for ES512 (+ tests)
Matthew Wild <mwild1@gmail.com>
parents: 12739
diff changeset
   157
	ES512 = new_ecdsa_algorithm("ES512", crypto.ecdsa_sha512_sign, crypto.ecdsa_sha512_verify, 66);
12708
31a2bd84191d util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents: 12706
diff changeset
   158
	RS256 = new_rsa_algorithm("RS256"), RS384 = new_rsa_algorithm("RS384"), RS512 = new_rsa_algorithm("RS512");
31a2bd84191d util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents: 12706
diff changeset
   159
	PS256 = new_rsa_algorithm("PS256"), PS384 = new_rsa_algorithm("PS384"), PS512 = new_rsa_algorithm("PS512");
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   160
};
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   161
12709
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   162
local function new_signer(algorithm, key_input, options)
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   163
	local impl = assert(algorithms[algorithm], "Unknown JWT algorithm: "..algorithm);
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   164
	local key = (impl.load_private_key or impl.load_key)(key_input);
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   165
	local sign = impl.sign;
12709
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   166
	local default_ttl = (options and options.default_ttl) or 3600;
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   167
	return function (payload)
12709
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   168
		local issued_at;
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   169
		if not payload.iat then
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   170
			issued_at = os.time();
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   171
			payload.iat = issued_at;
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   172
		end
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   173
		if not payload.exp then
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   174
			payload.exp = (issued_at or os.time()) + default_ttl;
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   175
		end
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   176
		return sign(key, payload);
10664
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
   177
	end
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   178
end
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   179
12709
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   180
local function new_verifier(algorithm, key_input, options)
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   181
	local impl = assert(algorithms[algorithm], "Unknown JWT algorithm: "..algorithm);
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   182
	local key = (impl.load_public_key or impl.load_key)(key_input);
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   183
	local verify = impl.verify;
12709
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   184
	local check_expiry = not (options and options.accept_expired);
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   185
	local claim_verifier = options and options.claim_verifier;
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   186
	return function (token)
12709
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   187
		local ok, payload = verify(key, token);
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   188
		if ok then
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   189
			local expires_at = check_expiry and payload.exp;
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   190
			if expires_at then
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   191
				if type(expires_at) ~= "number" then
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   192
					return nil, "invalid-expiry";
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   193
				elseif expires_at < os.time() then
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   194
					return nil, "token-expired";
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   195
				end
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   196
			end
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   197
			if claim_verifier and not claim_verifier(payload) then
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   198
				return nil, "incorrect-claims";
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   199
			end
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   200
		end
008a7097fdc5 util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents: 12708
diff changeset
   201
		return ok, payload;
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   202
	end
10664
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
   203
end
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
   204
12711
f75235110045 util.jwt: Add new init() convenience method to obtain both signer and verifier
Matthew Wild <mwild1@gmail.com>
parents: 12710
diff changeset
   205
local function init(algorithm, private_key, public_key, options)
f75235110045 util.jwt: Add new init() convenience method to obtain both signer and verifier
Matthew Wild <mwild1@gmail.com>
parents: 12710
diff changeset
   206
	return new_signer(algorithm, private_key, options), new_verifier(algorithm, public_key or private_key, options);
f75235110045 util.jwt: Add new init() convenience method to obtain both signer and verifier
Matthew Wild <mwild1@gmail.com>
parents: 12710
diff changeset
   207
end
f75235110045 util.jwt: Add new init() convenience method to obtain both signer and verifier
Matthew Wild <mwild1@gmail.com>
parents: 12710
diff changeset
   208
10664
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
   209
return {
12711
f75235110045 util.jwt: Add new init() convenience method to obtain both signer and verifier
Matthew Wild <mwild1@gmail.com>
parents: 12710
diff changeset
   210
	init = init;
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   211
	new_signer = new_signer;
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   212
	new_verifier = new_verifier;
12711
f75235110045 util.jwt: Add new init() convenience method to obtain both signer and verifier
Matthew Wild <mwild1@gmail.com>
parents: 12710
diff changeset
   213
	-- Exported mainly for tests
12708
31a2bd84191d util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents: 12706
diff changeset
   214
	_algorithms = algorithms;
12700
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   215
	-- Deprecated
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   216
	sign = algorithms.HS256.sign;
27a72982e331 util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents: 11565
diff changeset
   217
	verify = algorithms.HS256.verify;
10664
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
   218
};
c4ded3be7cc0 util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff changeset
   219