util/jwt.lua
author Kim Alvefur <zash@zash.se>
Sun, 24 Mar 2024 21:31:47 +0100
changeset 13467 3ce550ce44ce
parent 12979 d10957394a3c
permissions -rw-r--r--
util.startup: Don't use not yet existent shutdown procedure when started as root (thanks SigmaTel71)
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