File Coverage

blib/lib/Crypt/JWT.pm
Criterion Covered Total %
statement 468 518 90.3
branch 331 474 69.8
condition 90 184 48.9
subroutine 42 42 100.0
pod 2 2 100.0
total 933 1220 76.4


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