Software /
code /
prosody
Annotate
util/jwt.lua @ 13270:14bbfb2cc8dd default tip
lint: Teach luacheck about module:once
Silence warning for using this introduced in 9c62ffbdf2ae
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sun, 15 Oct 2023 16:43:14 +0200 |
parent | 12975:d10957394a3c |
rev | line source |
---|---|
10660
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; |
12975
d10957394a3c
util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents:
12738
diff
changeset
|
2 local crypto = require "prosody.util.crypto"; |
d10957394a3c
util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents:
12738
diff
changeset
|
3 local json = require "prosody.util.json"; |
d10957394a3c
util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents:
12738
diff
changeset
|
4 local hashes = require "prosody.util.hashes"; |
d10957394a3c
util: Prefix module imports with prosody namespace
Kim Alvefur <zash@zash.se>
parents:
12738
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:
12738
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:
12738
diff
changeset
|
7 local secure_equals = require "prosody.util.hashes".equals; |
10660
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-_]+)$" |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
diff
changeset
|
18 local function decode_jwt(blob, expected_alg) |
10660
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"; |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
diff
changeset
|
26 elseif header.alg ~= expected_alg then |
10660
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 |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
diff
changeset
|
31 |
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
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:
11561
diff
changeset
|
35 |
12706
108b1758bd8d
util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents:
12705
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:
12705
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:
12705
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:
12705
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:
12705
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:
12705
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:
12705
diff
changeset
|
42 end |
108b1758bd8d
util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents:
12705
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:
12705
diff
changeset
|
44 end |
108b1758bd8d
util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents:
12705
diff
changeset
|
45 |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
diff
changeset
|
46 -- HS*** family |
12704
31a2bd84191d
util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents:
12702
diff
changeset
|
47 local function new_hmac_algorithm(name) |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
diff
changeset
|
49 |
12704
31a2bd84191d
util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents:
12702
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:
12702
diff
changeset
|
51 |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
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:
11561
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:
11561
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:
11561
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:
11561
diff
changeset
|
58 |
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
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:
11561
diff
changeset
|
62 |
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
diff
changeset
|
65 end |
12706
108b1758bd8d
util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents:
12705
diff
changeset
|
66 |
108b1758bd8d
util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents:
12705
diff
changeset
|
67 return decode_raw_payload(raw_payload); |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
diff
changeset
|
69 |
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
diff
changeset
|
72 return key; |
10660
c4ded3be7cc0
util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
73 end |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
diff
changeset
|
74 |
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
diff
changeset
|
77 |
12699
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
78 local function new_crypto_algorithm(name, key_type, c_sign, c_verify, sig_encode, sig_decode) |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
diff
changeset
|
80 |
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
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:
11561
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:
11561
diff
changeset
|
85 |
12699
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
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:
12696
diff
changeset
|
87 if sig_encode then |
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
88 signature = sig_encode(signature); |
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
89 end |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
diff
changeset
|
90 |
12699
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
91 return signed.."."..b64url(signature); |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
diff
changeset
|
93 |
12699
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
94 verify = function (public_key, blob) |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
diff
changeset
|
97 |
12699
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
98 signature = unb64url(signature); |
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
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:
12696
diff
changeset
|
100 signature = sig_decode(signature); |
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
101 end |
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
102 if not signature then |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
diff
changeset
|
105 |
12699
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
106 local verify_ok = c_verify(public_key, signed, signature); |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
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:
11561
diff
changeset
|
110 |
12706
108b1758bd8d
util.jwt: Consolidate payload parsing, ensure it's always a valid object
Matthew Wild <mwild1@gmail.com>
parents:
12705
diff
changeset
|
111 return decode_raw_payload(raw_payload); |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
diff
changeset
|
113 |
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
diff
changeset
|
115 local key = assert(crypto.import_public_pem(public_key_pem)); |
12699
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
116 assert(key:get_type() == key_type, "incorrect key type"); |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
diff
changeset
|
119 |
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
diff
changeset
|
121 local key = assert(crypto.import_private_pem(private_key_pem)); |
12699
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
122 assert(key:get_type() == key_type, "incorrect key type"); |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
diff
changeset
|
125 }; |
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
diff
changeset
|
127 |
12699
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
128 -- RS***, PS*** |
12704
31a2bd84191d
util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents:
12702
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:
12702
diff
changeset
|
130 local function new_rsa_algorithm(name) |
31a2bd84191d
util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents:
12702
diff
changeset
|
131 local family, digest_bits = name:match("^(..)(...)$"); |
31a2bd84191d
util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents:
12702
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:
12702
diff
changeset
|
133 local c_verify = crypto[rsa_sign_algos[family].."_sha"..digest_bits.."_verify"]; |
12699
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
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:
12696
diff
changeset
|
135 end |
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
136 |
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
137 -- ES*** |
12735
445f7bd6ffc4
util.crypto, util.jwt: Generate consistent signature sizes (via padding)
Matthew Wild <mwild1@gmail.com>
parents:
12707
diff
changeset
|
138 local function new_ecdsa_algorithm(name, c_sign, c_verify, sig_bytes) |
12699
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
139 local function encode_ecdsa_sig(der_sig) |
12735
445f7bd6ffc4
util.crypto, util.jwt: Generate consistent signature sizes (via padding)
Matthew Wild <mwild1@gmail.com>
parents:
12707
diff
changeset
|
140 local r, s = crypto.parse_ecdsa_signature(der_sig, sig_bytes); |
12699
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
141 return r..s; |
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
142 end |
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
143 |
12738
62100f31eb8a
util.jwt: More robust ECDSA signature parsing, fail early on unexpected length
Matthew Wild <mwild1@gmail.com>
parents:
12736
diff
changeset
|
144 local expected_sig_length = sig_bytes*2; |
12699
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
145 local function decode_ecdsa_sig(jwk_sig) |
12738
62100f31eb8a
util.jwt: More robust ECDSA signature parsing, fail early on unexpected length
Matthew Wild <mwild1@gmail.com>
parents:
12736
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:
12736
diff
changeset
|
147 return nil; |
62100f31eb8a
util.jwt: More robust ECDSA signature parsing, fail early on unexpected length
Matthew Wild <mwild1@gmail.com>
parents:
12736
diff
changeset
|
148 end |
62100f31eb8a
util.jwt: More robust ECDSA signature parsing, fail early on unexpected length
Matthew Wild <mwild1@gmail.com>
parents:
12736
diff
changeset
|
149 return crypto.build_ecdsa_signature(jwk_sig:sub(1, sig_bytes), jwk_sig:sub(sig_bytes+1)); |
12699
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
150 end |
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
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:
12696
diff
changeset
|
152 end |
b3d0c1457584
util.jwt: Add support for RSA-based algorithms (RS256, PS256)
Matthew Wild <mwild1@gmail.com>
parents:
12696
diff
changeset
|
153 |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
diff
changeset
|
154 local algorithms = { |
12704
31a2bd84191d
util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents:
12702
diff
changeset
|
155 HS256 = new_hmac_algorithm("HS256"), HS384 = new_hmac_algorithm("HS384"), HS512 = new_hmac_algorithm("HS512"); |
12735
445f7bd6ffc4
util.crypto, util.jwt: Generate consistent signature sizes (via padding)
Matthew Wild <mwild1@gmail.com>
parents:
12707
diff
changeset
|
156 ES256 = new_ecdsa_algorithm("ES256", crypto.ecdsa_sha256_sign, crypto.ecdsa_sha256_verify, 32); |
12736
ad4ab01f9b11
util.jwt: Add support for ES512 (+ tests)
Matthew Wild <mwild1@gmail.com>
parents:
12735
diff
changeset
|
157 ES512 = new_ecdsa_algorithm("ES512", crypto.ecdsa_sha512_sign, crypto.ecdsa_sha512_verify, 66); |
12704
31a2bd84191d
util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents:
12702
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:
12702
diff
changeset
|
159 PS256 = new_rsa_algorithm("PS256"), PS384 = new_rsa_algorithm("PS384"), PS512 = new_rsa_algorithm("PS512"); |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
diff
changeset
|
160 }; |
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
diff
changeset
|
161 |
12705
008a7097fdc5
util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents:
12704
diff
changeset
|
162 local function new_signer(algorithm, key_input, options) |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
diff
changeset
|
165 local sign = impl.sign; |
12705
008a7097fdc5
util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents:
12704
diff
changeset
|
166 local default_ttl = (options and options.default_ttl) or 3600; |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
diff
changeset
|
167 return function (payload) |
12705
008a7097fdc5
util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents:
12704
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:
12704
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:
12704
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:
12704
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:
12704
diff
changeset
|
172 end |
008a7097fdc5
util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents:
12704
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:
12704
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:
12704
diff
changeset
|
175 end |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
diff
changeset
|
176 return sign(key, payload); |
10660
c4ded3be7cc0
util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
177 end |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
diff
changeset
|
179 |
12705
008a7097fdc5
util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents:
12704
diff
changeset
|
180 local function new_verifier(algorithm, key_input, options) |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
diff
changeset
|
183 local verify = impl.verify; |
12705
008a7097fdc5
util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents:
12704
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:
12704
diff
changeset
|
185 local claim_verifier = options and options.claim_verifier; |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
diff
changeset
|
186 return function (token) |
12705
008a7097fdc5
util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents:
12704
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:
12704
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:
12704
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:
12704
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:
12704
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:
12704
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:
12704
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:
12704
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:
12704
diff
changeset
|
195 end |
008a7097fdc5
util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents:
12704
diff
changeset
|
196 end |
008a7097fdc5
util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents:
12704
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:
12704
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:
12704
diff
changeset
|
199 end |
008a7097fdc5
util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents:
12704
diff
changeset
|
200 end |
008a7097fdc5
util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime)
Matthew Wild <mwild1@gmail.com>
parents:
12704
diff
changeset
|
201 return ok, payload; |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
diff
changeset
|
202 end |
10660
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 |
12707
f75235110045
util.jwt: Add new init() convenience method to obtain both signer and verifier
Matthew Wild <mwild1@gmail.com>
parents:
12706
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:
12706
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:
12706
diff
changeset
|
207 end |
f75235110045
util.jwt: Add new init() convenience method to obtain both signer and verifier
Matthew Wild <mwild1@gmail.com>
parents:
12706
diff
changeset
|
208 |
10660
c4ded3be7cc0
util.jwt: Basic JSON Web Token library supporting HS256 tokens
Kim Alvefur <zash@zash.se>
parents:
diff
changeset
|
209 return { |
12707
f75235110045
util.jwt: Add new init() convenience method to obtain both signer and verifier
Matthew Wild <mwild1@gmail.com>
parents:
12706
diff
changeset
|
210 init = init; |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
diff
changeset
|
212 new_verifier = new_verifier; |
12707
f75235110045
util.jwt: Add new init() convenience method to obtain both signer and verifier
Matthew Wild <mwild1@gmail.com>
parents:
12706
diff
changeset
|
213 -- Exported mainly for tests |
12704
31a2bd84191d
util.jwt: All the algorithms (+ all the tests!)
Matthew Wild <mwild1@gmail.com>
parents:
12702
diff
changeset
|
214 _algorithms = algorithms; |
12696
27a72982e331
util.jwt: Add support/tests for ES256 via improved API and using util.crypto
Matthew Wild <mwild1@gmail.com>
parents:
11561
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:
11561
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:
11561
diff
changeset
|
217 verify = algorithms.HS256.verify; |
10660
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 |