From 283818d7813df956c30a4e59dd49e862547f09ab Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Thu, 22 Mar 2018 16:07:01 +0100 Subject: [PATCH] Imported Upstream version 0.021 --- Changes | 10 +++++++ META.json | 8 +++--- META.yml | 4 +-- README | 24 ++++++++++------- lib/Crypt/JWT.pm | 62 ++++++++++++++++++++++++++++++-------------- lib/Crypt/KeyWrap.pm | 2 +- 6 files changed, 74 insertions(+), 36 deletions(-) diff --git a/Changes b/Changes index fe0f88f..fdfae9e 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,15 @@ Changes for Crypt-JWT distribution +0.021 2018/03/15 + - fix #13 off-by-one in exp verification + +0.020 2018/02/02 + - improved diagnostics + +0.019 2018/01/26 + - fix #11 kid keys + - fix #9 Support for Java lib that pads base64 encoding + 0.018 2016/08/31 - doc fixes - file perms fixes diff --git a/META.json b/META.json index e7cee27..76ef463 100644 --- a/META.json +++ b/META.json @@ -4,13 +4,13 @@ "Karel Miko" ], "dynamic_config" : 1, - "generated_by" : "ExtUtils::MakeMaker version 7.18, CPAN::Meta::Converter version 2.150005", + "generated_by" : "ExtUtils::MakeMaker version 7.3, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", - "version" : "2" + "version" : 2 }, "name" : "Crypt-JWT", "no_index" : { @@ -49,6 +49,6 @@ "url" : "https://github.com/DCIT/perl-Crypt-JWT" } }, - "version" : "0.018", - "x_serialization_backend" : "JSON::PP version 2.27400" + "version" : "0.021", + "x_serialization_backend" : "JSON::PP version 2.94" } diff --git a/META.yml b/META.yml index f3d4a53..6af687a 100644 --- a/META.yml +++ b/META.yml @@ -7,7 +7,7 @@ build_requires: configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 1 -generated_by: 'ExtUtils::MakeMaker version 7.18, CPAN::Meta::Converter version 2.150005' +generated_by: 'ExtUtils::MakeMaker version 7.3, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html @@ -26,5 +26,5 @@ requires: resources: bugtracker: https://github.com/DCIT/perl-Crypt-JWT/issues repository: https://github.com/DCIT/perl-Crypt-JWT -version: '0.018' +version: '0.021' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' diff --git a/README b/README index 42127f6..cb15823 100644 --- a/README +++ b/README @@ -72,7 +72,7 @@ FUNCTIONS PS256 public RSA key, see RS256 PS384 public RSA key, see RS256 PS512 public RSA key, see RS256 - ES256 public ECC key, perl HASH ref with JWK key structure, + ES256 public ECC key, perl HASH ref with JWK key structure, a reference to SCALAR string with PEM or DER or JSON/JWK data, an instance of Crypt::PK::ECC ES384 public ECC key, see ES256 @@ -127,9 +127,9 @@ FUNCTIONS 02p+d5g4OChfFNDhDtnIqjvY -----END PRIVATE KEY----- EOF - + my $jwk_key_json_string = '{"kty":"RSA","n":"0vx7agoebG...L6tSoc_BJECP","e":"AQAB"}'; - + #a reference to SCALAR string with PEM or DER or JSON/JWK data, my $data = decode_jwt(token=>$t, key=>\$pem_key_string); my $data = decode_jwt(token=>$t, key=>\$der_key_string); @@ -172,9 +172,9 @@ FUNCTIONS lBQ9T/RsLLc+PmpB1+7yPAR+oR5gZn3kJQ== -----END EC PRIVATE KEY----- EOF - + my $jwk_key_json_string = '{"kty":"EC","crv":"P-256","x":"MKB..7D4","y":"4Et..FyM"}'; - + #a reference to SCALAR string with PEM or DER or JSON/JWK data, my $data = decode_jwt(token=>$t, key=>\$pem_key_string); my $data = decode_jwt(token=>$t, key=>\$der_key_string); @@ -211,6 +211,12 @@ FUNCTIONS }; my $payload = decode_jwt(token=>$t, kid_keys=>$keylist); + Since 0.19 we also support: + + use LWP::Simple; + my $google_certs = get('https://www.googleapis.com/oauth2/v1/certs'); + my $payload = decode_jwt(token => $t, kid_keys => $google_certs); + When the token header contains 'kid' item the corresponding key is looked up in "kid_keys" list and used for token decoding (you do not need to pass the explicit key via "key" parameter). @@ -421,7 +427,7 @@ FUNCTIONS HS256 string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') HS384 dtto HS512 dtto - RS256 private RSA key, perl HASH ref with JWK key structure, + RS256 private RSA key, perl HASH ref with JWK key structure, a reference to SCALAR string with PEM or DER or JSON/JWK data, object: Crypt::PK::RSA, Crypt::OpenSSL::RSA, Crypt::X509 or Crypt::OpenSSL::X509 RS384 private RSA key, see RS256 @@ -429,7 +435,7 @@ FUNCTIONS PS256 private RSA key, see RS256 PS384 private RSA key, see RS256 PS512 private RSA key, see RS256 - ES256 private ECC key, perl HASH ref with JWK key structure, + ES256 private ECC key, perl HASH ref with JWK key structure, a reference to SCALAR string with PEM or DER or JSON/JWK data, an instance of Crypt::PK::ECC ES384 private ECC key, see ES256 @@ -447,12 +453,12 @@ FUNCTIONS PBES2-HS256+A128KW string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') PBES2-HS384+A192KW string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') PBES2-HS512+A256KW string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') - RSA-OAEP public RSA key, perl HASH ref with JWK key structure, + RSA-OAEP public RSA key, perl HASH ref with JWK key structure, a reference to SCALAR string with PEM or DER or JSON/JWK data, an instance of Crypt::PK::RSA or Crypt::OpenSSL::RSA RSA-OAEP-256 public RSA key, see RSA-OAEP RSA1_5 public RSA key, see RSA-OAEP - ECDH-ES public ECC key, perl HASH ref with JWK key structure, + ECDH-ES public ECC key, perl HASH ref with JWK key structure, a reference to SCALAR string with PEM or DER or JSON/JWK data, an instance of Crypt::PK::ECC ECDH-ES+A128KW public ECC key, see ECDH-ES diff --git a/lib/Crypt/JWT.pm b/lib/Crypt/JWT.pm index db61939..e7de3d3 100644 --- a/lib/Crypt/JWT.pm +++ b/lib/Crypt/JWT.pm @@ -3,7 +3,7 @@ package Crypt::JWT; use strict; use warnings; -our $VERSION = '0.018'; +our $VERSION = '0.021'; use Exporter 'import'; our %EXPORT_TAGS = ( all => [qw(decode_jwt encode_jwt)] ); @@ -76,16 +76,23 @@ sub _prepare_oct_key { sub _kid_lookup { my ($kid, $kid_keys, $alg) = @_; - $kid_keys = decode_json($kid_keys) unless ref $kid_keys; + $kid_keys = eval { decode_json($kid_keys) } unless ref $kid_keys; return undef unless ref $kid_keys eq 'HASH'; - return undef unless exists $kid_keys->{keys} && ref $kid_keys->{keys} eq 'ARRAY'; my $found; - for (@{$kid_keys->{keys}}) { - if ($_->{kid} && $_->{kty} && $_->{kid} eq $kid) { - $found = $_; - last; + if (exists $kid_keys->{keys} && ref $kid_keys->{keys} eq 'ARRAY') { + #FORMAT: { keys => [ {kid=>'A', kty=>?, ...}, {kid=>'B', kty=>?, ...} ] } + for (@{$kid_keys->{keys}}) { + if ($_->{kid} && $_->{kty} && $_->{kid} eq $kid) { + $found = $_; + last; + } } } + else { + #FORMAT: { hexadec1 => "----BEGIN CERTIFICATE-----...", hexadec2 => "----BEGIN CERTIFICATE-----..." } + #e.g. https://www.googleapis.com/oauth2/v1/certs + return \$kid_keys->{$kid} if $kid_keys->{$kid} && !ref $kid_keys->{$kid}; + } return undef if !$found; return $found if $found->{kty} eq 'oct' && $alg =~ /^(HS|dir|PBES2-HS|A)/; return $found if $found->{kty} eq 'EC' && $alg =~ /^(ES|EC)/; @@ -98,7 +105,8 @@ sub _b64u_to_hash { return undef unless $b64url; my $json = decode_b64u($b64url); return undef unless $json; - my $hash = decode_json($json); + my $hash = eval { decode_json($json) }; + return undef unless ref $hash eq 'HASH'; return $hash; } @@ -129,7 +137,7 @@ sub _verify_claims { ### exp if(defined $payload->{exp}) { if (!defined $args{verify_exp} || $args{verify_exp}==1) { - croak "JWT: exp claim check failed ($payload->{exp}/$leeway vs. $now)" if $payload->{exp} + $leeway < $now; + croak "JWT: exp claim check failed ($payload->{exp}/$leeway vs. $now)" if $payload->{exp} + $leeway <= $now; } } elsif ($args{verify_exp} && $args{verify_exp}==1) { @@ -450,6 +458,11 @@ sub _decode_jwe { my $ct = decode_b64u($b64u_ct); my $iv = decode_b64u($b64u_iv); my $tag = decode_b64u($b64u_tag); + croak "JWE: invalid header part" if $b64u_header && !$header; + croak "JWE: invalid ecek part" if $b64u_ecek && !$ecek; + croak "JWE: invalid ct part" if $b64u_ct && !$ct; + croak "JWE: invalid iv part" if $b64u_iv && !$iv; + croak "JWE: invalid tag part" if $b64u_tag && !$tag; my $key = defined $args{keypass} ? [$args{key}, $args{keypass}] : $args{key}; if ($header->{kid} && $args{kid_keys}) { @@ -515,6 +528,7 @@ sub _sign_jws { sub _verify_jws { my ($b64u_header, $b64u_payload, $b64u_sig, $alg, $key) = @_; my $sig = decode_b64u($b64u_sig); + croak "JWS: invalid sig part" if $b64u_sig && !$sig; my $data = "$b64u_header.$b64u_payload"; if ($alg eq 'none' ) { # no integrity @@ -574,6 +588,7 @@ sub _encode_jws { sub _decode_jws { my ($b64u_header, $b64u_payload, $b64u_sig, $unprotected_header, %args) = @_; my $header = _b64u_to_hash($b64u_header); + croak "JWS: invalid header part" if $b64u_header && !$header; $unprotected_header = {} if ref $unprotected_header ne 'HASH'; if (!$args{ignore_signature}) { @@ -602,6 +617,7 @@ sub _decode_jws { croak "JWS: decode failed" if !$valid; } my $payload = decode_b64u($b64u_payload); + croak "JWS: invalid payload part" if $b64u_payload && !$payload; $payload = _payload_unzip($payload, $header->{zip}) if $header->{zip}; $payload = _payload_dec($payload, $args{decode_payload}); _verify_claims($payload, %args) if ref $payload eq 'HASH'; # croaks on error @@ -667,11 +683,11 @@ sub decode_jwt { if (!$args{token}) { croak "JWT: missing token"; } - elsif ($args{token} =~ /^([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]*)\.([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) { + elsif ($args{token} =~ /^([a-zA-Z0-9_-]+)=*\.([a-zA-Z0-9_-]*)=*\.([a-zA-Z0-9_-]+)=*\.([a-zA-Z0-9_-]+)=*\.([a-zA-Z0-9_-]+)=*$/) { # JWE token (5 segments) ($header, $payload) = _decode_jwe($1, $2, $3, $4, $5, undef, {}, {}, %args); } - elsif ($args{token} =~ /^([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]*)$/) { + elsif ($args{token} =~ /^([a-zA-Z0-9_-]+)=*\.([a-zA-Z0-9_-]+)=*\.([a-zA-Z0-9_-]*)=*$/) { # JWS token (3 segments) ($header, $payload) = _decode_jws($1, $2, $3, {}, %args); } @@ -789,7 +805,7 @@ The value depends on the C token header value. PS256 public RSA key, see RS256 PS384 public RSA key, see RS256 PS512 public RSA key, see RS256 - ES256 public ECC key, perl HASH ref with JWK key structure, + ES256 public ECC key, perl HASH ref with JWK key structure, a reference to SCALAR string with PEM or DER or JSON/JWK data, an instance of Crypt::PK::ECC ES384 public ECC key, see ES256 @@ -844,9 +860,9 @@ Examples with RSA keys: 02p+d5g4OChfFNDhDtnIqjvY -----END PRIVATE KEY----- EOF - + my $jwk_key_json_string = '{"kty":"RSA","n":"0vx7agoebG...L6tSoc_BJECP","e":"AQAB"}'; - + #a reference to SCALAR string with PEM or DER or JSON/JWK data, my $data = decode_jwt(token=>$t, key=>\$pem_key_string); my $data = decode_jwt(token=>$t, key=>\$der_key_string); @@ -889,9 +905,9 @@ Examples with ECC keys: lBQ9T/RsLLc+PmpB1+7yPAR+oR5gZn3kJQ== -----END EC PRIVATE KEY----- EOF - + my $jwk_key_json_string = '{"kty":"EC","crv":"P-256","x":"MKB..7D4","y":"4Et..FyM"}'; - + #a reference to SCALAR string with PEM or DER or JSON/JWK data, my $data = decode_jwt(token=>$t, key=>\$pem_key_string); my $data = decode_jwt(token=>$t, key=>\$der_key_string); @@ -927,6 +943,12 @@ This parametes can be either a JWK Set JSON string (see RFC7517) or a perl HASH }; my $payload = decode_jwt(token=>$t, kid_keys=>$keylist); +Since 0.19 we also support: + + use LWP::Simple; + my $google_certs = get('https://www.googleapis.com/oauth2/v1/certs'); + my $payload = decode_jwt(token => $t, kid_keys => $google_certs); + When the token header contains 'kid' item the corresponding key is looked up in C list and used for token decoding (you do not need to pass the explicit key via C parameter). @@ -1143,7 +1165,7 @@ A key used for token encryption (JWE) or token signing (JWS). The value depends HS256 string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') HS384 dtto HS512 dtto - RS256 private RSA key, perl HASH ref with JWK key structure, + RS256 private RSA key, perl HASH ref with JWK key structure, a reference to SCALAR string with PEM or DER or JSON/JWK data, object: Crypt::PK::RSA, Crypt::OpenSSL::RSA, Crypt::X509 or Crypt::OpenSSL::X509 RS384 private RSA key, see RS256 @@ -1151,7 +1173,7 @@ A key used for token encryption (JWE) or token signing (JWS). The value depends PS256 private RSA key, see RS256 PS384 private RSA key, see RS256 PS512 private RSA key, see RS256 - ES256 private ECC key, perl HASH ref with JWK key structure, + ES256 private ECC key, perl HASH ref with JWK key structure, a reference to SCALAR string with PEM or DER or JSON/JWK data, an instance of Crypt::PK::ECC ES384 private ECC key, see ES256 @@ -1169,12 +1191,12 @@ A key used for token encryption (JWE) or token signing (JWS). The value depends PBES2-HS256+A128KW string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') PBES2-HS384+A192KW string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') PBES2-HS512+A256KW string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct') - RSA-OAEP public RSA key, perl HASH ref with JWK key structure, + RSA-OAEP public RSA key, perl HASH ref with JWK key structure, a reference to SCALAR string with PEM or DER or JSON/JWK data, an instance of Crypt::PK::RSA or Crypt::OpenSSL::RSA RSA-OAEP-256 public RSA key, see RSA-OAEP RSA1_5 public RSA key, see RSA-OAEP - ECDH-ES public ECC key, perl HASH ref with JWK key structure, + ECDH-ES public ECC key, perl HASH ref with JWK key structure, a reference to SCALAR string with PEM or DER or JSON/JWK data, an instance of Crypt::PK::ECC ECDH-ES+A128KW public ECC key, see ECDH-ES diff --git a/lib/Crypt/KeyWrap.pm b/lib/Crypt/KeyWrap.pm index e9ec735..1fa10a1 100644 --- a/lib/Crypt/KeyWrap.pm +++ b/lib/Crypt/KeyWrap.pm @@ -3,7 +3,7 @@ package Crypt::KeyWrap; use strict; use warnings; -our $VERSION = '0.018'; +our $VERSION = '0.021'; use Exporter 'import'; our %EXPORT_TAGS = ( all => [qw(aes_key_wrap aes_key_unwrap gcm_key_wrap gcm_key_unwrap pbes2_key_wrap pbes2_key_unwrap ecdh_key_wrap ecdh_key_unwrap ecdhaes_key_wrap ecdhaes_key_unwrap rsa_key_wrap rsa_key_unwrap)] );