line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Crypt::JWT; |
2
|
|
|
|
|
|
|
|
3
|
7
|
|
|
7
|
|
424235
|
use strict; |
|
7
|
|
|
|
|
70
|
|
|
7
|
|
|
|
|
213
|
|
4
|
7
|
|
|
7
|
|
35
|
use warnings; |
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
284
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = '0.033'; |
7
|
|
|
|
|
|
|
|
8
|
7
|
|
|
7
|
|
38
|
use Exporter 'import'; |
|
7
|
|
|
|
|
13
|
|
|
7
|
|
|
|
|
632
|
|
9
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( all => [qw(decode_jwt encode_jwt)] ); |
10
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); |
11
|
|
|
|
|
|
|
our @EXPORT = qw(); |
12
|
|
|
|
|
|
|
|
13
|
7
|
|
|
7
|
|
90
|
use Carp; |
|
7
|
|
|
|
|
30
|
|
|
7
|
|
|
|
|
568
|
|
14
|
7
|
|
|
7
|
|
3638
|
use Crypt::Misc qw(decode_b64u encode_b64u); |
|
7
|
|
|
|
|
116382
|
|
|
7
|
|
|
|
|
596
|
|
15
|
7
|
|
|
7
|
|
4735
|
use JSON qw(decode_json encode_json); |
|
7
|
|
|
|
|
58684
|
|
|
7
|
|
|
|
|
38
|
|
16
|
7
|
|
|
7
|
|
5358
|
use Crypt::PK::RSA; |
|
7
|
|
|
|
|
20547
|
|
|
7
|
|
|
|
|
329
|
|
17
|
7
|
|
|
7
|
|
4500
|
use Crypt::PK::ECC; |
|
7
|
|
|
|
|
22752
|
|
|
7
|
|
|
|
|
350
|
|
18
|
7
|
|
|
7
|
|
3582
|
use Crypt::PK::Ed25519; |
|
7
|
|
|
|
|
11959
|
|
|
7
|
|
|
|
|
352
|
|
19
|
7
|
|
|
7
|
|
3614
|
use Crypt::PK::X25519; |
|
7
|
|
|
|
|
10509
|
|
|
7
|
|
|
|
|
435
|
|
20
|
7
|
|
|
7
|
|
55
|
use Crypt::PRNG qw(random_bytes); |
|
7
|
|
|
|
|
17
|
|
|
7
|
|
|
|
|
377
|
|
21
|
7
|
|
|
7
|
|
3103
|
use Crypt::KeyWrap ':all'; |
|
7
|
|
|
|
|
20
|
|
|
7
|
|
|
|
|
1182
|
|
22
|
7
|
|
|
7
|
|
55
|
use Crypt::AuthEnc::GCM qw(gcm_encrypt_authenticate gcm_decrypt_verify); |
|
7
|
|
|
|
|
12
|
|
|
7
|
|
|
|
|
329
|
|
23
|
7
|
|
|
7
|
|
3452
|
use Crypt::Mac::HMAC qw(hmac); |
|
7
|
|
|
|
|
8542
|
|
|
7
|
|
|
|
|
384
|
|
24
|
7
|
|
|
7
|
|
4632
|
use Compress::Raw::Zlib; |
|
7
|
|
|
|
|
39494
|
|
|
7
|
|
|
|
|
1597
|
|
25
|
7
|
|
|
7
|
|
58
|
use Scalar::Util qw(looks_like_number); |
|
7
|
|
|
|
|
19
|
|
|
7
|
|
|
|
|
53549
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# JWS: https://tools.ietf.org/html/rfc7515 |
28
|
|
|
|
|
|
|
# JWE: https://tools.ietf.org/html/rfc7516 |
29
|
|
|
|
|
|
|
# JWK: https://tools.ietf.org/html/rfc7517 |
30
|
|
|
|
|
|
|
# JWA: https://tools.ietf.org/html/rfc7518 |
31
|
|
|
|
|
|
|
# JWT: https://tools.ietf.org/html/rfc7519 |
32
|
|
|
|
|
|
|
# X25519/Ed25519 https://tools.ietf.org/html/rfc8037 |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
sub _prepare_rsa_key { |
35
|
87
|
|
|
87
|
|
185
|
my ($key) = @_; |
36
|
87
|
50
|
|
|
|
221
|
croak "JWT: undefined RSA key" unless defined $key; |
37
|
87
|
100
|
|
|
|
491
|
croak "JWT: invalid RSA key (cannot be scalar)" unless ref $key; |
38
|
|
|
|
|
|
|
# we need Crypt::PK::RSA object |
39
|
86
|
100
|
|
|
|
305
|
return $key if ref($key) eq 'Crypt::PK::RSA'; |
40
|
38
|
50
|
66
|
|
|
298
|
return Crypt::PK::RSA->new($key) if ref($key) eq 'HASH' || ref($key) eq 'SCALAR'; |
41
|
0
|
0
|
|
|
|
0
|
return Crypt::PK::RSA->new(@$key) if ref($key) eq 'ARRAY'; |
42
|
|
|
|
|
|
|
# handle also: Crypt::OpenSSL::RSA, Crypt::X509, Crypt::OpenSSL::X509 |
43
|
0
|
|
|
|
|
0
|
my $str; |
44
|
0
|
0
|
|
|
|
0
|
if (ref($key) eq 'Crypt::OpenSSL::RSA') { |
|
|
0
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# https://metacpan.org/pod/Crypt::OpenSSL::RSA |
46
|
0
|
0
|
|
|
|
0
|
$str = $key->is_private ? $key->get_private_key_string : $key->get_public_key_string; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
elsif (ref($key) =~ /^Crypt::(X509|OpenSSL::X509)$/) { |
49
|
|
|
|
|
|
|
# https://metacpan.org/pod/Crypt::X509 |
50
|
|
|
|
|
|
|
# https://metacpan.org/pod/Crypt::OpenSSL::X509 |
51
|
0
|
|
|
|
|
0
|
$str = $key->pubkey; |
52
|
|
|
|
|
|
|
} |
53
|
0
|
0
|
0
|
|
|
0
|
return Crypt::PK::RSA->new(\$str) if defined $str && !ref($str); |
54
|
0
|
|
|
|
|
0
|
croak "JWT: invalid RSA key"; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
sub _prepare_ecc_key { |
58
|
13
|
|
|
13
|
|
33
|
my ($key) = @_; |
59
|
13
|
50
|
|
|
|
33
|
croak "JWT: undefined ECC key" unless defined $key; |
60
|
13
|
100
|
|
|
|
164
|
croak "JWT: invalid ECC key (cannot be scalar)" unless ref $key; |
61
|
|
|
|
|
|
|
# we need Crypt::PK::ECC object |
62
|
12
|
100
|
|
|
|
39
|
return $key if ref($key) eq 'Crypt::PK::ECC'; |
63
|
4
|
50
|
33
|
|
|
44
|
return Crypt::PK::ECC->new($key) if ref($key) eq 'HASH' || ref($key) eq 'SCALAR'; |
64
|
0
|
0
|
|
|
|
0
|
return Crypt::PK::ECC->new(@$key) if ref($key) eq 'ARRAY'; |
65
|
0
|
|
|
|
|
0
|
croak "JWT: invalid ECC key"; |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub _prepare_ed25519_key { |
69
|
2
|
|
|
2
|
|
6
|
my ($key) = @_; |
70
|
2
|
50
|
|
|
|
8
|
croak "JWT: undefined Ed25519 key" unless defined $key; |
71
|
2
|
50
|
|
|
|
7
|
croak "JWT: invalid Ed25519 key (cannot be scalar)" unless ref $key; |
72
|
|
|
|
|
|
|
# we need Crypt::PK::Ed25519 object |
73
|
2
|
50
|
|
|
|
9
|
return $key if ref($key) eq 'Crypt::PK::Ed25519'; |
74
|
2
|
50
|
33
|
|
|
30
|
return Crypt::PK::Ed25519->new($key) if ref($key) eq 'HASH' || ref($key) eq 'SCALAR'; |
75
|
0
|
0
|
|
|
|
0
|
return Crypt::PK::Ed25519->new(@$key) if ref($key) eq 'ARRAY'; |
76
|
0
|
|
|
|
|
0
|
croak "JWT: invalid Ed25519 key"; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
sub _prepare_ecdh_key { |
80
|
57
|
|
|
57
|
|
111
|
my ($key) = @_; |
81
|
57
|
50
|
|
|
|
138
|
croak "JWT: undefined ECDH key" unless defined $key; |
82
|
57
|
50
|
|
|
|
129
|
croak "JWT: invalid ECDH key (cannot be scalar)" unless ref $key; |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# we need Crypt::PK::X25519 or Crypt::PK::ECC object |
85
|
57
|
100
|
|
|
|
228
|
return $key if ref($key) =~ /^Crypt::PK::(ECC|X25519)$/; |
86
|
|
|
|
|
|
|
|
87
|
9
|
50
|
33
|
|
|
36
|
if (ref($key) eq 'HASH' || ref($key) eq 'SCALAR') { |
88
|
|
|
|
|
|
|
#HACK: this is ugly |
89
|
9
|
|
66
|
|
|
14
|
my $rv = eval { Crypt::PK::ECC->new($key) } || eval { Crypt::PK::X25519->new($key) }; |
90
|
9
|
50
|
|
|
|
44100
|
return $rv if defined $rv; |
91
|
|
|
|
|
|
|
} |
92
|
0
|
0
|
|
|
|
0
|
if (ref($key) eq 'ARRAY') { |
93
|
|
|
|
|
|
|
#HACK: this is ugly |
94
|
0
|
|
0
|
|
|
0
|
my $rv = eval { Crypt::PK::ECC->new(@$key) } || eval { Crypt::PK::X25519->new(@$key) }; |
95
|
0
|
0
|
|
|
|
0
|
return $rv if defined $rv; |
96
|
|
|
|
|
|
|
} |
97
|
0
|
|
|
|
|
0
|
croak "JWT: invalid ECDH key"; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub _prepare_oct_key { |
101
|
254
|
|
|
254
|
|
489
|
my ($key) = @_; |
102
|
254
|
50
|
|
|
|
561
|
croak "JWT: undefined oct key" unless defined $key; |
103
|
254
|
100
|
66
|
|
|
915
|
if (ref $key eq 'HASH' && $key->{k} && $key->{kty} && $key->{kty} eq 'oct') { |
|
|
50
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
104
|
7
|
|
|
|
|
46
|
return decode_b64u($key->{k}); |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
elsif (!ref $key) { |
107
|
247
|
|
|
|
|
1051
|
return $key; |
108
|
|
|
|
|
|
|
} |
109
|
0
|
|
|
|
|
0
|
croak "JWT: invalid oct key"; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
sub _kid_lookup { |
113
|
4
|
|
|
4
|
|
13
|
my ($kid, $kid_keys, $alg) = @_; |
114
|
4
|
50
|
33
|
|
|
18
|
return undef if !defined $kid || !defined $alg; |
115
|
4
|
100
|
66
|
|
|
21
|
$kid_keys = eval { decode_json($kid_keys) } if $kid_keys && !ref $kid_keys; |
|
2
|
|
|
|
|
16
|
|
116
|
4
|
50
|
|
|
|
14
|
croak "JWT: kid_keys must be a HASHREF or a valid JSON/HASH" if ref $kid_keys ne 'HASH'; |
117
|
4
|
|
|
|
|
7
|
my $found; |
118
|
4
|
50
|
33
|
|
|
24
|
if (exists $kid_keys->{keys} && ref $kid_keys->{keys} eq 'ARRAY') { |
119
|
|
|
|
|
|
|
#FORMAT: { keys => [ {kid=>'A', kty=>?, ...}, {kid=>'B', kty=>?, ...} ] } |
120
|
4
|
|
|
|
|
8
|
for (@{$kid_keys->{keys}}) { |
|
4
|
|
|
|
|
11
|
|
121
|
8
|
100
|
33
|
|
|
43
|
if ($_->{kid} && $_->{kty} && $_->{kid} eq $kid) { |
|
|
|
66
|
|
|
|
|
122
|
4
|
|
|
|
|
9
|
$found = $_; |
123
|
4
|
|
|
|
|
9
|
last; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
else { |
128
|
|
|
|
|
|
|
#FORMAT: { hexadec1 => "----BEGIN CERTIFICATE-----...", hexadec2 => "----BEGIN CERTIFICATE-----..." } |
129
|
|
|
|
|
|
|
#e.g. https://www.googleapis.com/oauth2/v1/certs |
130
|
0
|
0
|
0
|
|
|
0
|
return \$kid_keys->{$kid} if $kid_keys->{$kid} && !ref $kid_keys->{$kid}; |
131
|
|
|
|
|
|
|
} |
132
|
4
|
50
|
|
|
|
9
|
return undef if !$found; |
133
|
4
|
50
|
33
|
|
|
38
|
return $found if $found->{kty} eq 'oct' && $alg =~ /^(HS|dir|PBES2-HS|A)/; |
134
|
0
|
0
|
0
|
|
|
0
|
return $found if $found->{kty} eq 'OKP' && $alg =~ /^(EdDSA|ECDH-ES)/; |
135
|
0
|
0
|
0
|
|
|
0
|
return $found if $found->{kty} eq 'EC' && $alg =~ /^(ES|EC)/; |
136
|
0
|
0
|
0
|
|
|
0
|
return $found if $found->{kty} eq 'RSA' && $alg =~ /^(RS|PS)/; |
137
|
0
|
|
|
|
|
0
|
croak "JWT: key type '$found->{kty}' cannot be used with alg '$alg'"; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub _b64u_to_hash { |
141
|
279
|
|
|
279
|
|
566
|
my $b64url = shift; |
142
|
279
|
50
|
|
|
|
683
|
return undef unless $b64url; |
143
|
279
|
|
|
|
|
1298
|
my $json = decode_b64u($b64url); |
144
|
279
|
50
|
|
|
|
659
|
return undef unless $json; |
145
|
279
|
|
|
|
|
442
|
my $hash = eval { decode_json($json) }; |
|
279
|
|
|
|
|
1854
|
|
146
|
279
|
50
|
|
|
|
941
|
return undef unless ref $hash eq 'HASH'; |
147
|
279
|
|
|
|
|
536
|
return $hash; |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub _add_claims { |
151
|
19
|
|
|
19
|
|
58
|
my ($payload, %args) = @_; |
152
|
|
|
|
|
|
|
#### claims (defined for JWS only) |
153
|
|
|
|
|
|
|
# "exp" Expiration Time |
154
|
|
|
|
|
|
|
# "nbf" Not Before |
155
|
|
|
|
|
|
|
# "iat" Issued At |
156
|
|
|
|
|
|
|
# "iss" Issuer |
157
|
|
|
|
|
|
|
# "sub" Subject |
158
|
|
|
|
|
|
|
# "aud" Audience |
159
|
|
|
|
|
|
|
# "jti" JWT ID |
160
|
19
|
|
|
|
|
45
|
my $now = time; |
161
|
19
|
100
|
|
|
|
50
|
$payload->{iat} = $now if $args{auto_iat}; |
162
|
19
|
100
|
|
|
|
55
|
$payload->{exp} = $now + $args{relative_exp} if defined $args{relative_exp}; |
163
|
19
|
100
|
|
|
|
65
|
$payload->{nbf} = $now + $args{relative_nbf} if defined $args{relative_nbf}; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
sub _verify_claims { |
167
|
267
|
|
|
267
|
|
939
|
my ($payload, %args) = @_; |
168
|
|
|
|
|
|
|
|
169
|
267
|
100
|
|
|
|
715
|
return if $args{ignore_claims}; |
170
|
|
|
|
|
|
|
|
171
|
266
|
100
|
|
|
|
699
|
if (ref($payload) ne 'HASH') { |
172
|
|
|
|
|
|
|
# https://github.com/DCIT/perl-Crypt-JWT/issues/31 |
173
|
|
|
|
|
|
|
# payload needs to be decoded into a HASH for checking any verify_XXXX |
174
|
206
|
|
|
|
|
518
|
for my $claim (qw(exp nbf iat iss sub aud jti)) { |
175
|
1430
|
100
|
100
|
|
|
3178
|
if (defined $args{"verify_$claim"} && $args{"verify_$claim"} != 0) { |
176
|
2
|
|
|
|
|
435
|
croak "JWT: cannot check verify_$claim (payload not decoded JSON/HASH)"; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
} |
179
|
204
|
|
|
|
|
482
|
return; # nothing to check |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
60
|
|
100
|
|
|
205
|
my $leeway = $args{leeway} || 0; |
183
|
60
|
|
|
|
|
107
|
my $now = time; |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
### exp |
186
|
60
|
100
|
33
|
|
|
169
|
if(defined $payload->{exp}) { |
|
|
50
|
|
|
|
|
|
187
|
45
|
100
|
100
|
|
|
139
|
if (!defined $args{verify_exp} || $args{verify_exp}==1) { |
188
|
41
|
100
|
|
|
|
1076
|
croak "JWT: exp claim check failed ($payload->{exp}/$leeway vs. $now)" if $payload->{exp} + $leeway <= $now; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
elsif ($args{verify_exp} && $args{verify_exp}==1) { |
192
|
0
|
|
|
|
|
0
|
croak "JWT: exp claim required but missing" |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
### nbf |
196
|
53
|
100
|
33
|
|
|
161
|
if(defined $payload->{nbf}) { |
|
|
50
|
|
|
|
|
|
197
|
30
|
100
|
100
|
|
|
72
|
if (!defined $args{verify_nbf} || $args{verify_nbf}==1) { |
198
|
29
|
100
|
|
|
|
405
|
croak "JWT: nbf claim check failed ($payload->{nbf}/$leeway vs. $now)" if $payload->{nbf} - $leeway > $now; |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
elsif ($args{verify_nbf} && $args{verify_nbf}==1) { |
202
|
0
|
|
|
|
|
0
|
croak "JWT: nbf claim required but missing" |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
### iat |
206
|
50
|
100
|
|
|
|
109
|
if (exists $args{verify_iat}) { #default (non existing verify_iat) == no iat check |
207
|
4
|
50
|
0
|
|
|
12
|
if(defined $payload->{iat}) { |
|
|
0
|
|
|
|
|
|
208
|
4
|
100
|
100
|
|
|
17
|
if (!defined $args{verify_iat} || $args{verify_iat}==1) { |
209
|
3
|
100
|
|
|
|
240
|
croak "JWT: iat claim check failed ($payload->{iat}/$leeway vs. $now)" if $payload->{iat} - $leeway > $now; |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
elsif ($args{verify_iat} && $args{verify_iat}==1) { |
213
|
0
|
|
|
|
|
0
|
croak "JWT: iat claim required but missing" |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
### iss, sub, aud, jti |
218
|
48
|
|
|
|
|
108
|
foreach my $claim (qw(iss sub aud jti)) { |
219
|
165
|
|
|
|
|
303
|
my $check = $args{"verify_$claim"}; |
220
|
165
|
100
|
|
|
|
333
|
next unless (defined $check); |
221
|
|
|
|
|
|
|
|
222
|
47
|
100
|
|
|
|
110
|
if (exists $payload->{$claim}) { |
223
|
43
|
100
|
|
|
|
95
|
if (ref $check eq 'Regexp') { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
224
|
15
|
|
|
|
|
24
|
my $value = $payload->{$claim}; |
225
|
15
|
100
|
|
|
|
26
|
$value = "" if !defined $value; |
226
|
15
|
100
|
|
|
|
825
|
croak "JWT: $claim claim re check failed" unless $value =~ $check; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
elsif (ref $check eq 'CODE') { |
229
|
14
|
100
|
|
|
|
32
|
croak "JWT: $claim claim code check failed" unless $check->($payload->{$claim}); |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
elsif (!ref $check) { |
232
|
14
|
|
|
|
|
19
|
my $value = $payload->{$claim}; |
233
|
14
|
100
|
66
|
|
|
535
|
croak "JWT: $claim claim scalar check failed" unless defined $value && $value eq $check; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
else { |
236
|
0
|
|
|
|
|
0
|
croak "JWT: verify_$claim must be Regexp, Scalar or CODE"; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
else { |
240
|
4
|
|
|
|
|
479
|
croak "JWT: $claim claim required but missing" |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub _payload_zip { |
247
|
4
|
|
|
4
|
|
11
|
my ($payload, $header, $z) = @_; |
248
|
4
|
100
|
|
|
|
20
|
my @zip = ref $z eq 'ARRAY' ? @$z : ($z); |
249
|
4
|
50
|
|
|
|
12
|
if ($zip[0] eq 'deflate') { |
250
|
4
|
100
|
|
|
|
10
|
my $level = defined $zip[1] ? $zip[1] : 6; |
251
|
4
|
|
|
|
|
11
|
$header->{zip} = "DEF"; |
252
|
4
|
|
|
|
|
17
|
my $d = Compress::Raw::Zlib::Deflate->new(-Bufsize => 1024, -WindowBits => -&MAX_WBITS(), -AppendOutput => 1, -Level => $level ); |
253
|
4
|
|
|
|
|
3312
|
my $output = ''; |
254
|
4
|
50
|
|
|
|
50
|
$d->deflate($payload, $output) == Z_OK or croak "JWT: deflate failed"; |
255
|
4
|
50
|
|
|
|
197
|
$d->flush($output) == Z_OK or croak "JWT: deflate/flush failed"; |
256
|
4
|
50
|
|
|
|
32
|
croak "JWT: deflate/output failed" unless $output; |
257
|
4
|
|
|
|
|
180
|
$payload = $output; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
else { |
260
|
0
|
|
|
|
|
0
|
croak "JWT: unknown zip method '$zip[0]'"; |
261
|
|
|
|
|
|
|
} |
262
|
4
|
|
|
|
|
20
|
return $payload; |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub _payload_unzip { |
266
|
15
|
|
|
15
|
|
49
|
my ($payload, $z) = @_; |
267
|
15
|
50
|
|
|
|
53
|
if ($z eq "DEF") { |
268
|
15
|
|
|
|
|
78
|
my $d = Compress::Raw::Zlib::Inflate->new(-Bufsize => 1024, -WindowBits => -&MAX_WBITS()); |
269
|
15
|
|
|
|
|
6947
|
my $output = ''; |
270
|
15
|
|
|
|
|
247
|
$d->inflate($payload, $output); |
271
|
15
|
50
|
|
|
|
50
|
croak "JWT: inflate failed" unless $output; |
272
|
15
|
|
|
|
|
79
|
$payload = $output; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
else { |
275
|
0
|
|
|
|
|
0
|
croak "JWT: unknown zip method '$z'"; |
276
|
|
|
|
|
|
|
} |
277
|
15
|
|
|
|
|
44
|
return $payload; |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
sub _payload_enc { |
281
|
148
|
|
|
148
|
|
339
|
my ($payload) = @_; |
282
|
148
|
100
|
|
|
|
437
|
if (ref($payload) =~ /^(HASH|ARRAY)$/) { |
283
|
21
|
|
|
|
|
144
|
$payload = encode_json($payload); |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
else { |
286
|
127
|
50
|
|
|
|
443
|
utf8::downgrade($payload, 1) or croak "JWT: payload cannot contain wide character"; |
287
|
|
|
|
|
|
|
} |
288
|
148
|
|
|
|
|
345
|
return $payload; |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
sub _payload_dec { |
292
|
267
|
|
|
267
|
|
862
|
my ($payload, $decode_payload) = @_; |
293
|
267
|
100
|
100
|
|
|
1020
|
return $payload if defined $decode_payload && $decode_payload == 0; |
294
|
205
|
|
|
|
|
388
|
my $de = $payload; |
295
|
205
|
|
|
|
|
356
|
$de = eval { decode_json($de) }; |
|
205
|
|
|
|
|
2710
|
|
296
|
205
|
100
|
|
|
|
587
|
if ($decode_payload) { |
297
|
14
|
50
|
|
|
|
36
|
croak "JWT: payload not a valid JSON" unless $de; |
298
|
14
|
|
|
|
|
37
|
return $de; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
else { |
301
|
191
|
100
|
|
|
|
580
|
return defined $de ? $de : $payload; |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
sub _encrypt_jwe_cek { |
306
|
115
|
|
|
115
|
|
254
|
my ($key, $hdr) = @_; |
307
|
115
|
|
|
|
|
206
|
my $alg = $hdr->{alg}; |
308
|
115
|
|
|
|
|
174
|
my $enc = $hdr->{enc}; |
309
|
|
|
|
|
|
|
|
310
|
115
|
100
|
|
|
|
289
|
if ($alg eq 'dir') { |
311
|
12
|
|
|
|
|
28
|
return (_prepare_oct_key($key), ''); |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
103
|
|
|
|
|
161
|
my $cek; |
315
|
|
|
|
|
|
|
my $ecek; |
316
|
103
|
100
|
|
|
|
560
|
if ($enc =~ /^A(128|192|256)GCM/) { |
|
|
50
|
|
|
|
|
|
317
|
55
|
|
|
|
|
374
|
$cek = random_bytes($1/8); |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
elsif ($enc =~ /^A(128|192|256)CBC/) { |
320
|
48
|
|
|
|
|
374
|
$cek = random_bytes(2*$1/8); |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
103
|
100
|
|
|
|
2348
|
if ($alg =~ /^A(128|192|256)KW$/) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
324
|
18
|
|
|
|
|
55
|
$ecek = aes_key_wrap(_prepare_oct_key($key), $cek); |
325
|
18
|
|
|
|
|
63
|
return ($cek, $ecek); |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
elsif ($alg =~ /^A(128|192|256)GCMKW$/) { |
328
|
18
|
|
|
|
|
32
|
my ($t, $i); |
329
|
18
|
|
|
|
|
42
|
($ecek, $t, $i) = gcm_key_wrap(_prepare_oct_key($key), $cek); |
330
|
18
|
|
|
|
|
83
|
$hdr->{tag} = encode_b64u($t); |
331
|
18
|
|
|
|
|
48
|
$hdr->{iv} = encode_b64u($i); |
332
|
18
|
|
|
|
|
63
|
return ($cek, $ecek); |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
elsif ($alg =~ /^PBES2-HS(512|384|256)\+A(128|192|256)KW$/) { |
335
|
24
|
50
|
33
|
|
|
254
|
my $len = looks_like_number($hdr->{p2s}) && $hdr->{p2s} >= 8 && $hdr->{p2s} <= 9999 ? $hdr->{p2s} : 16; |
336
|
24
|
|
|
|
|
117
|
my $salt = random_bytes($len); |
337
|
24
|
50
|
|
|
|
293
|
my $iter = looks_like_number($hdr->{p2c}) ? $hdr->{p2c} : 5000; |
338
|
24
|
|
|
|
|
95
|
$ecek = pbes2_key_wrap(_prepare_oct_key($key), $cek, $alg, $salt, $iter); |
339
|
24
|
|
|
|
|
149
|
$hdr->{p2s} = encode_b64u($salt); |
340
|
24
|
|
|
|
|
67
|
$hdr->{p2c} = $iter; |
341
|
24
|
|
|
|
|
116
|
return ($cek, $ecek); |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
elsif ($alg =~ /^RSA(-OAEP|-OAEP-256|1_5)$/) { |
344
|
18
|
|
|
|
|
103
|
$key = _prepare_rsa_key($key); |
345
|
18
|
|
|
|
|
127
|
$ecek = rsa_key_wrap($key, $cek, $alg); |
346
|
18
|
|
|
|
|
89
|
return ($cek, $ecek); |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
elsif ($alg =~ /^ECDH-ES\+A(128|192|256)KW$/) { |
349
|
19
|
|
|
|
|
55
|
$key = _prepare_ecdh_key($key); |
350
|
19
|
|
|
|
|
123
|
($ecek, $hdr->{epk}) = ecdhaes_key_wrap($key, $cek, $alg, $hdr->{apu}, $hdr->{apv}); |
351
|
19
|
|
|
|
|
2744
|
return ($cek, $ecek); |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
elsif ($alg eq 'ECDH-ES') { |
354
|
6
|
|
|
|
|
19
|
$key = _prepare_ecdh_key($key); |
355
|
6
|
|
|
|
|
37
|
($cek, $hdr->{epk}) = ecdh_key_wrap($key, $enc, $hdr->{apu}, $hdr->{apv}); |
356
|
6
|
|
|
|
|
818
|
return ($cek, ''); |
357
|
|
|
|
|
|
|
} |
358
|
0
|
|
|
|
|
0
|
croak "JWE: unknown alg '$alg'"; |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
sub _decrypt_jwe_cek { |
362
|
168
|
|
|
168
|
|
422
|
my ($ecek, $key, $hdr) = @_; |
363
|
168
|
|
|
|
|
303
|
my $alg = $hdr->{alg}; |
364
|
168
|
|
|
|
|
294
|
my $enc = $hdr->{enc}; |
365
|
|
|
|
|
|
|
|
366
|
168
|
100
|
|
|
|
1314
|
if ($alg eq 'dir') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
367
|
18
|
|
|
|
|
51
|
return _prepare_oct_key($key); |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
elsif ($alg =~ /^A(128|192|256)KW$/) { |
370
|
22
|
|
|
|
|
60
|
return aes_key_unwrap(_prepare_oct_key($key), $ecek); |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
elsif ($alg =~ /^A(128|192|256)GCMKW$/) { |
373
|
22
|
|
|
|
|
60
|
return gcm_key_unwrap(_prepare_oct_key($key), $ecek, decode_b64u($hdr->{tag}), decode_b64u($hdr->{iv})); |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
elsif ($alg =~ /^PBES2-HS(512|384|256)\+A(128|192|256)KW$/) { |
376
|
39
|
|
|
|
|
143
|
return pbes2_key_unwrap(_prepare_oct_key($key), $ecek, $alg, decode_b64u($hdr->{p2s}), $hdr->{p2c}); |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
elsif ($alg =~ /^RSA(-OAEP|-OAEP-256|1_5)$/) { |
379
|
35
|
|
|
|
|
97
|
$key = _prepare_rsa_key($key); |
380
|
35
|
|
|
|
|
4772
|
return rsa_key_unwrap($key, $ecek, $alg); |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
elsif ($alg =~ /^ECDH-ES\+A(128|192|256)KW$/) { |
383
|
22
|
|
|
|
|
78
|
$key = _prepare_ecdh_key($key); |
384
|
22
|
|
|
|
|
167
|
return ecdhaes_key_unwrap($key, $ecek, $alg, $hdr->{epk}, $hdr->{apu}, $hdr->{apv}); |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
elsif ($alg eq 'ECDH-ES') { |
387
|
10
|
|
|
|
|
29
|
$key = _prepare_ecdh_key($key); |
388
|
10
|
|
|
|
|
74
|
return ecdh_key_unwrap($key, $enc, $hdr->{epk}, $hdr->{apu}, $hdr->{apv}); |
389
|
|
|
|
|
|
|
} |
390
|
0
|
|
|
|
|
0
|
croak "JWE: unknown alg '$alg'"; |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
sub _encrypt_jwe_payload { |
394
|
115
|
|
|
115
|
|
339
|
my ($cek, $enc, $b64u_header, $b64u_aad, $payload) = @_; |
395
|
115
|
50
|
|
|
|
274
|
my $aad = defined $b64u_aad ? "$b64u_header.$b64u_aad" : $b64u_header; |
396
|
115
|
100
|
|
|
|
996
|
if ($enc =~ /^A(128|192|256)GCM$/) { |
|
|
50
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# https://tools.ietf.org/html/rfc7518#section-5.3 |
398
|
61
|
|
|
|
|
255
|
my $len1 = $1/8; |
399
|
61
|
|
|
|
|
116
|
my $len2 = length($cek); |
400
|
61
|
50
|
|
|
|
179
|
croak "JWE: wrong AES key length ($len1 vs. $len2) for $enc" unless $len1 == $len2; |
401
|
61
|
|
|
|
|
210
|
my $iv = random_bytes(12); # for AESGCM always 12 (96 bits) |
402
|
61
|
|
|
|
|
13329
|
my ($ct, $tag) = gcm_encrypt_authenticate('AES', $cek, $iv, $aad, $payload); |
403
|
61
|
|
|
|
|
343
|
return ($ct, $iv, $tag); |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
elsif ($enc =~ /^A(128|192|256)CBC-HS(256|384|512)$/) { |
406
|
|
|
|
|
|
|
# https://tools.ietf.org/html/rfc7518#section-5.2 |
407
|
54
|
|
|
|
|
326
|
my ($size, $hash) = ($1/8, "SHA$2"); |
408
|
54
|
|
|
|
|
142
|
my $key_len = length($cek) / 2; |
409
|
54
|
|
|
|
|
126
|
my $mac_key = substr($cek, 0, $key_len); |
410
|
54
|
|
|
|
|
116
|
my $aes_key = substr($cek, $key_len, $key_len); |
411
|
54
|
50
|
|
|
|
154
|
croak "JWE: wrong AES key length ($key_len vs. $size)" unless $key_len == $size; |
412
|
54
|
|
|
|
|
178
|
my $iv = random_bytes(16); # for AES always 16 |
413
|
54
|
|
|
|
|
960
|
my $m = Crypt::Mode::CBC->new('AES'); |
414
|
54
|
|
|
|
|
257
|
my $ct = $m->encrypt($payload, $aes_key, $iv); |
415
|
54
|
|
|
|
|
1206
|
my $aad_len = length($aad); |
416
|
54
|
|
|
|
|
328
|
my $mac_input = $aad . $iv . $ct . pack('N2', ($aad_len / 2147483647)*8, ($aad_len % 2147483647)*8); |
417
|
54
|
|
|
|
|
661
|
my $mac = hmac($hash, $mac_key, $mac_input); |
418
|
54
|
|
|
|
|
136
|
my $sig_len = length($mac) / 2; |
419
|
54
|
|
|
|
|
132
|
my $sig = substr($mac, 0, $sig_len); |
420
|
54
|
|
|
|
|
409
|
return ($ct, $iv, $sig); |
421
|
|
|
|
|
|
|
} |
422
|
0
|
|
|
|
|
0
|
croak "JWE: unsupported enc '$enc'"; |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
sub _decrypt_jwe_payload { |
426
|
168
|
|
|
168
|
|
595
|
my ($cek, $enc, $aad, $ct, $iv, $tag) = @_; |
427
|
168
|
100
|
|
|
|
1625
|
if ($enc =~ /^A(128|192|256)GCM$/) { |
|
|
50
|
|
|
|
|
|
428
|
|
|
|
|
|
|
# https://tools.ietf.org/html/rfc7518#section-5.3 |
429
|
89
|
|
|
|
|
446
|
my $len1 = $1/8; |
430
|
89
|
|
|
|
|
182
|
my $len2 = length($cek); |
431
|
89
|
50
|
|
|
|
295
|
croak "JWE: wrong AES key length ($len1 vs. $len2) for $enc" unless $len1 == $len2; |
432
|
89
|
|
|
|
|
18073
|
return gcm_decrypt_verify('AES', $cek, $iv, $aad, $ct, $tag); |
433
|
|
|
|
|
|
|
} |
434
|
|
|
|
|
|
|
elsif ($enc =~ /^A(128|192|256)CBC-HS(256|384|512)$/) { |
435
|
|
|
|
|
|
|
# https://tools.ietf.org/html/rfc7518#section-5.2 |
436
|
79
|
|
|
|
|
547
|
my ($size, $hash) = ($1/8, "SHA$2"); |
437
|
79
|
|
|
|
|
205
|
my $key_len = length($cek) / 2; |
438
|
79
|
|
|
|
|
204
|
my $mac_key = substr($cek, 0, $key_len); |
439
|
79
|
|
|
|
|
162
|
my $aes_key = substr($cek, $key_len, $key_len); |
440
|
79
|
50
|
|
|
|
237
|
croak "JWE: wrong AES key length ($key_len vs. $size)" unless $key_len == $size; |
441
|
79
|
|
|
|
|
137
|
my $aad_len = length($aad); # AAD == original encoded header |
442
|
79
|
|
|
|
|
461
|
my $mac_input = $aad . $iv . $ct . pack('N2', ($aad_len / 2147483647)*8, ($aad_len % 2147483647)*8); |
443
|
79
|
|
|
|
|
1049
|
my $mac = hmac($hash, $mac_key, $mac_input); |
444
|
79
|
|
|
|
|
178
|
my $sig_len = length($mac) / 2; |
445
|
79
|
|
|
|
|
189
|
my $sig = substr($mac, 0, $sig_len); |
446
|
79
|
50
|
|
|
|
216
|
croak "JWE: tag mismatch" unless $sig eq $tag; |
447
|
79
|
|
|
|
|
598
|
my $m = Crypt::Mode::CBC->new('AES'); |
448
|
79
|
|
|
|
|
377
|
my $pt = $m->decrypt($ct, $aes_key, $iv); |
449
|
79
|
|
|
|
|
2210
|
return $pt; |
450
|
|
|
|
|
|
|
} |
451
|
0
|
|
|
|
|
0
|
croak "JWE: unsupported enc '$enc'"; |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
sub _encode_jwe { |
455
|
115
|
|
|
115
|
|
381
|
my %args = @_; |
456
|
115
|
|
|
|
|
286
|
my $payload = $args{payload}; |
457
|
115
|
|
|
|
|
188
|
my $alg = $args{alg}; |
458
|
115
|
|
|
|
|
295
|
my $enc = $args{enc}; |
459
|
115
|
100
|
|
|
|
338
|
my $header = $args{extra_headers} ? \%{$args{extra_headers}} : {}; |
|
2
|
|
|
|
|
7
|
|
460
|
115
|
50
|
|
|
|
329
|
croak "JWE: missing 'enc'" if !defined $enc; |
461
|
115
|
50
|
|
|
|
276
|
croak "JWE: missing 'payload'" if !defined $payload; |
462
|
|
|
|
|
|
|
# add claims to payload |
463
|
115
|
100
|
|
|
|
340
|
_add_claims($payload, %args) if ref $payload eq 'HASH'; |
464
|
|
|
|
|
|
|
# serialize payload |
465
|
115
|
|
|
|
|
291
|
$payload = _payload_enc($payload); |
466
|
|
|
|
|
|
|
# compress payload |
467
|
115
|
100
|
|
|
|
289
|
$payload = _payload_zip($payload, $header, $args{zip}) if $args{zip}; # may set some header items |
468
|
|
|
|
|
|
|
# prepare header |
469
|
115
|
|
|
|
|
278
|
$header->{alg} = $alg; |
470
|
115
|
|
|
|
|
219
|
$header->{enc} = $enc; |
471
|
|
|
|
|
|
|
# key |
472
|
115
|
50
|
|
|
|
299
|
croak "JWE: missing 'key'" if !$args{key}; |
473
|
115
|
50
|
|
|
|
288
|
my $key = defined $args{keypass} ? [$args{key}, $args{keypass}] : $args{key}; |
474
|
|
|
|
|
|
|
# prepare cek |
475
|
115
|
|
|
|
|
318
|
my ($cek, $ecek) = _encrypt_jwe_cek($key, $header); # adds some header items |
476
|
|
|
|
|
|
|
# encode header |
477
|
115
|
|
|
|
|
961
|
my $json_header = encode_json($header); |
478
|
115
|
|
|
|
|
432
|
my $b64u_header = encode_b64u($json_header); |
479
|
115
|
50
|
|
|
|
366
|
my $b64u_aad = defined $args{aad} ? encode_b64u($args{aad}) : undef; |
480
|
|
|
|
|
|
|
# encrypt payload |
481
|
115
|
|
|
|
|
362
|
my ($ct, $iv, $tag) = _encrypt_jwe_payload($cek, $enc, $b64u_header, $b64u_aad, $payload); |
482
|
|
|
|
|
|
|
# return token parts |
483
|
115
|
|
|
|
|
1293
|
return ( $b64u_header, |
484
|
|
|
|
|
|
|
encode_b64u($ecek), |
485
|
|
|
|
|
|
|
encode_b64u($iv), |
486
|
|
|
|
|
|
|
encode_b64u($ct), |
487
|
|
|
|
|
|
|
encode_b64u($tag), |
488
|
|
|
|
|
|
|
$b64u_aad); |
489
|
|
|
|
|
|
|
} |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
sub _decode_jwe { |
492
|
174
|
|
|
174
|
|
1368
|
my ($b64u_header, $b64u_ecek, $b64u_iv, $b64u_ct, $b64u_tag, $b64u_aad, $unprotected, $shared_unprotected, %args) = @_; |
493
|
174
|
|
|
|
|
611
|
my $header = _b64u_to_hash($b64u_header); |
494
|
174
|
|
|
|
|
593
|
my $ecek = decode_b64u($b64u_ecek); |
495
|
174
|
|
|
|
|
490
|
my $ct = decode_b64u($b64u_ct); |
496
|
174
|
|
|
|
|
384
|
my $iv = decode_b64u($b64u_iv); |
497
|
174
|
|
|
|
|
386
|
my $tag = decode_b64u($b64u_tag); |
498
|
174
|
50
|
33
|
|
|
796
|
croak "JWE: invalid header part" if $b64u_header && !$header; |
499
|
174
|
50
|
66
|
|
|
670
|
croak "JWE: invalid ecek part" if $b64u_ecek && !$ecek; |
500
|
174
|
50
|
33
|
|
|
627
|
croak "JWE: invalid ct part" if $b64u_ct && !$ct; |
501
|
174
|
50
|
33
|
|
|
550
|
croak "JWE: invalid iv part" if $b64u_iv && !$iv; |
502
|
174
|
50
|
33
|
|
|
575
|
croak "JWE: invalid tag part" if $b64u_tag && !$tag; |
503
|
|
|
|
|
|
|
|
504
|
174
|
|
|
|
|
233
|
my $key; |
505
|
174
|
100
|
|
|
|
413
|
if (exists $args{key}) { |
|
|
50
|
|
|
|
|
|
506
|
172
|
50
|
|
|
|
460
|
$key = defined $args{keypass} ? [$args{key}, $args{keypass}] : $args{key}; |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
elsif (exists $args{kid_keys}) { |
509
|
|
|
|
|
|
|
# BEWARE: stricter approach since 0.023 |
510
|
|
|
|
|
|
|
# when 'kid_keys' specified it croaks if header doesn't contain 'kid' value or if 'kid' wasn't found in 'kid_keys' |
511
|
2
|
|
|
|
|
10
|
my $k = _kid_lookup($header->{kid}, $args{kid_keys}, $header->{alg}); |
512
|
2
|
50
|
|
|
|
6
|
croak "JWE: kid_keys lookup failed" if !defined $k; |
513
|
2
|
|
|
|
|
6
|
$key = $k; |
514
|
|
|
|
|
|
|
} |
515
|
174
|
50
|
|
|
|
364
|
croak "JWE: missing key" if !defined $key; |
516
|
|
|
|
|
|
|
|
517
|
174
|
|
|
|
|
339
|
my $aa = $args{accepted_alg}; |
518
|
174
|
100
|
66
|
|
|
673
|
if (ref($aa) eq 'Regexp') { |
|
|
100
|
66
|
|
|
|
|
519
|
1
|
50
|
|
|
|
162
|
croak "JWE: alg '$header->{alg}' does not match accepted_alg" if $header->{alg} !~ $aa; |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
elsif ($aa && (ref($aa) eq 'ARRAY' || !ref($aa))) { |
522
|
5
|
100
|
|
|
|
22
|
my %acca = ref $aa ? map { $_ => 1 } @$aa : ( $aa => 1 ); |
|
5
|
|
|
|
|
15
|
|
523
|
5
|
100
|
|
|
|
367
|
croak "JWE: alg '$header->{alg}' not in accepted_alg" if !$acca{$header->{alg}}; |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
|
526
|
171
|
|
|
|
|
333
|
my $ae = $args{accepted_enc}; |
527
|
171
|
100
|
66
|
|
|
624
|
if (ref($ae) eq 'Regexp') { |
|
|
100
|
66
|
|
|
|
|
528
|
1
|
50
|
|
|
|
136
|
croak "JWE: enc '$header->{enc}' does not match accepted_enc" if $header->{enc} !~ $ae; |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
elsif ($ae && (ref($ae) eq 'ARRAY' || !ref($ae))) { |
531
|
5
|
100
|
|
|
|
20
|
my %acce = ref $ae ? map { $_ => 1 } @$ae : ( $ae => 1 ); |
|
5
|
|
|
|
|
15
|
|
532
|
5
|
100
|
|
|
|
331
|
croak "JWE: enc '$header->{enc}' not in accepted_enc" if !$acce{$header->{enc}}; |
533
|
|
|
|
|
|
|
} |
534
|
|
|
|
|
|
|
|
535
|
168
|
|
|
|
|
950
|
$header = { %$shared_unprotected, %$unprotected, %$header }; # merge headers |
536
|
168
|
|
|
|
|
546
|
my $cek = _decrypt_jwe_cek($ecek, $key, $header); |
537
|
168
|
50
|
|
|
|
657
|
my $aad = defined $b64u_aad ? "$b64u_header.$b64u_aad" : $b64u_header; |
538
|
168
|
|
|
|
|
752
|
my $payload = _decrypt_jwe_payload($cek, $header->{enc}, $aad, $ct, $iv, $tag); |
539
|
168
|
100
|
|
|
|
675
|
$payload = _payload_unzip($payload, $header->{zip}) if $header->{zip}; |
540
|
168
|
|
|
|
|
699
|
$payload = _payload_dec($payload, $args{decode_payload}); |
541
|
168
|
|
|
|
|
931
|
_verify_claims($payload, %args); # croaks on error |
542
|
168
|
|
|
|
|
819
|
return ($header, $payload); |
543
|
|
|
|
|
|
|
} |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
sub _sign_jws { |
546
|
33
|
|
|
33
|
|
81
|
my ($b64u_header, $b64u_payload, $alg, $key) = @_; |
547
|
33
|
100
|
|
|
|
73
|
return '' if $alg eq 'none'; # no integrity |
548
|
32
|
|
|
|
|
43
|
my $sig; |
549
|
32
|
|
|
|
|
75
|
my $data = "$b64u_header.$b64u_payload"; |
550
|
32
|
100
|
|
|
|
158
|
if ($alg =~ /^HS(256|384|512)$/) { # HMAC integrity |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
551
|
19
|
|
|
|
|
43
|
$key = _prepare_oct_key($key); |
552
|
19
|
|
|
|
|
231
|
$sig = hmac("SHA$1", $key, $data); |
553
|
|
|
|
|
|
|
} |
554
|
|
|
|
|
|
|
elsif ($alg =~ /^RS(256|384|512)/) { # RSA+PKCS1-V1_5 signatures |
555
|
5
|
|
|
|
|
20
|
my $pk = _prepare_rsa_key($key); |
556
|
5
|
|
|
|
|
28202
|
$sig = $pk->sign_message($data, "SHA$1", 'v1.5'); |
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
elsif ($alg =~ /^PS(256|384|512)/) { # RSA+PSS signatures |
559
|
3
|
|
|
|
|
9
|
my $hash = "SHA$1"; |
560
|
3
|
|
|
|
|
10
|
my $hashlen = $1/8; |
561
|
3
|
|
|
|
|
10
|
my $pk = _prepare_rsa_key($key); |
562
|
3
|
|
|
|
|
16444
|
$sig = $pk->sign_message($data, $hash, 'pss', $hashlen); |
563
|
|
|
|
|
|
|
} |
564
|
|
|
|
|
|
|
elsif ($alg =~ /^ES(256|256K|384|512)$/) { # ECDSA signatures |
565
|
4
|
|
|
|
|
16
|
my $hash = {ES256 => 'SHA256', ES256K => 'SHA256', ES384 => 'SHA384', ES512 => 'SHA512'}->{$alg}; |
566
|
4
|
|
|
|
|
17
|
my $pk = _prepare_ecc_key($key); |
567
|
4
|
|
|
|
|
20856
|
$sig = $pk->sign_message_rfc7518($data, $hash); |
568
|
|
|
|
|
|
|
} |
569
|
|
|
|
|
|
|
elsif ($alg eq 'EdDSA') { # Ed25519 signatures |
570
|
1
|
|
|
|
|
6
|
my $pk = _prepare_ed25519_key($key); |
571
|
1
|
|
|
|
|
6465
|
$sig = $pk->sign_message($data); |
572
|
|
|
|
|
|
|
} |
573
|
32
|
|
|
|
|
216
|
return encode_b64u($sig); |
574
|
|
|
|
|
|
|
} |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
sub _verify_jws { |
577
|
98
|
|
|
98
|
|
248
|
my ($b64u_header, $b64u_payload, $b64u_sig, $alg, $key) = @_; |
578
|
98
|
|
|
|
|
329
|
my $sig = decode_b64u($b64u_sig); |
579
|
98
|
50
|
33
|
|
|
362
|
croak "JWS: invalid sig part" if $b64u_sig && !$sig; |
580
|
98
|
|
|
|
|
217
|
my $data = "$b64u_header.$b64u_payload"; |
581
|
|
|
|
|
|
|
|
582
|
98
|
50
|
|
|
|
603
|
if ($alg eq 'none' ) { # no integrity |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
583
|
0
|
|
|
|
|
0
|
return 1; |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
elsif ($alg =~ /^HS(256|384|512)$/) { # HMAC integrity |
586
|
62
|
|
|
|
|
135
|
$key = _prepare_oct_key($key); |
587
|
62
|
50
|
|
|
|
809
|
return 1 if $sig eq hmac("SHA$1", $key, $data); |
588
|
|
|
|
|
|
|
} |
589
|
|
|
|
|
|
|
elsif ($alg =~ /^RS(256|384|512)/) { # RSA+PKCS1-V1_5 signatures |
590
|
20
|
|
|
|
|
59
|
my $hash = "SHA$1"; |
591
|
20
|
|
|
|
|
59
|
my $pk = _prepare_rsa_key($key); |
592
|
19
|
50
|
|
|
|
8464
|
return 1 if $pk->verify_message($sig, $data, $hash, 'v1.5'); |
593
|
|
|
|
|
|
|
} |
594
|
|
|
|
|
|
|
elsif ($alg =~ /^PS(256|384|512)/) { # RSA+PSS signatures |
595
|
6
|
|
|
|
|
41
|
my $hash = "SHA$1"; |
596
|
6
|
|
|
|
|
31
|
my $hashlen = $1/8; |
597
|
6
|
|
|
|
|
18
|
my $pk = _prepare_rsa_key($key); |
598
|
6
|
50
|
|
|
|
1982
|
return 1 if $pk->verify_message($sig, $data, $hash, 'pss', $hashlen); |
599
|
|
|
|
|
|
|
} |
600
|
|
|
|
|
|
|
elsif ($alg =~ /^ES(256|256K|384|512)$/) { # ECDSA signatures |
601
|
9
|
|
|
|
|
54
|
my $hash = {ES256 => 'SHA256', ES256K => 'SHA256', ES384 => 'SHA384', ES512 => 'SHA512'}->{$alg}; |
602
|
9
|
|
|
|
|
36
|
my $pk = _prepare_ecc_key($key); |
603
|
8
|
50
|
|
|
|
50964
|
return 1 if $pk->verify_message_rfc7518($sig, $data, $hash); |
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
elsif ($alg eq 'EdDSA') { # Ed25519 signatures |
606
|
1
|
|
|
|
|
6
|
my $pk = _prepare_ed25519_key($key); |
607
|
1
|
50
|
|
|
|
6444
|
return 1 if $pk->verify_message($sig, $data); |
608
|
|
|
|
|
|
|
} |
609
|
0
|
|
|
|
|
0
|
return 0; |
610
|
|
|
|
|
|
|
} |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
sub _encode_jws { |
613
|
34
|
|
|
34
|
|
98
|
my %args = @_; |
614
|
34
|
|
|
|
|
66
|
my $payload = $args{payload}; |
615
|
34
|
|
|
|
|
63
|
my $alg = $args{alg}; |
616
|
34
|
100
|
|
|
|
86
|
my $header = $args{extra_headers} ? \%{$args{extra_headers}} : {}; |
|
2
|
|
|
|
|
6
|
|
617
|
34
|
50
|
|
|
|
82
|
croak "JWS: missing 'payload'" if !defined $payload; |
618
|
34
|
100
|
100
|
|
|
196
|
croak "JWS: alg 'none' not allowed" if $alg eq 'none' && !$args{allow_none}; |
619
|
|
|
|
|
|
|
# add claims to payload |
620
|
33
|
100
|
|
|
|
108
|
_add_claims($payload, %args) if ref $payload eq 'HASH'; |
621
|
|
|
|
|
|
|
# serialize payload |
622
|
33
|
|
|
|
|
79
|
$payload = _payload_enc($payload); |
623
|
|
|
|
|
|
|
# compress payload |
624
|
33
|
100
|
|
|
|
86
|
$payload = _payload_zip($payload, $header, $args{zip}) if $args{zip}; # may set some header items |
625
|
|
|
|
|
|
|
# encode payload |
626
|
33
|
|
|
|
|
126
|
my $b64u_payload = encode_b64u($payload); |
627
|
|
|
|
|
|
|
# prepare header |
628
|
33
|
|
|
|
|
77
|
$header->{alg} = $alg; |
629
|
|
|
|
|
|
|
# encode header |
630
|
33
|
|
|
|
|
137
|
my $json_header = encode_json($header); |
631
|
33
|
|
|
|
|
78
|
my $b64u_header = encode_b64u($json_header); |
632
|
|
|
|
|
|
|
# key |
633
|
33
|
50
|
66
|
|
|
137
|
croak "JWS: missing 'key'" if !$args{key} && $alg ne 'none'; |
634
|
33
|
50
|
|
|
|
79
|
my $key = defined $args{keypass} ? [$args{key}, $args{keypass}] : $args{key}; |
635
|
|
|
|
|
|
|
# sign header |
636
|
33
|
|
|
|
|
77
|
my $b64u_signature = _sign_jws($b64u_header, $b64u_payload, $alg, $key); |
637
|
33
|
|
|
|
|
206
|
return ($b64u_header, $b64u_payload, $b64u_signature); |
638
|
|
|
|
|
|
|
} |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
sub _decode_jws { |
641
|
105
|
|
|
105
|
|
567
|
my ($b64u_header, $b64u_payload, $b64u_sig, $unprotected_header, %args) = @_; |
642
|
105
|
|
|
|
|
289
|
my $header = _b64u_to_hash($b64u_header); |
643
|
105
|
50
|
33
|
|
|
465
|
croak "JWS: invalid header part" if $b64u_header && !$header; |
644
|
105
|
100
|
|
|
|
233
|
$unprotected_header = {} if ref $unprotected_header ne 'HASH'; |
645
|
|
|
|
|
|
|
|
646
|
105
|
100
|
|
|
|
303
|
if (!$args{ignore_signature}) { |
647
|
104
|
|
|
|
|
184
|
my $alg = $header->{alg}; |
648
|
104
|
50
|
|
|
|
192
|
croak "JWS: missing header 'alg'" unless $alg; |
649
|
104
|
100
|
100
|
|
|
356
|
croak "JWS: alg 'none' not allowed" if $alg eq 'none' && !$args{allow_none}; |
650
|
103
|
50
|
66
|
|
|
239
|
croak "JWS: alg 'none' expects no signature" if $alg eq 'none' && defined $b64u_sig && length($b64u_sig) > 0; |
|
|
|
66
|
|
|
|
|
651
|
|
|
|
|
|
|
|
652
|
103
|
|
|
|
|
179
|
my $aa = $args{accepted_alg}; |
653
|
103
|
100
|
|
|
|
358
|
if (ref $aa eq 'Regexp') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
654
|
1
|
50
|
|
|
|
138
|
croak "JWS: alg '$alg' does not match accepted_alg" if $alg !~ $aa; |
655
|
|
|
|
|
|
|
} |
656
|
|
|
|
|
|
|
elsif (ref $aa eq 'ARRAY') { |
657
|
2
|
|
|
|
|
6
|
my %acca = map { $_ => 1 } @$aa; |
|
5
|
|
|
|
|
15
|
|
658
|
2
|
100
|
|
|
|
216
|
croak "JWS: alg '$alg' not in accepted_alg" if !$acca{$alg}; |
659
|
|
|
|
|
|
|
} |
660
|
|
|
|
|
|
|
elsif (defined $aa) { |
661
|
3
|
100
|
|
|
|
113
|
croak "JWS: alg '$alg' not accepted_alg" if $aa ne $alg; |
662
|
|
|
|
|
|
|
} |
663
|
|
|
|
|
|
|
|
664
|
100
|
100
|
|
|
|
207
|
if ($alg ne 'none') { |
665
|
98
|
|
|
|
|
132
|
my $key; |
666
|
98
|
100
|
|
|
|
196
|
if (exists $args{key}) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
667
|
95
|
50
|
|
|
|
222
|
$key = defined $args{keypass} ? [$args{key}, $args{keypass}] : $args{key}; |
668
|
|
|
|
|
|
|
} |
669
|
|
|
|
|
|
|
elsif (exists $args{kid_keys}) { |
670
|
|
|
|
|
|
|
# BEWARE: stricter approach since 0.023 |
671
|
|
|
|
|
|
|
# when 'kid_keys' specified it croaks if header doesn't contain 'kid' value or if 'kid' wasn't found in 'kid_keys' |
672
|
2
|
50
|
|
|
|
8
|
my $kid = exists $header->{kid} ? $header->{kid} : $unprotected_header->{kid}; |
673
|
2
|
|
|
|
|
7
|
my $k = _kid_lookup($kid, $args{kid_keys}, $alg); |
674
|
2
|
50
|
|
|
|
6
|
croak "JWS: kid_keys lookup failed" if !defined $k; |
675
|
2
|
|
|
|
|
4
|
$key = $k; |
676
|
|
|
|
|
|
|
} |
677
|
|
|
|
|
|
|
elsif ($args{key_from_jwk_header}) { |
678
|
|
|
|
|
|
|
# BEWARE: stricter approach since 0.023 |
679
|
|
|
|
|
|
|
# - header 'jwk' is by default ignored (unless given: key_from_jwk_header => 1) |
680
|
|
|
|
|
|
|
# - only RSA/ECDSA public keys are accepted |
681
|
1
|
|
|
|
|
3
|
my $k = $header->{jwk}; |
682
|
1
|
50
|
33
|
|
|
9
|
croak "JWS: jwk header does not contain a key" if !defined $k || ref($k) ne 'HASH' || !defined $k->{kty}; |
|
|
|
33
|
|
|
|
|
683
|
1
|
50
|
33
|
|
|
13
|
croak "JWS: jwk header allowed only for RSA/ECDSA" if $alg !~ /^(RS|PS|ES)/ || $k->{kty} !~ /^(RSA|EC)$/; |
684
|
1
|
50
|
33
|
|
|
29
|
croak "JWS: jwk header must be a public key" if $k->{d} || $k->{p} || $k->{q} || $k->{dp} || $k->{dq} || $k->{qi}; |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
685
|
1
|
|
|
|
|
3
|
$key = $k; |
686
|
|
|
|
|
|
|
} |
687
|
98
|
50
|
|
|
|
199
|
croak "JWS: missing key" if !defined $key; |
688
|
|
|
|
|
|
|
|
689
|
98
|
|
|
|
|
236
|
my $valid = _verify_jws($b64u_header, $b64u_payload, $b64u_sig, $alg, $key); |
690
|
96
|
50
|
|
|
|
329
|
croak "JWS: invalid signature" if !$valid; |
691
|
|
|
|
|
|
|
} |
692
|
|
|
|
|
|
|
} |
693
|
99
|
|
|
|
|
302
|
my $payload = decode_b64u($b64u_payload); |
694
|
99
|
50
|
66
|
|
|
375
|
croak "JWS: invalid payload part" if $b64u_payload && !$payload; |
695
|
99
|
100
|
|
|
|
244
|
$payload = _payload_unzip($payload, $header->{zip}) if $header->{zip}; |
696
|
99
|
|
|
|
|
323
|
$payload = _payload_dec($payload, $args{decode_payload}); |
697
|
99
|
|
|
|
|
413
|
_verify_claims($payload, %args); # croaks on error |
698
|
68
|
|
|
|
|
370
|
$header = { %$unprotected_header, %$header }; # merge headers |
699
|
68
|
|
|
|
|
254
|
return ($header, $payload); |
700
|
|
|
|
|
|
|
} |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
sub encode_jwt { |
703
|
149
|
|
|
149
|
1
|
228904
|
my %args = @_; |
704
|
|
|
|
|
|
|
|
705
|
149
|
50
|
|
|
|
576
|
croak "JWT: missing 'alg'" unless $args{alg}; |
706
|
149
|
|
50
|
|
|
759
|
my $ser = $args{serialization} || 'compact'; |
707
|
149
|
100
|
|
|
|
1512
|
if ($args{alg} =~ /^(none|EdDSA|(HS|RS|PS)(256|384|512)|ES(256|256K|384|512))$/) { |
|
|
50
|
|
|
|
|
|
708
|
|
|
|
|
|
|
###JWS |
709
|
34
|
|
|
|
|
138
|
my ($b64u_header, $b64u_payload, $b64u_signature) = _encode_jws(%args); |
710
|
33
|
50
|
|
|
|
123
|
if ($ser eq 'compact') { # https://tools.ietf.org/html/rfc7515#section-7.1 |
|
|
0
|
|
|
|
|
|
711
|
33
|
50
|
|
|
|
103
|
croak "JWT: cannot use 'unprotected_headers' with compact serialization" if defined $args{unprotected_headers}; |
712
|
33
|
|
|
|
|
210
|
return "$b64u_header.$b64u_payload.$b64u_signature"; |
713
|
|
|
|
|
|
|
} |
714
|
|
|
|
|
|
|
elsif ($ser eq 'flattened') { # https://tools.ietf.org/html/rfc7515#section-7.2.2 |
715
|
0
|
|
|
|
|
0
|
my $token = { protected => $b64u_header, payload => $b64u_payload, signature => $b64u_signature }; |
716
|
0
|
0
|
|
|
|
0
|
$token->{header} = \%{$args{unprotected_headers}} if ref $args{unprotected_headers} eq 'HASH'; |
|
0
|
|
|
|
|
0
|
|
717
|
0
|
|
|
|
|
0
|
return encode_json($token); |
718
|
|
|
|
|
|
|
} |
719
|
|
|
|
|
|
|
else { |
720
|
0
|
|
|
|
|
0
|
croak "JWT: unsupported JWS serialization '$ser'"; |
721
|
|
|
|
|
|
|
} |
722
|
|
|
|
|
|
|
} |
723
|
|
|
|
|
|
|
elsif ($args{alg} =~ /^(dir|A(128|192|256)KW|A(128|192|256)GCMKW|PBES2-(HS256\+A128KW|HS384\+A192KW|HS512\+A256KW)|RSA-OAEP|RSA-OAEP-256|RSA1_5|ECDH-ES\+A(128|192|256)KW|ECDH-ES)$/) { |
724
|
|
|
|
|
|
|
### JWE |
725
|
115
|
|
|
|
|
539
|
my ($b64u_header, $b64u_ecek, $b64u_iv, $b64u_ct, $b64u_tag, $b64u_aad) = _encode_jwe(%args); |
726
|
115
|
50
|
|
|
|
462
|
if ($ser eq 'compact') { # https://tools.ietf.org/html/rfc7516#section-7.1 |
|
|
0
|
|
|
|
|
|
727
|
115
|
50
|
|
|
|
384
|
croak "JWT: cannot use 'aad' with compact serialization" if defined $args{aad}; |
728
|
115
|
50
|
|
|
|
302
|
croak "JWT: cannot use 'unprotected_headers' with compact serialization" if defined $args{unprotected_headers}; |
729
|
115
|
50
|
|
|
|
270
|
croak "JWT: cannot use 'shared_unprotected_headers' with compact serialization" if defined $args{shared_unprotected_headers}; |
730
|
115
|
|
|
|
|
850
|
return "$b64u_header.$b64u_ecek.$b64u_iv.$b64u_ct.$b64u_tag"; |
731
|
|
|
|
|
|
|
} |
732
|
|
|
|
|
|
|
elsif ($ser eq 'flattened') { # https://tools.ietf.org/html/rfc7516#section-7.2.2 |
733
|
0
|
|
|
|
|
0
|
my $token = { |
734
|
|
|
|
|
|
|
protected => $b64u_header, |
735
|
|
|
|
|
|
|
encrypted_key => $b64u_ecek, |
736
|
|
|
|
|
|
|
iv => $b64u_iv, |
737
|
|
|
|
|
|
|
ciphertext => $b64u_ct, |
738
|
|
|
|
|
|
|
tag => $b64u_tag, |
739
|
|
|
|
|
|
|
}; |
740
|
|
|
|
|
|
|
# header: JWE Per-Recipient Unprotected Header when the JWE Per-Recipient Unprotected Header |
741
|
0
|
0
|
|
|
|
0
|
$token->{header} = \%{$args{unprotected_headers}} if ref $args{unprotected_headers} eq 'HASH'; |
|
0
|
|
|
|
|
0
|
|
742
|
|
|
|
|
|
|
# unprotected: JWE Shared Unprotected Header |
743
|
0
|
0
|
|
|
|
0
|
$token->{unprotected} = \%{$args{shared_unprotected_headers}} if ref $args{shared_unprotected_headers} eq 'HASH'; |
|
0
|
|
|
|
|
0
|
|
744
|
|
|
|
|
|
|
# aad: Additional Authenticated Data (AAD) |
745
|
0
|
0
|
|
|
|
0
|
$token->{aad} = $b64u_aad if defined $b64u_aad; |
746
|
0
|
|
|
|
|
0
|
return encode_json($token); |
747
|
|
|
|
|
|
|
} |
748
|
|
|
|
|
|
|
else { |
749
|
0
|
|
|
|
|
0
|
croak "JWT: unsupported JWE serialization '$ser'"; |
750
|
|
|
|
|
|
|
} |
751
|
|
|
|
|
|
|
} |
752
|
|
|
|
|
|
|
else { |
753
|
0
|
|
|
|
|
0
|
croak "JWT: unexpected alg '$args{alg}'"; |
754
|
|
|
|
|
|
|
} |
755
|
|
|
|
|
|
|
} |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
sub decode_jwt { |
758
|
279
|
|
|
279
|
1
|
126018
|
my %args = @_; |
759
|
279
|
|
|
|
|
639
|
my ($header, $payload); |
760
|
|
|
|
|
|
|
|
761
|
279
|
50
|
|
|
|
4434
|
if (!$args{token}) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
762
|
0
|
|
|
|
|
0
|
croak "JWT: missing token"; |
763
|
|
|
|
|
|
|
} |
764
|
|
|
|
|
|
|
elsif ($args{token} =~ /^([a-zA-Z0-9_-]+)=*\.([a-zA-Z0-9_-]*)=*\.([a-zA-Z0-9_-]+)=*\.([a-zA-Z0-9_-]+)=*\.([a-zA-Z0-9_-]+)=*$/) { |
765
|
|
|
|
|
|
|
# JWE token (5 segments) |
766
|
173
|
|
|
|
|
955
|
($header, $payload) = _decode_jwe($1, $2, $3, $4, $5, undef, {}, {}, %args); |
767
|
|
|
|
|
|
|
} |
768
|
|
|
|
|
|
|
elsif ($args{token} =~ /^([a-zA-Z0-9_-]+)=*\.([a-zA-Z0-9_-]+)=*\.([a-zA-Z0-9_-]*)=*$/) { |
769
|
|
|
|
|
|
|
# JWS token (3 segments) |
770
|
103
|
|
|
|
|
484
|
($header, $payload) = _decode_jws($1, $2, $3, {}, %args); |
771
|
|
|
|
|
|
|
} |
772
|
|
|
|
|
|
|
elsif ($args{token} =~ /^\s*\{.*?\}\s*$/s) { |
773
|
3
|
|
|
|
|
36
|
my $hash = decode_json($args{token}); |
774
|
3
|
100
|
66
|
|
|
25
|
if (defined $hash->{payload} && $hash->{protected}) { |
|
|
50
|
33
|
|
|
|
|
775
|
|
|
|
|
|
|
# Flattened JWS JSON Serialization |
776
|
2
|
|
|
|
|
14
|
($header, $payload) = _decode_jws($hash->{protected}, $hash->{payload}, $hash->{signature}, $hash->{header}, %args); |
777
|
|
|
|
|
|
|
} |
778
|
|
|
|
|
|
|
elsif ($hash->{ciphertext} && $hash->{protected}) { |
779
|
|
|
|
|
|
|
# Flattened JWE JSON Serialization |
780
|
1
|
|
|
|
|
8
|
($header, $payload) = _decode_jwe($hash->{protected}, $hash->{encrypted_key}, $hash->{iv}, $hash->{ciphertext}, $hash->{tag}, $hash->{aad}, $hash->{header}, $hash->{unprotected}, %args); |
781
|
|
|
|
|
|
|
} |
782
|
|
|
|
|
|
|
else { |
783
|
0
|
|
|
|
|
0
|
croak "JWT: unsupported JWS/JWT JSON Serialization"; |
784
|
|
|
|
|
|
|
} |
785
|
|
|
|
|
|
|
} |
786
|
|
|
|
|
|
|
else { |
787
|
0
|
|
|
|
|
0
|
croak "JWT: invalid token format"; |
788
|
|
|
|
|
|
|
} |
789
|
236
|
100
|
|
|
|
1051
|
return ($header, $payload) if $args{decode_header}; |
790
|
228
|
|
|
|
|
1056
|
return $payload; |
791
|
|
|
|
|
|
|
} |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
1; |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
#### URLs |
796
|
|
|
|
|
|
|
# https://metacpan.org/pod/JSON::WebToken |
797
|
|
|
|
|
|
|
# https://metacpan.org/pod/Mojo::JWT |
798
|
|
|
|
|
|
|
# https://bitbucket.org/b_c/jose4j/wiki/JWE%20Examples |
799
|
|
|
|
|
|
|
# https://bitbucket.org/b_c/jose4j/wiki/JWS%20Examples |
800
|
|
|
|
|
|
|
# https://github.com/dvsekhvalnov/jose-jwt/tree/master/JWT/jwe |
801
|
|
|
|
|
|
|
# https://github.com/progrium/ruby-jwt |
802
|
|
|
|
|
|
|
# https://github.com/jpadilla/pyjwt/ |
803
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
=pod |
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
=head1 NAME |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
Crypt::JWT - JSON Web Token (JWT, JWS, JWE) as defined by RFC7519, RFC7515, RFC7516 |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
=head1 SYNOPSIS |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
# encoding |
813
|
|
|
|
|
|
|
use Crypt::JWT qw(encode_jwt); |
814
|
|
|
|
|
|
|
my $jws_token = encode_jwt(payload=>$data, alg=>'HS256', key=>'secret'); |
815
|
|
|
|
|
|
|
my $jwe_token = encode_jwt(payload=>$data, alg=>'PBES2-HS256+A128KW', enc=>'A128GCM', key=>'secret'); |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
# decoding |
818
|
|
|
|
|
|
|
use Crypt::JWT qw(decode_jwt); |
819
|
|
|
|
|
|
|
my $data1 = decode_jwt(token=>$jws_token, key=>'secret'); |
820
|
|
|
|
|
|
|
my $data2 = decode_jwt(token=>$jwe_token, key=>'secret'); |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
=head1 DESCRIPTION |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
Implements B - L. |
825
|
|
|
|
|
|
|
The implementation covers not only B - L, |
826
|
|
|
|
|
|
|
but also B - L. |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
The module implements B defined in L - B. |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
This module supports B and B serialization, general JSON serialization is not supported yet. |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
=head1 EXPORT |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
Nothing is exported by default. |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
You can export selected functions: |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
use Crypt::JWT qw(decode_jwt encode_jwt); |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
Or all of them at once: |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
use Crypt::JWT ':all'; |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
=head1 FUNCTIONS |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
=head2 decode_jwt |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
my $data = decode_jwt(%named_args); |
849
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
Named arguments: |
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
=over |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
=item token |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
Mandatory argument, a string with either JWS or JWE JSON Web Token. |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
### JWS token example (3 segments) |
859
|
|
|
|
|
|
|
$t = "eyJhbGciOiJIUzI1NiJ9.dGVzdA.ujBihtLSr66CEWqN74SpLUkv28lra_CeHnxLmLNp4Jo"; |
860
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>$k); |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
### JWE token example (5 segments) |
863
|
|
|
|
|
|
|
$t = "eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiQTEyOEtXIn0.UusxEbzhGkORxTRq0xkFKhvzPrXb9smw.VGfOuq0Fxt6TsdqLZUpnxw.JajIQQ.pkKZ7MHS0XjyGmRsqgom6w"; |
864
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>$k); |
865
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
=item key |
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
A key used for token decryption (JWE) or token signature validation (JWS). |
869
|
|
|
|
|
|
|
The value depends on the C token header value. |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
JWS alg header key value |
872
|
|
|
|
|
|
|
------------------ ---------------------------------- |
873
|
|
|
|
|
|
|
none no key required |
874
|
|
|
|
|
|
|
HS256 string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') |
875
|
|
|
|
|
|
|
HS384 dtto |
876
|
|
|
|
|
|
|
HS512 dtto |
877
|
|
|
|
|
|
|
RS256 public RSA key, perl HASH ref with JWK key structure, |
878
|
|
|
|
|
|
|
a reference to SCALAR string with PEM or DER or JSON/JWK data, |
879
|
|
|
|
|
|
|
object: Crypt::PK::RSA, Crypt::OpenSSL::RSA, Crypt::X509 or Crypt::OpenSSL::X509 |
880
|
|
|
|
|
|
|
RS384 public RSA key, see RS256 |
881
|
|
|
|
|
|
|
RS512 public RSA key, see RS256 |
882
|
|
|
|
|
|
|
PS256 public RSA key, see RS256 |
883
|
|
|
|
|
|
|
PS384 public RSA key, see RS256 |
884
|
|
|
|
|
|
|
PS512 public RSA key, see RS256 |
885
|
|
|
|
|
|
|
ES256 public ECC key, perl HASH ref with JWK key structure, |
886
|
|
|
|
|
|
|
a reference to SCALAR string with PEM or DER or JSON/JWK data, |
887
|
|
|
|
|
|
|
an instance of Crypt::PK::ECC |
888
|
|
|
|
|
|
|
ES256K public ECC key, see ES256 |
889
|
|
|
|
|
|
|
ES384 public ECC key, see ES256 |
890
|
|
|
|
|
|
|
ES512 public ECC key, see ES256 |
891
|
|
|
|
|
|
|
EdDSA public Ed25519 key |
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
JWE alg header key value |
894
|
|
|
|
|
|
|
------------------ ---------------------------------- |
895
|
|
|
|
|
|
|
dir string (raw octects) or perl HASH ref with JWK, kty=>'oct', length depends on 'enc' algorithm |
896
|
|
|
|
|
|
|
A128KW string (raw octects) 16 bytes (or perl HASH ref with JWK, kty=>'oct') |
897
|
|
|
|
|
|
|
A192KW string (raw octects) 24 bytes (or perl HASH ref with JWK, kty=>'oct') |
898
|
|
|
|
|
|
|
A256KW string (raw octects) 32 bytes (or perl HASH ref with JWK, kty=>'oct') |
899
|
|
|
|
|
|
|
A128GCMKW string (raw octects) 16 bytes (or perl HASH ref with JWK, kty=>'oct') |
900
|
|
|
|
|
|
|
A192GCMKW string (raw octects) 24 bytes (or perl HASH ref with JWK, kty=>'oct') |
901
|
|
|
|
|
|
|
A256GCMKW string (raw octects) 32 bytes (or perl HASH ref with JWK, kty=>'oct') |
902
|
|
|
|
|
|
|
PBES2-HS256+A128KW string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') |
903
|
|
|
|
|
|
|
PBES2-HS384+A192KW string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') |
904
|
|
|
|
|
|
|
PBES2-HS512+A256KW string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') |
905
|
|
|
|
|
|
|
RSA-OAEP private RSA key, perl HASH ref with JWK key structure, |
906
|
|
|
|
|
|
|
a reference to SCALAR string with PEM or DER or JSON/JWK data, |
907
|
|
|
|
|
|
|
an instance of Crypt::PK::RSA or Crypt::OpenSSL::RSA |
908
|
|
|
|
|
|
|
RSA-OAEP-256 private RSA key, see RSA-OAEP |
909
|
|
|
|
|
|
|
RSA1_5 private RSA key, see RSA-OAEP |
910
|
|
|
|
|
|
|
ECDH-ES private ECC or X25519 key, perl HASH ref with JWK key structure, |
911
|
|
|
|
|
|
|
a reference to SCALAR string with PEM or DER or JSON/JWK data, |
912
|
|
|
|
|
|
|
an instance of Crypt::PK::ECC |
913
|
|
|
|
|
|
|
ECDH-ES+A128KW private ECC or X25519 key, see ECDH-ES |
914
|
|
|
|
|
|
|
ECDH-ES+A192KW private ECC or X25519 key, see ECDH-ES |
915
|
|
|
|
|
|
|
ECDH-ES+A256KW private ECC or X25519 key, see ECDH-ES |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
Example using the key from C token header: |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key_from_jwk_header=>1); |
920
|
|
|
|
|
|
|
my ($header, $data) = decode_jwt(token=>$t, decode_header=>1, key_from_jwk_header=>1); |
921
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
Examples with raw octet keys: |
923
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
#string |
925
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>'secretkey'); |
926
|
|
|
|
|
|
|
#binary key |
927
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>pack("H*", "788A6E38F36B7596EF6A669E94")); |
928
|
|
|
|
|
|
|
#perl HASH ref with JWK structure (key type 'oct') |
929
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>{kty=>'oct', k=>"GawgguFyGrWKav7AX4VKUg"}); |
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
Examples with RSA keys: |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
my $pem_key_string = <<'EOF'; |
934
|
|
|
|
|
|
|
-----BEGIN PRIVATE KEY----- |
935
|
|
|
|
|
|
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCoVm/Sl5r+Ofky |
936
|
|
|
|
|
|
|
jioRSZK26GW6WyjyfWKddsSi13/NOtCn0rRErSF/u3QrgGMpWFqKohqbi1VVC+SZ |
937
|
|
|
|
|
|
|
... |
938
|
|
|
|
|
|
|
8c1vm2YFafgdkSk9Qd1oU2Fv1aOQy4VovOFzJ3CcR+2r7cbRfcpLGnintHtp9yek |
939
|
|
|
|
|
|
|
02p+d5g4OChfFNDhDtnIqjvY |
940
|
|
|
|
|
|
|
-----END PRIVATE KEY----- |
941
|
|
|
|
|
|
|
EOF |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
my $jwk_key_json_string = '{"kty":"RSA","n":"0vx7agoebG...L6tSoc_BJECP","e":"AQAB"}'; |
944
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
#a reference to SCALAR string with PEM or DER or JSON/JWK data, |
946
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>\$pem_key_string); |
947
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>\$der_key_string); |
948
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>\$jwk_key_json_string); |
949
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
#instance of Crypt::PK::RSA |
951
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>Crypt::PK::RSA->new('keyfile.pem')); |
952
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>Crypt::PK::RSA->new(\$pem_key_string)); |
953
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
#instance of Crypt::OpenSSL::RSA |
955
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>Crypt::OpenSSL::RSA->new_private_key($pem_key_string)); |
956
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
#instance of Crypt::X509 (public key only) |
958
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>Crypt::X509->new(cert=>$cert)); |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
#instance of Crypt::OpenSSL::X509 (public key only) |
961
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>Crypt::OpenSSL::X509->new_from_file('cert.pem')); |
962
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>Crypt::OpenSSL::X509->new_from_string($cert)); |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
#perl HASH ref with JWK structure (key type 'RSA') |
965
|
|
|
|
|
|
|
my $rsa_priv = { |
966
|
|
|
|
|
|
|
kty => "RSA", |
967
|
|
|
|
|
|
|
n => "0vx7agoebGcQSuuPiLJXZpt...eZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", |
968
|
|
|
|
|
|
|
e => "AQAB", |
969
|
|
|
|
|
|
|
d => "X4cTteJY_gn4FYPsXB8rdXi...FLN5EEaG6RoVH-HLKD9Mdx5ooGURknhnrRwUkC7h5fJLMWbFAKLWY2v7B6NqSzUvx0_YSf", |
970
|
|
|
|
|
|
|
p => "83i-7IvMGXoMXCskv73TKr8...Z27zvoj6pbUQyLPBQxtPnwD20-60eTmD2ujMt5PoMrm8RmNhVWtjjMmMjOpSicFHjXOuVI", |
971
|
|
|
|
|
|
|
q => "3dfOR9cuYq-0S-mkFLzgItg...q3hWeMuG0ouqnb3obLyuqjVZQ1dIrdgTnCdYzBcOW5r37AFXjift_NGiovonzhKpoVVS78", |
972
|
|
|
|
|
|
|
dp => "G4sPXkc6Ya9y8oJW9_ILj4...zi_H7TkS8x5SdX3oE0oiYwxIiemTAu0UOa5pgFGyJ4c8t2VF40XRugKTP8akhFo5tA77Qe", |
973
|
|
|
|
|
|
|
dq => "s9lAH9fggBsoFR8Oac2R_E...T2kGOhvIllTE1efA6huUvMfBcpn8lqW6vzzYY5SSF7pMd_agI3G8IbpBUb0JiraRNUfLhc", |
974
|
|
|
|
|
|
|
qi => "GyM_p6JrXySiz1toFgKbWV...4ypu9bMWx3QJBfm0FoYzUIZEVEcOqwmRN81oDAaaBk0KWGDjJHDdDmFW3AN7I-pux_mHZG", |
975
|
|
|
|
|
|
|
}; |
976
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>$rsa_priv}); |
977
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
Examples with ECC keys: |
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
my $pem_key_string = <<'EOF'; |
981
|
|
|
|
|
|
|
-----BEGIN EC PRIVATE KEY----- |
982
|
|
|
|
|
|
|
MHcCAQEEIBG1c3z52T8XwMsahGVdOZWgKCQJfv+l7djuJjgetdbDoAoGCCqGSM49 |
983
|
|
|
|
|
|
|
AwEHoUQDQgAEoBUyo8CQAFPeYPvv78ylh5MwFZjTCLQeb042TjiMJxG+9DLFmRSM |
984
|
|
|
|
|
|
|
lBQ9T/RsLLc+PmpB1+7yPAR+oR5gZn3kJQ== |
985
|
|
|
|
|
|
|
-----END EC PRIVATE KEY----- |
986
|
|
|
|
|
|
|
EOF |
987
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
my $jwk_key_json_string = '{"kty":"EC","crv":"P-256","x":"MKB..7D4","y":"4Et..FyM"}'; |
989
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
#a reference to SCALAR string with PEM or DER or JSON/JWK data, |
991
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>\$pem_key_string); |
992
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>\$der_key_string); |
993
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>\$jwk_key_json_string); |
994
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
#instance of Crypt::PK::ECC |
996
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>Crypt::PK::ECC->new('keyfile.pem')); |
997
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>Crypt::PK::ECC->new(\$pem_key_string)); |
998
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
#perl HASH ref with JWK structure (key type 'EC') |
1000
|
|
|
|
|
|
|
my $ecc_priv = { |
1001
|
|
|
|
|
|
|
kty => "EC", |
1002
|
|
|
|
|
|
|
crv => "P-256", |
1003
|
|
|
|
|
|
|
x => "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", |
1004
|
|
|
|
|
|
|
y => "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", |
1005
|
|
|
|
|
|
|
d => "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE", |
1006
|
|
|
|
|
|
|
}; |
1007
|
|
|
|
|
|
|
my $data = decode_jwt(token=>$t, key=>$ecc_priv}); |
1008
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
=item keypass |
1010
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
When 'key' parameter is an encrypted private RSA or ECC key this optional parameter may contain a password for private key decryption. |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
=item kid_keys |
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
This parametes can be either a JWK Set JSON string (see RFC7517) or a perl HASH ref with JWK Set structure like this: |
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
my $keylist = { |
1018
|
|
|
|
|
|
|
keys => [ |
1019
|
|
|
|
|
|
|
{ kid=>"key1", kty=>"oct", k=>"GawgguFyGrWKav7AX4VKUg" }, |
1020
|
|
|
|
|
|
|
{ kid=>"key2", kty=>"oct", k=>"ulxLGy4XqhbpkR5ObGh1gX" }, |
1021
|
|
|
|
|
|
|
] |
1022
|
|
|
|
|
|
|
}; |
1023
|
|
|
|
|
|
|
my $payload = decode_jwt(token=>$t, kid_keys=>$keylist); |
1024
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
The structure described above is used e.g. by L |
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
use Mojo::UserAgent; |
1028
|
|
|
|
|
|
|
my $ua = Mojo::UserAgent->new; |
1029
|
|
|
|
|
|
|
my $google_keys => $ua->get('https://www.googleapis.com/oauth2/v2/certs')->result->json; |
1030
|
|
|
|
|
|
|
my $payload = decode_jwt(token => $t, kid_keys => $google_keys); |
1031
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
B we also support alternative structure used e.g. by L: |
1033
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
use LWP::Simple; |
1035
|
|
|
|
|
|
|
my $google_certs = get('https://www.googleapis.com/oauth2/v1/certs'); |
1036
|
|
|
|
|
|
|
my $payload = decode_jwt(token => $t, kid_keys => $google_certs); |
1037
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
When the token header contains C item the corresponding key is looked up in C list and used for token |
1039
|
|
|
|
|
|
|
decoding (you do not need to pass the explicit key via C parameter). |
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
B When C is specified it croaks if token header does not contain C value or |
1042
|
|
|
|
|
|
|
if C was not found in C. |
1043
|
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
=item key_from_jwk_header |
1045
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
B |
1047
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
C<1> - use C header value for validating JWS signature if neither C nor C specified, B |
1049
|
|
|
|
|
|
|
|
1050
|
|
|
|
|
|
|
C<0> (default) - ignore C header value when validating JWS signature |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
Keep in mind that enabling C requires C header to exist and be an valid RSA/ECDSA public key (otherwise it croaks). |
1053
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
=item allow_none |
1055
|
|
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
C<1> - accept JWS tokens with C 'alg' header value (which means that token has no signature), B |
1057
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
C<0> (default) - do not allow JWS with C 'alg' header value |
1059
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
=item ignore_signature |
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
C<1> - do not check signature on JWS tokens, B |
1063
|
|
|
|
|
|
|
|
1064
|
|
|
|
|
|
|
C<0> (default) - check signature on JWS tokens |
1065
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
=item accepted_alg |
1067
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
C (default) means accept all 'alg' algorithms except 'none' (for accepting 'none' use C) |
1069
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
C name of accepted 'alg' algorithm (only one) |
1071
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
C a list of accepted 'alg' algorithms |
1073
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
C that has to match 'alg' algorithm name |
1075
|
|
|
|
|
|
|
|
1076
|
|
|
|
|
|
|
my $payload = decode_jwt(token=>$t, key=>$k, accepted_alg=>'HS256'); |
1077
|
|
|
|
|
|
|
#or |
1078
|
|
|
|
|
|
|
my $payload = decode_jwt(token=>$t, key=>$k, accepted_alg=>['HS256','HS384']); |
1079
|
|
|
|
|
|
|
#or |
1080
|
|
|
|
|
|
|
my $payload = decode_jwt(token=>$t, key=>$k, accepted_alg=>qr/^HS(256|384|512)$/); |
1081
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
=item accepted_enc |
1083
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
C (default) means accept all 'enc' algorithms |
1085
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
C name of accepted 'enc' algorithm (only one) |
1087
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
C a list of accepted 'enc' algorithms |
1089
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
C that has to match 'enc' algorithm name |
1091
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
my $payload = decode_jwt(token=>$t, key=>$k, accepted_enc=>'A192GCM'); |
1093
|
|
|
|
|
|
|
#or |
1094
|
|
|
|
|
|
|
my $payload = decode_jwt(token=>$t, key=>$k, accepted_enc=>['A192GCM','A256GCM']); |
1095
|
|
|
|
|
|
|
#or |
1096
|
|
|
|
|
|
|
my $payload = decode_jwt(token=>$t, key=>$k, accepted_enc=>qr/^A(128|192|256)GCM$/); |
1097
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
=item decode_payload |
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
C<0> - do not decode payload, return it as a raw string (octects). |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
C<1> - decode payload from JSON string, return it as perl hash ref (or array ref) - decode_json failure means fatal error (croak). |
1103
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
C (default) - if possible decode payload from JSON string, if decode_json fails return payload as a raw string (octets). |
1105
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
=item decode_header |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
C<0> (default) - do not return decoded header as a return value of decode_jwt() |
1109
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
C<1> - return decoded header as a return value of decode_jwt() |
1111
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
my $payload = decode_jwt(token=>$t, key=>$k); |
1113
|
|
|
|
|
|
|
#or |
1114
|
|
|
|
|
|
|
my ($header, $payload) = decode_jwt(token=>$t, key=>$k, decode_header=>1); |
1115
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
=item verify_iss |
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
B If C is specified and |
1119
|
|
|
|
|
|
|
claim C (Issuer) is completely missing it is a failure since 0.024 |
1120
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
C - subroutine (with 'iss' claim value passed as argument) should return C otherwise verification fails |
1122
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
C - 'iss' claim value has to match given regexp otherwise verification fails |
1124
|
|
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
C - 'iss' claim value has to be equal to given string (since 0.029) |
1126
|
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
C (default) - do not verify 'iss' claim |
1128
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
=item verify_aud |
1130
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
B If C is specified and |
1132
|
|
|
|
|
|
|
claim C (Audience) is completely missing it is a failure since 0.024 |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
C - subroutine (with 'aud' claim value passed as argument) should return C otherwise verification fails |
1135
|
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
C - 'aud' claim value has to match given regexp otherwise verification fails |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
C - 'aud' claim value has to be equal to given string (since 0.029) |
1139
|
|
|
|
|
|
|
|
1140
|
|
|
|
|
|
|
C (default) - do not verify 'aud' claim |
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
=item verify_sub |
1143
|
|
|
|
|
|
|
|
1144
|
|
|
|
|
|
|
B If C is specified and |
1145
|
|
|
|
|
|
|
claim C (Subject) is completely missing it is a failure since 0.024 |
1146
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
C - subroutine (with 'sub' claim value passed as argument) should return C otherwise verification fails |
1148
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
C - 'sub' claim value has to match given regexp otherwise verification fails |
1150
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
C - 'sub' claim value has to be equal to given string (since 0.029) |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
C (default) - do not verify 'sub' claim |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
=item verify_jti |
1156
|
|
|
|
|
|
|
|
1157
|
|
|
|
|
|
|
B If C is specified and |
1158
|
|
|
|
|
|
|
claim C (JWT ID) is completely missing it is a failure since 0.024 |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
C - subroutine (with 'jti' claim value passed as argument) should return C otherwise verification fails |
1161
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
C - 'jti' claim value has to match given regexp otherwise verification fails |
1163
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
C - 'jti' claim value has to be equal to given string (since 0.029) |
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
C (default) - do not verify 'jti' claim |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
=item verify_iat |
1169
|
|
|
|
|
|
|
|
1170
|
|
|
|
|
|
|
C - Issued At 'iat' claim must be valid (not in the future) if present |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
C<0> (default) - ignore 'iat' claim |
1173
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
C<1> - require valid 'iat' claim |
1175
|
|
|
|
|
|
|
|
1176
|
|
|
|
|
|
|
=item verify_nbf |
1177
|
|
|
|
|
|
|
|
1178
|
|
|
|
|
|
|
C (default) - Not Before 'nbf' claim must be valid if present |
1179
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
C<0> - ignore 'nbf' claim |
1181
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
C<1> - require valid 'nbf' claim |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
=item verify_exp |
1185
|
|
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
C (default) - Expiration Time 'exp' claim must be valid if present |
1187
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
C<0> - ignore 'exp' claim |
1189
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
C<1> - require valid 'exp' claim |
1191
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
=item leeway |
1193
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
Tolerance in seconds related to C, C and C. Default is C<0>. |
1195
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
=item ignore_claims |
1197
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
C<1> - do not check claims (iat, exp, nbf, iss, aud, sub, jti), B |
1199
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
C<0> (default) - check claims |
1201
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
=back |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
=head2 encode_jwt |
1205
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
my $token = encode_jwt(%named_args); |
1207
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
Named arguments: |
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
=over |
1211
|
|
|
|
|
|
|
|
1212
|
|
|
|
|
|
|
=item payload |
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
Value of this mandatory parameter can be a string/buffer or HASH ref or ARRAY ref |
1215
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
my $token = encode_jwt(payload=>"any raw data", key=>$k, alg=>'HS256'); |
1217
|
|
|
|
|
|
|
#or |
1218
|
|
|
|
|
|
|
my $token = encode_jwt(payload=>{a=>1,b=>2}, key=>$k, alg=>'HS256'); |
1219
|
|
|
|
|
|
|
#or |
1220
|
|
|
|
|
|
|
my $token = encode_jwt(payload=>[11,22,33,44], key=>$k, alg=>'HS256'); |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
HASH refs and ARRAY refs payloads are serialized as JSON strings |
1223
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
=item alg |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
The 'alg' header value is mandatory for both JWE and JWS tokens. |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
Supported JWE 'alg' algorithms: |
1229
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
dir |
1231
|
|
|
|
|
|
|
A128KW |
1232
|
|
|
|
|
|
|
A192KW |
1233
|
|
|
|
|
|
|
A256KW |
1234
|
|
|
|
|
|
|
A128GCMKW |
1235
|
|
|
|
|
|
|
A192GCMKW |
1236
|
|
|
|
|
|
|
A256GCMKW |
1237
|
|
|
|
|
|
|
PBES2-HS256+A128KW |
1238
|
|
|
|
|
|
|
PBES2-HS384+A192KW |
1239
|
|
|
|
|
|
|
PBES2-HS512+A256KW |
1240
|
|
|
|
|
|
|
RSA-OAEP |
1241
|
|
|
|
|
|
|
RSA-OAEP-256 |
1242
|
|
|
|
|
|
|
RSA1_5 |
1243
|
|
|
|
|
|
|
ECDH-ES+A128KW |
1244
|
|
|
|
|
|
|
ECDH-ES+A192KW |
1245
|
|
|
|
|
|
|
ECDH-ES+A256KW |
1246
|
|
|
|
|
|
|
ECDH-ES |
1247
|
|
|
|
|
|
|
|
1248
|
|
|
|
|
|
|
Supported JWS algorithms: |
1249
|
|
|
|
|
|
|
|
1250
|
|
|
|
|
|
|
none ... no integrity (NOTE: disabled by default) |
1251
|
|
|
|
|
|
|
HS256 ... HMAC+SHA256 integrity |
1252
|
|
|
|
|
|
|
HS384 ... HMAC+SHA384 integrity |
1253
|
|
|
|
|
|
|
HS512 ... HMAC+SHA512 integrity |
1254
|
|
|
|
|
|
|
RS256 ... RSA+PKCS1-V1_5 + SHA256 signature |
1255
|
|
|
|
|
|
|
RS384 ... RSA+PKCS1-V1_5 + SHA384 signature |
1256
|
|
|
|
|
|
|
RS512 ... RSA+PKCS1-V1_5 + SHA512 signature |
1257
|
|
|
|
|
|
|
PS256 ... RSA+PSS + SHA256 signature |
1258
|
|
|
|
|
|
|
PS384 ... RSA+PSS + SHA384 signature |
1259
|
|
|
|
|
|
|
PS512 ... RSA+PSS + SHA512 signature |
1260
|
|
|
|
|
|
|
ES256 ... ECDSA + SHA256 signature |
1261
|
|
|
|
|
|
|
ES256K ... ECDSA + SHA256 signature |
1262
|
|
|
|
|
|
|
ES384 ... ECDSA + SHA384 signature |
1263
|
|
|
|
|
|
|
ES512 ... ECDSA + SHA512 signature |
1264
|
|
|
|
|
|
|
EdDSA ... Ed25519 signature |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
=item enc |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
The 'enc' header is mandatory for JWE tokens. |
1269
|
|
|
|
|
|
|
|
1270
|
|
|
|
|
|
|
Supported 'enc' algorithms: |
1271
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
A128GCM |
1273
|
|
|
|
|
|
|
A192GCM |
1274
|
|
|
|
|
|
|
A256GCM |
1275
|
|
|
|
|
|
|
A128CBC-HS256 |
1276
|
|
|
|
|
|
|
A192CBC-HS384 |
1277
|
|
|
|
|
|
|
A256CBC-HS512 |
1278
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
=item key |
1280
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
A key used for token encryption (JWE) or token signing (JWS). The value depends on C token header value. |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
JWS alg header key value |
1284
|
|
|
|
|
|
|
------------------ ---------------------------------- |
1285
|
|
|
|
|
|
|
none no key required |
1286
|
|
|
|
|
|
|
HS256 string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') |
1287
|
|
|
|
|
|
|
HS384 dtto |
1288
|
|
|
|
|
|
|
HS512 dtto |
1289
|
|
|
|
|
|
|
RS256 private RSA key, perl HASH ref with JWK key structure, |
1290
|
|
|
|
|
|
|
a reference to SCALAR string with PEM or DER or JSON/JWK data, |
1291
|
|
|
|
|
|
|
object: Crypt::PK::RSA, Crypt::OpenSSL::RSA, Crypt::X509 or Crypt::OpenSSL::X509 |
1292
|
|
|
|
|
|
|
RS384 private RSA key, see RS256 |
1293
|
|
|
|
|
|
|
RS512 private RSA key, see RS256 |
1294
|
|
|
|
|
|
|
PS256 private RSA key, see RS256 |
1295
|
|
|
|
|
|
|
PS384 private RSA key, see RS256 |
1296
|
|
|
|
|
|
|
PS512 private RSA key, see RS256 |
1297
|
|
|
|
|
|
|
ES256 private ECC key, perl HASH ref with JWK key structure, |
1298
|
|
|
|
|
|
|
a reference to SCALAR string with PEM or DER or JSON/JWK data, |
1299
|
|
|
|
|
|
|
an instance of Crypt::PK::ECC |
1300
|
|
|
|
|
|
|
ES256K private ECC key, see ES256 |
1301
|
|
|
|
|
|
|
ES384 private ECC key, see ES256 |
1302
|
|
|
|
|
|
|
ES512 private ECC key, see ES256 |
1303
|
|
|
|
|
|
|
EdDSA private Ed25519 key |
1304
|
|
|
|
|
|
|
|
1305
|
|
|
|
|
|
|
JWE alg header key value |
1306
|
|
|
|
|
|
|
------------------ ---------------------------------- |
1307
|
|
|
|
|
|
|
dir string (raw octects) or perl HASH ref with JWK, kty=>'oct', length depends on 'enc' algorithm |
1308
|
|
|
|
|
|
|
A128KW string (raw octects) 16 bytes (or perl HASH ref with JWK, kty=>'oct') |
1309
|
|
|
|
|
|
|
A192KW string (raw octects) 24 bytes (or perl HASH ref with JWK, kty=>'oct') |
1310
|
|
|
|
|
|
|
A256KW string (raw octects) 32 bytes (or perl HASH ref with JWK, kty=>'oct') |
1311
|
|
|
|
|
|
|
A128GCMKW string (raw octects) 16 bytes (or perl HASH ref with JWK, kty=>'oct') |
1312
|
|
|
|
|
|
|
A192GCMKW string (raw octects) 24 bytes (or perl HASH ref with JWK, kty=>'oct') |
1313
|
|
|
|
|
|
|
A256GCMKW string (raw octects) 32 bytes (or perl HASH ref with JWK, kty=>'oct') |
1314
|
|
|
|
|
|
|
PBES2-HS256+A128KW string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') |
1315
|
|
|
|
|
|
|
PBES2-HS384+A192KW string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') |
1316
|
|
|
|
|
|
|
PBES2-HS512+A256KW string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') |
1317
|
|
|
|
|
|
|
RSA-OAEP public RSA key, perl HASH ref with JWK key structure, |
1318
|
|
|
|
|
|
|
a reference to SCALAR string with PEM or DER or JSON/JWK data, |
1319
|
|
|
|
|
|
|
an instance of Crypt::PK::RSA or Crypt::OpenSSL::RSA |
1320
|
|
|
|
|
|
|
RSA-OAEP-256 public RSA key, see RSA-OAEP |
1321
|
|
|
|
|
|
|
RSA1_5 public RSA key, see RSA-OAEP |
1322
|
|
|
|
|
|
|
ECDH-ES public ECC or X25519 key, perl HASH ref with JWK key structure, |
1323
|
|
|
|
|
|
|
a reference to SCALAR string with PEM or DER or JSON/JWK data, |
1324
|
|
|
|
|
|
|
an instance of Crypt::PK::ECC |
1325
|
|
|
|
|
|
|
ECDH-ES+A128KW public ECC or X25519 key, see ECDH-ES |
1326
|
|
|
|
|
|
|
ECDH-ES+A192KW public ECC or X25519 key, see ECDH-ES |
1327
|
|
|
|
|
|
|
ECDH-ES+A256KW public ECC or X25519 key, see ECDH-ES |
1328
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
=item keypass |
1330
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
When 'key' parameter is an encrypted private RSA or ECC key this optional parameter may contain a password for private key decryption. |
1332
|
|
|
|
|
|
|
|
1333
|
|
|
|
|
|
|
=item allow_none |
1334
|
|
|
|
|
|
|
|
1335
|
|
|
|
|
|
|
C<1> - allow JWS with C 'alg' header value (which means that token has no signature), B |
1336
|
|
|
|
|
|
|
|
1337
|
|
|
|
|
|
|
C<0> (default) - do not allow JWS with C 'alg' header value |
1338
|
|
|
|
|
|
|
|
1339
|
|
|
|
|
|
|
=item extra_headers |
1340
|
|
|
|
|
|
|
|
1341
|
|
|
|
|
|
|
This optional parameter may contain a HASH ref with items that will be added to JWT header. |
1342
|
|
|
|
|
|
|
|
1343
|
|
|
|
|
|
|
If you want to use PBES2-based 'alg' like C you can set PBES2 salt len (p2s) in bytes and |
1344
|
|
|
|
|
|
|
iteration count (p2c) via C like this: |
1345
|
|
|
|
|
|
|
|
1346
|
|
|
|
|
|
|
my $token = encode_jwt(payload=>$p, key=>$k, alg=>'PBES2-HS512+A256KW', extra_headers=>{p2c=8000, p2s=>32}); |
1347
|
|
|
|
|
|
|
#NOTE: handling of p2s header is a special case, in the end it is replaced with the generated salt |
1348
|
|
|
|
|
|
|
|
1349
|
|
|
|
|
|
|
=item unprotected_headers |
1350
|
|
|
|
|
|
|
|
1351
|
|
|
|
|
|
|
A hash with additional integrity unprotected headers - JWS and JWE (not available for C serialization); |
1352
|
|
|
|
|
|
|
|
1353
|
|
|
|
|
|
|
=item shared_unprotected_headers |
1354
|
|
|
|
|
|
|
|
1355
|
|
|
|
|
|
|
A hash with additional integrity unprotected headers - JWE only (not available for C serialization); |
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
=item aad |
1358
|
|
|
|
|
|
|
|
1359
|
|
|
|
|
|
|
Additional Authenticated Data - scalar value with any (even raw octects) data - JWE only (not available for C serialization); |
1360
|
|
|
|
|
|
|
|
1361
|
|
|
|
|
|
|
=item serialization |
1362
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
Specify serialization method: C (= default) for Compact JWS/JWE serialization or C for Flattened JWS/JWE JSON serialization. |
1364
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
General JSON serialization is not supported yet. |
1366
|
|
|
|
|
|
|
|
1367
|
|
|
|
|
|
|
=item zip |
1368
|
|
|
|
|
|
|
|
1369
|
|
|
|
|
|
|
Compression method, currently 'deflate' is the only one supported. C (default) means no compression. |
1370
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
my $token = encode_jwt(payload=>$p, key=>$k, alg=>'HS256', zip=>'deflate'); |
1372
|
|
|
|
|
|
|
#or define compression level |
1373
|
|
|
|
|
|
|
my $token = encode_jwt(payload=>$p, key=>$k, alg=>'HS256', zip=>['deflate', 9]); |
1374
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
=item auto_iat |
1376
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
C<1> - set 'iat' (Issued At) claim to current time (epoch seconds since 1970) at the moment of token encoding |
1378
|
|
|
|
|
|
|
|
1379
|
|
|
|
|
|
|
C<0> (default) - do not set 'iat' claim |
1380
|
|
|
|
|
|
|
|
1381
|
|
|
|
|
|
|
NOTE: claims are part of the payload and can be used only if the payload is a HASH ref! |
1382
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
=item relative_exp |
1384
|
|
|
|
|
|
|
|
1385
|
|
|
|
|
|
|
Set 'exp' claim (Expiration Time) to current time + C value (in seconds). |
1386
|
|
|
|
|
|
|
|
1387
|
|
|
|
|
|
|
NOTE: claims are part of the payload and can be used only if the payload is a HASH ref! |
1388
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
=item relative_nbf |
1390
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
Set 'nbf' claim (Not Before) to current time + C value (in seconds). |
1392
|
|
|
|
|
|
|
|
1393
|
|
|
|
|
|
|
NOTE: claims are part of the payload and can be used only if the payload is a HASH ref! |
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
=back |
1396
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
=head1 SEE ALSO |
1398
|
|
|
|
|
|
|
|
1399
|
|
|
|
|
|
|
L, L, L, L, L, L |
1400
|
|
|
|
|
|
|
|
1401
|
|
|
|
|
|
|
=head1 LICENSE |
1402
|
|
|
|
|
|
|
|
1403
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. |
1404
|
|
|
|
|
|
|
|
1405
|
|
|
|
|
|
|
=head1 COPYRIGHT |
1406
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
Copyright (c) 2015-2021 DCIT, a.s. L / Karel Miko |