Crypt-JWT

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

Changes for Crypt-JWT distribution

0.038   2026-05-16
        - SECURITY:
            * constant-time MAC compare;
            * enforce JWK alg/use/key_ops and EC alg/crv consistency;
            * reject mixed-symmetry or duplicate-kid keysets;
            * cap PBES2 p2c and inflated payload size;
            * new $MIN_HMAC_KEY_LEN (4) and $MIN_RSA_BITS (2048);
            * new section SECURITY CONSIDERATIONS in POD
        - fix: ConcatKDF: INTEROP BREAK with <=0.037 for ECDH-ES + A192CBC-HS384 / A256CBC-HS512 only
        - fix: ECDH-ES apu/apv header values are base64url-decoded before KDF input
        - fix: AAD bit-length encoding (only diverged at AAD >= 512 MB)
        - fix: accepted_alg / accepted_enc now croak on unsupported types
        - aes_key_wrap/unwrap:
            * strict RFC 3394 (KW) vs RFC 5649 (KWP) modes;
            * ct length validation
            * fix unwrap of aligned KWP messages
        - require Compress::Raw::Zlib >= 2.057
        - new author-only Wycheproof harness t/wycheproof.t (AUTHOR_MODE=1)

README.md  view on Meta::CPAN


- enc

    The 'enc' header is mandatory for JWE tokens.

    Supported 'enc' algorithms:

        A128GCM
        A192GCM
        A256GCM
        A128CBC-HS256
        A192CBC-HS384
        A256CBC-HS512

- key

    A key used for token encryption (JWE) or token signing (JWS). The value depends on `alg` token header value.

        JWS alg header      key value
        ------------------  ----------------------------------
        none                no key required
        HS256               string (raw octets) of any length (or perl HASH ref with JWK, kty=>'oct')
        HS384               same as HS256

lib/Crypt/JWT.pm  view on Meta::CPAN


  if ($alg eq 'dir') {
    return (_prepare_oct_key($key), '');
  }

  my $cek;
  my $ecek;
  if ($enc =~ /^A(128|192|256)GCM/) {
    $cek = random_bytes($1/8);
  }
  elsif ($enc =~ /^A(128|192|256)CBC/) {
    $cek = random_bytes(2*$1/8);
  }

  if ($alg =~ /^A(128|192|256)KW$/) {
    # RFC 7518 sec 4.4 wraps via "AES Key Wrap algorithm specified in RFC 3394"
    $ecek = aes_key_wrap(_prepare_oct_key($key), $cek, 'AES', 0);
    return ($cek, $ecek);
  }
  elsif ($alg =~ /^A(128|192|256)GCMKW$/) {
    my ($t, $i);

lib/Crypt/JWT.pm  view on Meta::CPAN

  my $aad = defined $b64u_aad ? "$b64u_header.$b64u_aad" : $b64u_header;
  if ($enc =~ /^A(128|192|256)GCM$/) {
    # https://tools.ietf.org/html/rfc7518#section-5.3
    my $len1 = $1/8;
    my $len2 = length($cek);
    croak "JWE: wrong AES key length ($len1 vs. $len2) for $enc" unless $len1 == $len2;
    my $iv = random_bytes(12); # for AESGCM always 12 (96 bits)
    my ($ct, $tag) = gcm_encrypt_authenticate('AES', $cek, $iv, $aad, $payload);
    return ($ct, $iv, $tag);
  }
  elsif ($enc =~ /^A(128|192|256)CBC-HS(256|384|512)$/) {
    # https://tools.ietf.org/html/rfc7518#section-5.2
    my ($size, $hash) = ($1/8, "SHA$2");
    my $key_len = length($cek) / 2;
    my $mac_key = substr($cek, 0, $key_len);
    my $aes_key = substr($cek, $key_len, $key_len);
    croak "JWE: wrong AES key length ($key_len vs. $size)" unless $key_len == $size;
    my $iv = random_bytes(16); # for AES always 16
    my $m = Crypt::Mode::CBC->new('AES');
    my $ct = $m->encrypt($payload, $aes_key, $iv);
    my $aad_len = length($aad);
    # RFC 7518 5.2.2.1: AL = AAD length in bits as 64-bit big-endian.
    # Split aad_len*8 into two 32-bit halves; both intermediate values
    # stay within 32-bit range, so this is safe on 32-bit Perl too.
    my $al_hi = $aad_len >> 29;
    my $al_lo = ($aad_len & 0x1FFFFFFF) << 3;
    my $mac_input = $aad . $iv . $ct . pack('N2', $al_hi, $al_lo);
    my $mac = hmac($hash, $mac_key, $mac_input);
    my $sig_len = length($mac) / 2;

lib/Crypt/JWT.pm  view on Meta::CPAN


sub _decrypt_jwe_payload {
  my ($cek, $enc, $aad, $ct, $iv, $tag) = @_;
  if ($enc =~ /^A(128|192|256)GCM$/) {
    # https://tools.ietf.org/html/rfc7518#section-5.3
    my $len1 = $1/8;
    my $len2 = length($cek);
    croak "JWE: wrong AES key length ($len1 vs. $len2) for $enc" unless $len1 == $len2;
    return gcm_decrypt_verify('AES', $cek, $iv, $aad, $ct, $tag);
  }
  elsif ($enc =~ /^A(128|192|256)CBC-HS(256|384|512)$/) {
    # https://tools.ietf.org/html/rfc7518#section-5.2
    my ($size, $hash) = ($1/8, "SHA$2");
    my $key_len = length($cek) / 2;
    my $mac_key = substr($cek, 0, $key_len);
    my $aes_key = substr($cek, $key_len, $key_len);
    croak "JWE: wrong AES key length ($key_len vs. $size)" unless $key_len == $size;
    my $aad_len = length($aad); # AAD == original encoded header
    # RFC 7518 5.2.2.1: AL = AAD length in bits as 64-bit big-endian.
    # Split aad_len*8 into two 32-bit halves; both intermediate values
    # stay within 32-bit range, so this is safe on 32-bit Perl too.
    my $al_hi = $aad_len >> 29;
    my $al_lo = ($aad_len & 0x1FFFFFFF) << 3;
    my $mac_input = $aad . $iv . $ct . pack('N2', $al_hi, $al_lo);
    my $mac = hmac($hash, $mac_key, $mac_input);
    my $sig_len = length($mac) / 2;
    my $sig = substr($mac, 0, $sig_len);
    croak "JWE: tag mismatch" unless slow_eq($sig, $tag);
    my $m = Crypt::Mode::CBC->new('AES');
    my $pt = $m->decrypt($ct, $aes_key, $iv);
    return $pt;
  }
  croak "JWE: unsupported enc '$enc'";
}

sub _encode_jwe {
  my %args = @_;
  my $payload = $args{payload};
  my $alg     = $args{alg};

lib/Crypt/JWT.pm  view on Meta::CPAN


=item enc

The 'enc' header is mandatory for JWE tokens.

Supported 'enc' algorithms:

 A128GCM
 A192GCM
 A256GCM
 A128CBC-HS256
 A192CBC-HS384
 A256CBC-HS512

=item key

A key used for token encryption (JWE) or token signing (JWS). The value depends on C<alg> token header value.

 JWS alg header      key value
 ------------------  ----------------------------------
 none                no key required
 HS256               string (raw octets) of any length (or perl HASH ref with JWK, kty=>'oct')
 HS384               same as HS256

lib/Crypt/KeyWrap.pm  view on Meta::CPAN

                                     pack("N", length($apv)) . $apv .
                                     pack("N", 8 *$key_size));
  }
  return substr($data, 0, $key_size);
}

sub ecdh_key_wrap {
  my ($kek_public, $enc, $apu, $apv) = @_;
  croak "ecdh_key_wrap: no Crypt::PK::ECC" unless ref $kek_public eq 'Crypt::PK::ECC';
  my $encryption_key_size = 256;
  if ($enc =~ /^A(128|192|256)CBC-HS/) {
    $encryption_key_size = $1*2;
  }
  if ($enc =~ /^A(128|192|256)GCM/) {
    $encryption_key_size = $1;
  }
  my $ephemeral = Crypt::PK::ECC->new()->generate_key($kek_public->curve2hash);
  my $shared_secret = $ephemeral->shared_secret($kek_public);
  my $ct_data = _concat_kdf('SHA256', $encryption_key_size/8, $shared_secret, $enc, $apu, $apv);
  return ($ct_data, $ephemeral->export_key_jwk('public'));
}

sub ecdh_key_unwrap {
  my ($kek_private, $enc, $epk, $apu, $apv) = @_;
  croak "ecdh_key_unwrap: no Crypt::PK::ECC" unless ref $kek_private eq 'Crypt::PK::ECC';
  croak "ecdh_key_unwrap: no private key" unless $kek_private->is_private;
  my $encryption_key_size = 256;
  if ($enc =~ /^A(128|192|256)CBC-HS/) {
    $encryption_key_size = $1*2;
  }
  if ($enc =~ /^A(128|192|256)GCM/) {
    $encryption_key_size = $1;
  }
  my $ephemeral = ref($epk) eq 'Crypt::PK::ECC' ? $epk : Crypt::PK::ECC->new(ref $epk ? $epk : \$epk);
  my $shared_secret = $kek_private->shared_secret($ephemeral);
  my $pt_data = _concat_kdf('SHA256', $encryption_key_size/8, $shared_secret, $enc, $apu, $apv);
  return $pt_data;
}

t/jwe_ecdh_es_cbc_hs_tv.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;

use Crypt::JWT qw(decode_jwt);

# Regression vectors for the ConcatKDF fix (counter-per-iteration + honour the
# requested hash). The bug only manifested when key_size > hash_size, i.e.
# ECDH-ES (direct) paired with A192CBC-HS384 (48-byte key) or A256CBC-HS512
# (64-byte key). Round-trip tests in this repo could not catch it because both
# encode and decode were equally wrong.
#
# These two tokens were produced by jwcrypto 1.5.7 (an independent, spec-
# compliant JOSE implementation) and encrypt the payload {"hello":"world"} to
# the static EC P-256 keypair below. A regression in _concat_kdf would derive
# the wrong CEK and decryption would fail.

my $ec_priv_pem = <<'EOF';
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg26+F/ifLwUlXYaEF
iUMpiTEljnxJVBLXOUMnmoF8pO6hRANCAAQtJcfPtxnqrdBNe2R2gqTlHOgnfnmz
TEh6MynIJ44MiITSmLZhlFP8GdX+A0lcKPzS6tN2W6Zu9gZjtpwR65ab
-----END PRIVATE KEY-----
EOF

my %vectors = (
  'ECDH-ES + A192CBC-HS384' =>
    'eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTE5MkNCQy1IUzM4NCIsImVwayI6eyJjcnYi'
  . 'OiJQLTI1NiIsImt0eSI6IkVDIiwieCI6ImZiNkpNTXdlQlpHc0xPeVdwUVZHSUNRUm0w'
  . 'LVIzRElMUkdxYzNIZDJPVDgiLCJ5IjoiVE9hUUpOSVNkWFlad3ByOTRpT2tSTi1mSHZO'
  . 'S2t0WDlROTVHbGJpN1pjQSJ9fQ..QqoyONjUURBsmQswE1-3gw.BXnr518x06QecAi9'
  . '_9Baz5ytVeLt1MBRRrFijlxrpRs.Qx7Artlj1MNZcGjqeVWPVVrJpciQoKyJ',
  'ECDH-ES + A256CBC-HS512' =>
    'eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTI1NkNCQy1IUzUxMiIsImVwayI6eyJjcnYi'
  . 'OiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjlJRTI3b0hrTjJ0LWpVaGZnbjA3aGtqc1hV'
  . 'VG8tV3hLclNxVjJXMTNuZ3ciLCJ5IjoiRUVybE1kNGtVdDU5VnRQUUhRMUtvaE85MUxT'
  . 'UzJqOTZnUTdMVC1hb1FTTSJ9fQ..JQE5msVIbA-4Fq5W6fhwRA.VdZ9WfNwCEVaQunX'
  . 'pwTTz58LQl6u1ASV6_gwJzSHg0Y.EOAirtbVgDnIRcwDHKBuF777pnT8lYPrGAgPdo14Eho',
);

my $expected = '{"hello":"world"}';

for my $label (sort keys %vectors) {

t/jwt_decode_tv.t  view on Meta::CPAN

  my $expected = '{"hello": "world"}';
  is($json, $expected, 'PS384');
}
### PS512
{
  my $token = "eyJhbGciOiJQUzUxMiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.IvbnmxhKvM70C0n0grkF807wOQLyPOBwJOee-p7JHCQcSstNeml3Owdyw9C3HGHzOdK9db51yAkjJ2TCojxqHW4OR5Apna8tvafYgD2femn1V3GdkGj6ZvYdV3q4ldnmahVeO36vHYy5P0zFcEGU1_j3S3DwGmhw2k...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>\$rsaPub) } or warn $@;
  my $expected = '{"hello": "world"}';
  is($json, $expected, 'PS512');
}
### RSA_OAEP_A128CBC_HS256
{
  my $token = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhDQkMtSFMyNTYifQ.dgIoddBRTBLi8b6fwjaIU5uUP_J-6jL5AtIvoNZDwN0JSmsXkm9SIFz7kQfwavBz_PPG6h0yId55YVFnCqrB5qCIbifmBQPEcB5acKCybHuoHhEBCnQpqxVtHLXZ0dUyd6Xs5h9ymgbbZMjpAoCUK7si90m4O5BCSdedZNQvdXWQW599CRft...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>\$rsaPriv) } or warn $@;
  my $expected = '{"exp":1391196668,"sub":"alice","nbf":1391196068,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"03ac026e-55aa-4475-a806-f09e83048922","iat":1391196068}';
  is($json, $expected, 'RSA_OAEP_A128CBC_HS256');
}
### RSA_OAEP_A128CBC_HS256_Compressed
{
  my $token = "eyJhbGciOiJSU0EtT0FFUCIsInppcCI6IkRFRiIsImVuYyI6IkExMjhDQkMtSFMyNTYifQ.nXSS9jDwE0dXkcGI7UquZBhn2nsB2P8u-YSWEuTAgEeuV54qNU4SlE76bToI1z4LUuABHmZOv9S24xkF45b7Mrap_Fu4JXH8euXrQgKQb9o_HL5FvE8m4zk5Ow13MKGPvHvWKOaNEBFriwYIfPi6QBYrpuqn0BaANc_a...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>\$rsaPriv) } or warn $@;
  my $expected = '{"exp":1392963710,"sub":"alice","nbf":1392963110,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"9fa7a38a-28fd-421c-825c-8fab3bbf3fb4","iat":1392963110}';
  is($json, $expected, 'RSA_OAEP_A128CBC_HS256_Compressed');
}
### RSA_OAEP_A256CBC_HS512
{
  my $token = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZDQkMtSFM1MTIifQ.gCLatpLEIHdwqXGNQ6pI2azteXmksiGvHZoXaFmGjvN6T71ky5Ov8DHmXyaFdxWVPxiPAf6RDpJlokSR34e1W9ey0m9xWELJ_hH_bEoH4s7-wI74edS06i35let0YvCubl3eIemuQNkaJEqoEaHx8sLZ-SsoRxi7tRAIABl4f_THC8CDLw7S...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>\$rsaPriv) } or warn $@;
  my $expected = '{"exp":1391196668,"sub":"alice","nbf":1391196068,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"19539b79-e5cf-4f99-a66e-00a980e1b0a9","iat":1391196068}';
  is($json, $expected, 'RSA_OAEP_A256CBC_HS512');
}
### RSA_OAEP_A192CBC_HS384
{
  my $token = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExOTJDQkMtSFMzODQifQ.BZ8MgMgby05auOw-Gb4ii-fgcRWAlCHd6pMZNFafle6BAT1accRGUsMGRzJRETUFFqoy3rzfdSdFcqgc7lmUQUXrVei6XCRei5VZJo1YlzIPN9rEig3sSJ99hg1mrXh3ezFX_JczTn7xEaRRzdatnkSvWBMMmbMWVjqlpkXSOr7P7x2Ctf-G...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>\$rsaPriv) } or warn $@;
  my $expected = '{"exp":1391196668,"sub":"alice","nbf":1391196068,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"59f54c91-5224-4484-9c3a-e57b87b6f212","iat":1391196068}';
  is($json, $expected, 'RSA_OAEP_A192CBC_HS384');
}
### RSA_OAEP_256_A128CBC_HS256
{
  my $token = "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.bje66yTjMUpyGzbt3QvPNOmCmUPowgEmoBHXw-pByhST2VBSs0_67JKDymKW0VpmQC5Qb7ZLC6nNG8YW5pxTZDOeTQLodhAvzoNAsrx4M2R_N58ZVqBPLKTq7FKi1NNd8oJ80dwWbOJ13dkLH68SlhOK5bhqKFgtbzalnglL2kq8Fki...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>\$rsaPriv) } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'RSA_OAEP_256_A128CBC_HS256');
}
### RSA_OAEP_256_A192CBC_HS384
{
  my $token = "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0In0.COuKvozBVi2vkEPpFdx0HTMpU9tmpP1lLngbmGn8RVphY-vjhVaduv8D_Ay_1j8LuMz4tgP98xWtbJkTyhxY1kBwXe0CgqFUOSJ1mTEPRkKSXpdFR7rT1Pv68qug2yKaXT_qcviyBerIcUVFbXBmtiYAosYO4kaPSOE1IvLadFOrMkx...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>\$rsaPriv) } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'RSA_OAEP_256_A192CBC_HS384');
}
### RSA_OAEP_256_A256CBC_HS512
{
  my $token = "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0.Pt1q6MNdaiVWhMnY7r6DVpkYQmzyIjhb0cj10LowP_FgMu1dOQVuNwhK14MO1ki1y1Pvxouct9wwmb5gE7jNJBy6vU-FrrY62WNr_hKL3Cq2030LlJwauv1XQrEE-GCw1srxOAsw6LNT14v4f0qjeW46mIHNX4CZMEO9ntwojWsHTNs...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>\$rsaPriv) } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'RSA_OAEP_256_A256CBC_HS512');
}
### RSA_1_5_A128CBC_HS256
{
  my $token = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.bx_4TL7gh14IeM3EClP3iVfY9pbT81pflXd1lEZOVPJR6PaewRFXWmiJcaqH9fcU9IjGGQ19BS-UPtpErenL5kw7KORFgIBm4hObCYxLoAadMy8A-qQeOWyjnxbE0mbQIdoFI4nGK5qWTEQUWZCMwosvyeHLqEZDzr9CNLAAFTujvsZJJ7NLTkA...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>\$rsaPriv) } or warn $@;
  my $expected = '{"exp":1391196668,"sub":"alice","nbf":1391196068,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"3814fff3-db66-45d9-a29a-d2cc2407bdcf","iat":1391196068}';
  is($json, $expected, 'RSA_1_5_A128CBC_HS256');
}
### RSA_1_5_A192CBC_HS384
{
  my $token = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0In0.ApUpt1SGilnXuqvFSHdTV0K9QKSf0P6wEEOTrAqWMwyEOLlyb6VR8o6fdd4wXMTkkL5Bp9BH1x0oibTrVwVa50rxbPDlRJQe0yvBm0w02nkzl3Tt4fE3sGjEXGgI8w8ZxSVAN0EkaXLqzsG1rQ631ptzqyNzg9BWfy53cHhuzh9w00ZOXZtNc7G...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>\$rsaPriv) } or warn $@;
  my $expected = '{"exp":1391196668,"sub":"alice","nbf":1391196068,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"c9d44ff8-ff1e-4490-8454-941e45766152","iat":1391196068}';
  is($json, $expected, 'RSA_1_5_A192CBC_HS384');
}
### RSA_1_5_A256CBC_HS512
{
  my $token = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0.GVXwkd5rfqffr4ue26IGHXuiV6r-rQa9OQ4B1LtodsTpWfraOLyhyHYseEKpXV4aSMWWN0q2HS0myj73BuGsDMP-xiIM04QxWD7dbP2OticXzktcHHhMFUx0OK_IOmc21qshTqbb0yKWizMnCuVosQqw2tg_up2sgjqIyiwzpgvC5_l9ddxnTBV...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>\$rsaPriv) } or warn $@;
  my $expected = '{"exp":1391196668,"sub":"alice","nbf":1391196068,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"7efcdbc6-b2b5-4480-985d-bdf741b376bb","iat":1391196068}';
  is($json, $expected, 'RSA_1_5_A256CBC_HS512');
}
### RSA_OAEP_A128GCM
{
  my $token = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.Izae78a1L2Z0ai_aYbvVbWjiZwz3DTlD27c4Jh44SZAz7T_w7GHiWGuxa4CYPq4Ul_9i5qpdUK1WJOTxlL8C-TXbWzxgwhs-DdmkRBmI5JWozc6RIYz2ddYBIPDTpOSbg_nwVzCUkqId6PwATSPiYjLY0ZwsSung1JGuSKU5WHzdCLh8cXKFdSNo4PA6...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>\$rsaPriv) } or warn $@;
  my $expected = '{"exp":1391705293,"sub":"alice","nbf":1391704693,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"2f3b5379-a851-4202-ac9a-85baae41459e","iat":1391704693}';
  is($json, $expected, 'RSA_OAEP_A128GCM');
}
### RSA_OAEP_A192GCM
{

t/jwt_decode_tv.t  view on Meta::CPAN

  my $expected = '{"exp":1392552631,"sub":"alice","nbf":1392552031,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"a3fea096-2e96-4d8b-b7cd-070e08b533fb","iat":1392552031}';
  is($json, $expected, 'DIR_A192GCM');
}
### DIR_A256GCM
{
  my $token = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..Fmz3PLVfv-ySl4IJ.LMZpXMDoBIll5yuEs81Bws2-iUUaBSpucJPL-GtDKXkPhFpJmES2T136Vd8xzvp-3JW-fvpRZtlhluqGHjywPctol71Zuz9uFQjuejIU4axA_XiAy-BadbRUm1-25FRT30WtrrxKltSkulmIS5N-Nsi_zmCz5xicB1ZnzneRXGaXY4B44...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$aes256Key) } or warn $@;
  my $expected = '{"exp":1392552841,"sub":"alice","nbf":1392552241,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"efdfc02f-945e-4e1f-85a6-9f240f6cf153","iat":1392552241}';
  is($json, $expected, 'DIR_A256GCM');
}
### DIR_A128CBC_HS256
{
  my $token = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..3lClLoerWhxIc811QXDLbg.iFd5MNk2eWDlW3hbq7vTFLPJlC0Od_MSyWGakEn5kfYbbPk7BM_SxUMptwcvDnZ5uBKwwPAYOsHIm5IjZ79LKZul9ZnOtJONRvxWLeS9WZiX4CghOLZL7dLypKn-mB22xsmSUbtizMuNSdgJwUCxEmms7vYOpL0Che-...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$aes256Key) } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'DIR_A128CBC_HS256');
}
### ECDH_ES_A128CBC_HS256
{
  my $token = "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOENCQy1IUzI1NiIsImVwayI6eyJrdHkiOiJFQyIsIngiOiItVk1LTG5NeW9IVHRGUlpGNnFXNndkRm5BN21KQkdiNzk4V3FVMFV3QVhZIiwieSI6ImhQQWNReTgzVS01Qjl1U21xbnNXcFZzbHVoZGJSZE1nbnZ0cGdmNVhXTjgiLCJjcnYiOiJQLTI1NiJ9fQ..UA3...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$Ecc256Private) } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'ECDH_ES_A128CBC_HS256');
}
### ECDH_ES_A128GCM
{
  my $token = "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJrdHkiOiJFQyIsIngiOiJPbDdqSWk4SDFpRTFrcnZRTmFQeGp5LXEtY3pQME40RVdPM1I3NTg0aEdVIiwieSI6Ik1kU2V1OVNudWtwOWxLZGU5clVuYmp4a3ozbV9kTWpqQXc5NFd3Q0xaa3MiLCJjcnYiOiJQLTI1NiJ9fQ..E4XwpWZ2kO-...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$Ecc256Private) } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'ECDH_ES_A128GCM');
}
### ECDH_ES_A192GCM
{

t/jwt_decode_tv.t  view on Meta::CPAN

  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'ECDH_ES_A192KW_A192GCM');
}
### ECDH_ES_A256KW_A256GCM
{
  my $token = "eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZHQ00iLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiQU5UZy1LOFlBVXVPazBtUW1aTERQVWlPcVZFUFBrLVBmNmtSdG42Y0IycyIsInkiOiJsSmk3UExFRGU2WndxSjQ2alpyLUZtUHp5c3dGa3BkSVU3WlUzNHQ4RURzIiwiY3J2IjoiUC0yNTYifX0.Iqp...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$Ecc256Private) } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'ECDH_ES_A256KW_A256GCM');
}
### PBSE2_HS256_A128KW_A128CBC_HS256
{
  my $token = "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwicDJjIjo4MTkyLCJwMnMiOiJiMFlFVmxMemtaNW9UUjBMIn0.dhPAhJ9kmaEbP-02VtEoPOF2QSEYM5085V6zYt1U1qIlVNRcHTGDgQ.4QAAq0dVQT41dQKDG7dhRA.H9MgJmesbU1ow6GCa0lEMwv8A_sHvgaWKkaMcdoj_...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>"top secret") } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'PBSE2_HS256_A128KW_A128CBC_HS256');
}
### PBSE2_HS256_A128KW_A256GCM
{
  my $token = "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMjU2R0NNIiwicDJjIjo4MTkyLCJwMnMiOiJqVVozY0NEX2hMZ3pQVHVfIn0.cgEZLTNOoGgDJXhRj0Ca0DL_HTY2xRKzVpoRnOf_Yuxm6IsQJgf0NA.7sAUk5_ryTMO_hLB.y7arc1aQP1--WUwlUsti4SiW6O2nrmGviTYznPjw9KD9Tu4E4QQO3RC...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>"top secret") } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'PBSE2_HS256_A128KW_A256GCM');
}
### PBSE2_HS384_A192KW_A192CBC_HS384
{
  my $token = "eyJhbGciOiJQQkVTMi1IUzM4NCtBMTkyS1ciLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwicDJjIjo4MTkyLCJwMnMiOiIxZEdaODBpQTBqb3lGTzFqIn0.iElgf12HbQWt3enumKP_j3WDxGLfbwSePHYAbYEb_w3himk0swcdiTPo1Jm8MU7le7L_Z8rU2Uk.7LoW9-g7U8c3GNAYO3Z5Jw.guSjXuYN9deq6XIsbkbx...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>"top secret") } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'PBSE2_HS384_A192KW_A192CBC_HS384');
}
### PBSE2_HS512_A256KW_A256CBC_HS512
{
  my $token = "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwicDJjIjo4MTkyLCJwMnMiOiJCUlkxQ1M3VXNpaTZJNzhkIn0.ovjAL7yRnB_XdJbK8lAaUDRZ-CyVeio8f4pnqOt1FPj1PoQAdEX3S5x6DlzR8aqN_WR5LUwdqDSyUDYhSurnmq8VLfzd3AEe.YAjH6g_zekXJIlPN4Ooo5Q...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>"top secret") } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'PBSE2_HS512_A256KW_A256CBC_HS512');
}
### A128GCMKW_A128CBC_HS256
{
  my $token = "eyJhbGciOiJBMTI4R0NNS1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiaXYiOiJ1SDVxVThlN2JVZXhGYWh3IiwidGFnIjoiamdxc2czdHoyUGo0QmhEWU1xTnBrdyJ9.peAzKiVO3_w2tAlSzRZdqqQpnUSpgPDHi_xgTd6VzP4.o8bhvYO_UTkrsxQmm__nIg.MSmgetpjXHWMs0TyuGgmWd-msfbQ7oVWC4WuCJc...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$aes128Key) } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'A128GCMKW_A128CBC_HS256');
}
### A128GCMKW_A256CBC_HS512
{
  my $token = "eyJhbGciOiJBMTI4R0NNS1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiaXYiOiI5bUwxR1YzUUZIWGtVbEdUIiwidGFnIjoiU0xrTDVpdmhncy1HNjRBTS01bTBxdyJ9.S3_MudWEzKWCp8RRxIG5p6H2YOtMDCkOXXKM9J8J4lMX5N2CcUqsKkDQ4TE1rG7gD5qYgHsb8AiQFLbhjgDeAA.WiOHBPlws9hImQr6bZ8...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$aes128Key) } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'A128GCMKW_A256CBC_HS512');
}
### A192GCMKW_A192CBC_HS384
{
  my $token = "eyJhbGciOiJBMTkyR0NNS1ciLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiaXYiOiJzRHRLdnRzZVk2UkFuU2twIiwidGFnIjoiZDFDS3dKWnlXSnlvcW5HTUFwbmR6dyJ9.2L9u7vV0P8bZddbkCKKe6_C5JTLf8wRZC8xzEe4gvmcGoF2K5AledhcqT6mIlaPx.1JY51r77jimrvKxts9EroQ.922BMD0HOscwZxn4pm...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$aes192Key) } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'A192GCMKW_A192CBC_HS384');
}
### A256GCMKW_A256CBC_HS512
{
  my $token = "eyJhbGciOiJBMjU2R0NNS1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiaXYiOiJvUV9xbDNJUHNibEVPaDBXIiwidGFnIjoieTllMEFfY1hZMnNDZ24tamxsNl9TdyJ9.K5BxtxcV0simNM-69RvjZuNBjxaavDVnBzP7EFXSjbWZi3NjZFoTcFljcu2TuzR_F9zdjjBbohEgaf4kUMVZfg.881rEerOD33OLCHKdTW...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$aes256Key) } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'A256GCMKW_A256CBC_HS512');
}
### A128KW_A128CBC_HS256
{
  my $token = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.DPRoUHQ3Ac8duyD32nUNH3eNUKzUIMYgEdf5GwJ8rW4MYQdl2PCIHA.B1dR6t93aUPcFC1c1aUjeA.lHPKTK0ehgzq70_Ihdh-svI2icUa9usgqP8sF5j50fsQAGizITZpTTXKOKd9-GSEVmJo07551hq9xscZj4vXsDEx-z-akxg0nlL5fFE24...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$aes128Key) } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'A128KW_A128CBC_HS256');
}
### A192KW_A192CBC_HS384
{
  my $token = "eyJhbGciOiJBMTkyS1ciLCJlbmMiOiJBMTkyQ0JDLUhTMzg0In0.OLwgc7EaQdvsf54GfU69qH143C79H_eETvM_yGBgJzEB5367k9tbw6qW4TlQ56GMj__5QDJBvAg.BvYY_v4_dxxsK4M8A0T_TA.V0jBe7o-OahMkqGDgWW0Lxq1eTKPJYix7hjKmmqaKlhdVcnT0cdOU0ahdg82Ls-Vg_NaWKas8MhahHspz18G...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$aes192Key) } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'A192KW_A192CBC_HS384');
}
### A256KW_A256CBC_HS512
{
  my $token = "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0.91z9VM1VLIA_qyTbqeInFoit7c4PWVuQ5mHcDyNsfofDGXS1qUDdPCWRdLC8ybvJflqHej7SCjEUMxuzOtPOUOgo-8rcdeHi.rsx7FYNTunzditC8XTMJXg.k88BLb0qs8g0UnKjSq9rs2PcrhpafEaUEX2kT-wMdmviZ9UEJrECoQY7MmJgCyQ...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$aes256Key) } or warn $@;
  my $expected = '{"exp":1392553211,"sub":"alice","nbf":1392552611,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"586dd129-a29f-49c8-9de7-454af1155e27","iat":1392552611}';
  is($json, $expected, 'A256KW_A256CBC_HS512');
}
### DIR_A192CBC_HS384
{
  my $token = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0In0..fX42Nn8ABHClA0UfbpkX_g.ClZzxQIzg40GpTETaLejGNhCN0mqSM1BNCIU5NldeF-hGS7_u_5uFsJoWK8BLCoWRtQ3cWIeaHgOa5njCftEK1AoHvechgNCQgme-fuF3f2v5DOphU-tveYzN-uvrUthS0LIrAYrwQW0c0DKcJZ-9vQmC__EzesZgUH...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$aes384Key) } or warn $@;
  my $expected = '{"exp":1392553372,"sub":"alice","nbf":1392552772,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"f81648e9-e9b3-4e37-a655-fcfacace0ef0","iat":1392552772}';
  is($json, $expected, 'DIR_A192CBC_HS384');
}
### DIR_A256CBC_HS512
{
  my $token = "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0..ZD93XtD7TOa2WMbqSuaY9g.1J5BAuxNRMWaw43s7hR82gqLiaZOHBmfD3_B9k4I2VIDKzS9oEF_NS2o7UIBa6t_fWHU7vDm9lNAN4rqq7OvtCBHJpFk31dcruQHxwYKn5xNefG7YP-o6QtpyNioNWJpaSD5VRcRO5ufRrw2bu4_nOth00yJU5jjN3O...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$aes512Key) } or warn $@;
  my $expected = '{"exp":1392553617,"sub":"alice","nbf":1392553017,"aud":["https:\/\/app-one.com","https:\/\/app-two.com"],"iss":"https:\/\/openid.net","jti":"029ea059-b8aa-44eb-a5ad-59458de678f8","iat":1392553017}';
  is($json, $expected, 'DIR_A256CBC_HS512');
}

### RS256 - BAD scalar key
{
  my $token = "eyJhbGciOiJSUzI1NiIsImN0eSI6InRleHRcL3BsYWluIn0.eyJoZWxsbyI6ICJ3b3JsZCJ9.NL_dfVpZkhNn4bZpCyMq5TmnXbT4yiyecuB6Kax_lV8Yq2dG8wLfea-T4UKnrjLOwxlbwLwuKzffWcnWv3LVAWfeBxhGTa0c4_0TX_wzLnsgLuU6s9M2GBkAIuSMHY6UTFumJlEeRBeiqZNrlqvmAzQ9ppJHfWWkW4...
  my $json = eval { decode_jwt(token=>$token, decode_payload=>0, key=>$rsaPub) };
  is($json, undef, 'RS256');
}
### ES256 - BAD scalar key
{

t/jwt_encode_decode.t  view on Meta::CPAN

bqQ5SnARUuC11COhtYuO2K5oxb78jDiApY2m3FnpPWUEPxRYdo+IQVbb4wKBgCZ9
JajJL3phDRDBtXlMNHOtNcDzjKDw+Eik5Zylj05UEumCEmzReVCkrhS8KCWvRwPA
Ynw6w/jH6aNTNRz5p6IpRFlK38DKqnQpDpW4iUISmPAGdekBh+dJA14ZlVWvAUVn
VUFgU1M1l0uZFzGnrJFc3sbU4Mpj3DgIVzfqYezFAoGBALEQD4oCaZfEv77H9c4S
U6xzPe8UcLgdukek5vifLCkT2+6eccTZZjgQRb1plsXbaPHQRJTZcnUmWp9+98gS
8c1vm2YFafgdkSk9Qd1oU2Fv1aOQy4VovOFzJ3CcR+2r7cbRfcpLGnintHtp9yek
02p+d5g4OChfFNDhDtnIqjvY
-----END PRIVATE KEY-----
EOF

my @enclist = (qw/A128GCM A192GCM A256GCM A128CBC-HS256 A192CBC-HS384 A256CBC-HS512/);
my %jwealg = (
    'A128KW'             => '1234567890123456',                 #128 bits/16 bytes
    'A192KW'             => '123456789012345678901234',         #192 bits/24 bytes
    'A256KW'             => '12345678901234567890123456789012', #256 bits/32 bytes
    'A128GCMKW'          => '1234567890123456',                 #128 bits/16 bytes
    'A192GCMKW'          => '123456789012345678901234',         #192 bits/24 bytes
    'A256GCMKW'          => '12345678901234567890123456789012', #256 bits/32 bytes
    'PBES2-HS256+A128KW' => 'any length 1',
    'PBES2-HS384+A192KW' => 'any length 12',
    'PBES2-HS512+A256KW' => 'any length 123',

t/jwt_encode_decode.t  view on Meta::CPAN

    my $token = encode_jwt(key=>$k->[1], payload=>$payload, alg=>$alg, enc=>$enc);
    ok($token, "token: enc=>$enc alg=>$alg");
    my $decoded = decode_jwt(key=>$k->[0], token=>$token);
    is($decoded, 'testik', "decoded: enc=>$enc alg=>$alg");
  }
}

for my $enc (@enclist) {
  my $alg = 'dir';
  my $key_size;
  if ($enc =~ /^A(128|192|256)CBC-HS/) {
    $key_size = 2*$1/8;
  }
  elsif ($enc =~ /^A(128|192|256)GCM/) {
    $key_size = $1/8;
  }
  my $k = 'x' x $key_size;
  my $payload = 'testik';
  my $token = encode_jwt(key=>$k, payload=>$payload, alg=>$alg, enc=>$enc);
  ok($token, "token: enc=>$enc alg=>$alg");
  my $decoded = decode_jwt(key=>$k, token=>$token, alg=>$alg, enc=>$enc);

t/jwt_params.t  view on Meta::CPAN

use Crypt::JWT qw(encode_jwt decode_jwt);
use Crypt::PK::ECC;
use Crypt::PK::RSA;
use Crypt::Misc qw(encode_b64u);
use JSON qw(encode_json);

# key password is 'secret'
my $rsaPriv = <<'EOF';
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,2823DCBA91F7DBA2ED920CAEE40F0BB4

KAADjca5SzbAbdz2cF567ZO9WjZz+lA1C40gsOBvHB6LjWU32YGW6Hz9a7pwUjOh
E/gGSFkKv6pTJgXfLs/l+pIDGSohhzChw7hkmN1IgVXqDQZw3koW5Yn7bg6xeJoI
JFwIIQhnft6BHG2o/5MzUTRwHpIxRuIaz2FnZtBNbVtQInHtP8LJIAVoyoO4c0ET
IQBDj7dwOAPdxOsrKCRkjI8IBMwWtKBq7XunkE15dZFFZrZOfIaXUqNYF9DlCHBk
eGV2lZoL99pOtJzHTBzv3rtyPYqCNotTNnui2Z0Jzcq8K97XAlzKhL7BFMw5TSUF
Tf9ECgumaRELXDdlUtEiZ7uACBXAW+qTUxOCrp+EeyfUBYPLuiy9KQvJd4C+8QIs
OIYekzfqZfhbhOdb0U7ZRN3KXfuNS70vKfoMyuW4UVx75QZt3CnJL8M6dn+eijjw
mEVCT/a8SLgTgMKtl2AzFiJK4WqvnUs9iOswlaAWCIpvrMQmxltoL34aim55EZKd
gDlEW5zCcjYe8A5d5abd4cX8vVrN57j2O3Dk9Dgyr4ZHPjBMF8b6LnWqBGrgFrbQ

t/kw_ecdh.t  view on Meta::CPAN

  my $unw = ecdh_key_unwrap($bob_private, $header->{enc}, $header->{epk}, $header->{apu}, $header->{apv});
  is(unpack("H*", $unw), "56aa8deaf8236d205c2228cd71a7101a", "RFC 7518 Appendix C ECDH-ES apu/apv");

  $unw = eval { ecdh_key_unwrap($bob_private, $header->{enc}, $header->{epk}, "Alice", "Bob") };
  is($unw, undef, "ECDH-ES rejects non-base64url apu/apv");
}

{
  my $header={
    alg => "ECDH-ES",
    enc => "A128CBC-HS256",
    epk => {
             crv => "P-256",
             kty => "EC",
             x   => "-VMKLnMyoHTtFRZF6qW6wdFnA7mJBGb798WqU0UwAXY",
             y   => "hPAcQy83U-5B9uSmqnsWpVsluhdbRdMgnvtpgf5XWN8",
           },
  };
  my $expected_hex='81cbc97bcec94c11f704a10057ecde25d0c2ad56821e15816e98308bafdf8a5c';
  my $unw = ecdh_key_unwrap($kek_private, $header->{enc}, $header->{epk}, $header->{apu}, $header->{apv});
  is(unpack("H*", $unw), $expected_hex, "ECDH-ES + A128CBC-HS256")
}

{
  my $header={
    alg => "ECDH-ES",
    enc => "A128GCM",
    epk => {
             crv => "P-256",
             kty => "EC",
             x   => "Ol7jIi8H1iE1krvQNaPxjy-q-czP0N4EWO3R7584hGU",

t/kw_pbes2.t  view on Meta::CPAN

use Test::More;
use Crypt::KeyWrap qw(pbes2_key_wrap pbes2_key_unwrap);
use Crypt::Misc qw(decode_b64u);

{
  ### PBES2HS256A128KW test vector from https://github.com/Spomky-Labs/jose/blob/master/tests/PBES2_HS_AESKWKeyEncryptionTest.php
  my $header = {
    'alg' => 'PBES2-HS256+A128KW',
    'p2s' => '2WCTcJZ1Rvd_CJuJripQ1w',
    'p2c' => 4096,
    'enc' => 'A128CBC-HS256',
    'cty' => 'jwk+json',
  };
  my $b64u_wcek = 'TrqXOwuNUfDV9VPTNbyGvEJ9JMjefAVn-TR1uIxR9p6hsRQh9Tk7BA';
  my $key = join '', map { chr($_) } (84, 104, 117, 115, 32, 102, 114, 111, 109, 32, 109, 121, 32, 108, 105, 112, 115, 44, 32, 98, 121, 32, 121, 111, 117, 114, 115, 44, 32, 109, 121, 32, 115, 105, 110, 32, 105, 115, 32, 112, 117, 114, 103, 101, 100, ...
  my $cek = join '', map { chr($_) } (111, 27, 25, 52, 66, 29, 20, 78, 92, 176, 56, 240, 65, 208, 82, 112, 161, 131, 36, 55, 202, 236, 185, 172, 129, 23, 153, 194, 195, 48, 253, 182);
  my $wcek = decode_b64u($b64u_wcek);
  my $salt = decode_b64u($header->{p2s});
  my $iter = $header->{p2c};
  
  my $unw = pbes2_key_unwrap($key, $wcek, $header->{alg}, $salt, $iter);

t/rfc3394.t  view on Meta::CPAN

  {
    name => "4.5 Wrap 192 bits of Key Data with a 256-bit KEK",
    kek  => "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
    key  => "00112233445566778899AABBCCDDEEFF0001020304050607",
    ct   => "A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1",
  },
  {
    name => "4.6 Wrap 256 bits of Key Data with a 256-bit KEK",
    kek  => "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
    key  => "00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F",
    ct   => "28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21",
  },
);

for my $t (@tv) {
  my $kek = pack("H*", $t->{kek});
  my $pt  = pack("H*", $t->{key});
  my $exp = lc $t->{ct};

  my $ct = aes_key_wrap($kek, $pt, 'AES', 0);
  is(unpack("H*", $ct), $exp, "wrap: $t->{name}");

t/rfc7516.t  view on Meta::CPAN

            ".5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A".
            ".XFBoMYUZodetZdvTiFvSkQ";

  my ($header, $payload) = decode_jwt(token=>$jwe, key=>$jwk, decode_header=>1, decode_payload=>0);
  is($header->{alg}, "RSA-OAEP", "A.1 header alg");
  is($header->{enc}, "A256GCM",  "A.1 header enc");
  is($payload, $plain_oaep,      "A.1 plaintext recovered");
}

#----------------------------------------------------------------------
# A.2 Example JWE using RSAES-PKCS1-v1_5 and AES_128_CBC_HMAC_SHA_256
#----------------------------------------------------------------------
{
  my $jwk = {
    kty => "RSA",
    n   => "sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1WlUzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDprecbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBIY2EaV7...
    e   => "AQAB",
    d   => "VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-rynq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj-VvXLO...
    p   => "9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEPkrdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM",
    q   => "uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-yBhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0",
    dp  => "w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuvngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcraHawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs",

t/rfc7516.t  view on Meta::CPAN

    qi  => "eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlCtUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZB9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo",
  };
  my $jwe = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0".
            ".UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7e...
            ".AxY8DCtDaGlsbGljb3RoZQ".
            ".KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY".
            ".9hH0vgRfYgPnAHOd8stkvw";

  my ($header, $payload) = decode_jwt(token=>$jwe, key=>$jwk, decode_header=>1, decode_payload=>0);
  is($header->{alg}, "RSA1_5",        "A.2 header alg");
  is($header->{enc}, "A128CBC-HS256", "A.2 header enc");
  is($payload, $plain_pksp,           "A.2 plaintext recovered");
}

#----------------------------------------------------------------------
# A.3 Example JWE Using AES Key Wrap and AES_128_CBC_HMAC_SHA_256
#----------------------------------------------------------------------
{
  my $jwk = { kty => "oct", k => "GawgguFyGrWKav7AX4VKUg" };
  my $jwe = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0".
            ".6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ".
            ".AxY8DCtDaGlsbGljb3RoZQ".
            ".KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY".
            ".U0m_YmjN04DJvceFICbCVQ";

  my ($header, $payload) = decode_jwt(token=>$jwe, key=>$jwk, decode_header=>1, decode_payload=>0);
  is($header->{alg}, "A128KW",        "A.3 header alg");
  is($header->{enc}, "A128CBC-HS256", "A.3 header enc");
  is($payload, $plain_pksp,           "A.3 plaintext recovered");
}

#----------------------------------------------------------------------
# A.5 Flattened JWE JSON Serialization (same payload as A.3)
#----------------------------------------------------------------------
{
  my $jwk = { kty => "oct", k => "GawgguFyGrWKav7AX4VKUg" };
  my $flat = '{"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",'.
             '"unprotected":{"jku":"https://server.example.com/keys.jwks"},'.
             '"header":{"alg":"A128KW","kid":"7"},'.
             '"encrypted_key":"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ",'.
             '"iv":"AxY8DCtDaGlsbGljb3RoZQ",'.
             '"ciphertext":"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",'.
             '"tag":"Mz-VPPyU4RlcuYv1IwIvzw"}';

  my ($header, $payload) = decode_jwt(token=>$flat, key=>$jwk, decode_header=>1, decode_payload=>0);
  is($header->{alg}, "A128KW",                                   "A.5 header alg (per-recipient)");
  is($header->{enc}, "A128CBC-HS256",                            "A.5 header enc (protected)");
  is($header->{jku}, "https://server.example.com/keys.jwks",     "A.5 header jku (shared)");
  is($header->{kid}, "7",                                        "A.5 header kid (per-recipient)");
  is($payload, $plain_pksp,                                      "A.5 plaintext recovered");
}

done_testing;

t/rfc7518.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;

use Crypt::Mode::CBC;
use Crypt::Mac::HMAC qw(hmac);

# Test vectors from RFC 7518 (JSON Web Algorithms) Appendix B and
# Appendix C.
# https://www.rfc-editor.org/rfc/rfc7518.txt
#
# Appendix B specifies AES_CBC_HMAC_SHA2 inputs (K, P, IV, A) and the
# expected outputs (E, M, T) at the primitive level. We exercise
# Crypt::Mode::CBC + Crypt::Mac::HMAC the way RFC 7518 Section 5.2
# prescribes.
#
# Appendix C provides an end-to-end ECDH-ES Concat KDF example whose
# output we compare against ecdh_key_unwrap from Crypt::KeyWrap.

# All Appendix B test cases share the same plaintext, IV and AAD.
# Strip any whitespace from the hex strings so the layout below is
# free to follow the RFC's grouping.
sub _h { my $s = shift; $s =~ s/\s+//g; pack 'H*', $s }

t/rfc7518.t  view on Meta::CPAN

    54 68 65 20 73 65 63 6f 6e 64 20 70 72 69 6e 63
    69 70 6c 65 20 6f 66 20 41 75 67 75 73 74 65 20
    4b 65 72 63 6b 68 6f 66 66 73
};

sub aes_cbc_hmac_sha2_encrypt {
    my ($K, $hash, $tag_len) = @_;
    my $half = length($K) / 2;
    my $MAC_KEY = substr($K, 0, $half);
    my $ENC_KEY = substr($K, $half);
    my $E = Crypt::Mode::CBC->new('AES')->encrypt($P, $ENC_KEY, $IV);
    my $AL = pack('NN', 0, 8 * length($A));
    my $M = hmac($hash, $MAC_KEY, $A . $IV . $E . $AL);
    my $T = substr($M, 0, $tag_len);
    return ($E, $M, $T);
}

#----------------------------------------------------------------------
# B.1 AES_128_CBC_HMAC_SHA_256
#----------------------------------------------------------------------
{
    my $K = _h q{
        00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
        10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
    };
    my $E_exp = _h q{
        c8 0e df a3 2d df 39 d5 ef 00 c0 b4 68 83 42 79
        a2 e4 6a 1b 80 49 f7 92 f7 6b fe 54 b9 03 a9 c9
        a9 4a c9 b4 7a d2 65 5c 5f 10 f9 ae f7 14 27 e2

t/rfc7518.t  view on Meta::CPAN

        bd 34 d8 48 b3 d6 95 50 a6 76 46 34 44 27 ad e5
        4b 88 51 ff b5 98 f7 f8 00 74 b9 47 3c 82 e2 db
    };
    my $M_exp = _h q{
        65 2c 3f a3 6b 0a 7c 5b 32 19 fa b3 a3 0b c1 c4
        e6 e5 45 82 47 65 15 f0 ad 9f 75 a2 b7 1c 73 ef
    };
    my $T_exp = _h q{ 65 2c 3f a3 6b 0a 7c 5b 32 19 fa b3 a3 0b c1 c4 };

    my ($E, $M, $T) = aes_cbc_hmac_sha2_encrypt($K, 'SHA256', 16);
    is(unpack('H*', $E), unpack('H*', $E_exp), "B.1 AES_128_CBC_HMAC_SHA_256 ciphertext E");
    is(unpack('H*', $M), unpack('H*', $M_exp), "B.1 AES_128_CBC_HMAC_SHA_256 HMAC M");
    is(unpack('H*', $T), unpack('H*', $T_exp), "B.1 AES_128_CBC_HMAC_SHA_256 truncated tag T");
}

#----------------------------------------------------------------------
# B.2 AES_192_CBC_HMAC_SHA_384
#----------------------------------------------------------------------
{
    my $K = _h q{
        00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
        10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
        20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
    };
    my $E_exp = _h q{
        ea 65 da 6b 59 e6 1e db 41 9b e6 2d 19 71 2a e5
        d3 03 ee b5 00 52 d0 df d6 69 7f 77 22 4c 8e db

t/rfc7518.t  view on Meta::CPAN

        84 90 ac 0e 58 94 9b fe 51 87 5d 73 3f 93 ac 20
        75 16 80 39 cc c7 33 d7 45 94 f8 86 b3 fa af d4
        86 f2 5c 71 31 e3 28 1e 36 c7 a2 d1 30 af de 57
    };
    my $T_exp = _h q{
        84 90 ac 0e 58 94 9b fe 51 87 5d 73 3f 93 ac 20
        75 16 80 39 cc c7 33 d7
    };

    my ($E, $M, $T) = aes_cbc_hmac_sha2_encrypt($K, 'SHA384', 24);
    is(unpack('H*', $E), unpack('H*', $E_exp), "B.2 AES_192_CBC_HMAC_SHA_384 ciphertext E");
    is(unpack('H*', $M), unpack('H*', $M_exp), "B.2 AES_192_CBC_HMAC_SHA_384 HMAC M");
    is(unpack('H*', $T), unpack('H*', $T_exp), "B.2 AES_192_CBC_HMAC_SHA_384 truncated tag T");
}

#----------------------------------------------------------------------
# B.3 AES_256_CBC_HMAC_SHA_512
#----------------------------------------------------------------------
{
    my $K = _h q{
        00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
        10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
        20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
        30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
    };
    my $E_exp = _h q{
        4a ff aa ad b7 8c 31 c5 da 4b 1b 59 0d 10 ff bd

t/rfc7518.t  view on Meta::CPAN

        2e 62 69 a8 c5 6a 81 6d bc 1b 26 77 61 95 5b c5
        fd 30 a5 65 c6 16 ff b2 f3 64 ba ec e6 8f c4 07
        53 bc fc 02 5d de 36 93 75 4a a1 f5 c3 37 3b 9c
    };
    my $T_exp = _h q{
        4d d3 b4 c0 88 a7 f4 5c 21 68 39 64 5b 20 12 bf
        2e 62 69 a8 c5 6a 81 6d bc 1b 26 77 61 95 5b c5
    };

    my ($E, $M, $T) = aes_cbc_hmac_sha2_encrypt($K, 'SHA512', 32);
    is(unpack('H*', $E), unpack('H*', $E_exp), "B.3 AES_256_CBC_HMAC_SHA_512 ciphertext E");
    is(unpack('H*', $M), unpack('H*', $M_exp), "B.3 AES_256_CBC_HMAC_SHA_512 HMAC M");
    is(unpack('H*', $T), unpack('H*', $T_exp), "B.3 AES_256_CBC_HMAC_SHA_512 truncated tag T");
}

#----------------------------------------------------------------------
# Appendix C: Example ECDH-ES Key Agreement Computation
# Verify the derived key matches the RFC's "VqqN6vgjbSBcIijNcacQGg".
#----------------------------------------------------------------------
{
    use Crypt::PK::ECC;
    use Crypt::KeyWrap qw(ecdh_key_unwrap);
    use Crypt::Misc qw(encode_b64u);

t/rfc7519.t  view on Meta::CPAN

    my $jwt = "eyJhbGciOiJub25lIn0".
              ".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ".
              ".";

    my ($header, $payload) = decode_jwt(token=>$jwt, allow_none=>1, decode_header=>1, verify_exp=>0);
    is($header->{alg}, "none", "Section 6.1 alg=none header");
    is($payload->{iss}, "joe", "Section 6.1 payload iss");
}

#----------------------------------------------------------------------
# Appendix A.1: Example Encrypted JWT (RSA1_5 + A128CBC-HS256).
# Same JWE computation/keys as RFC 7516 Appendix A.2, but the JWE
# Plaintext is the JWT Claims Set from Section 3.1.
#----------------------------------------------------------------------
{
    my $jwt = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0".
              ".QR1Owv2ug2WyPBnbQrRARTeEk9kDO2w8qDcjiHnSJflSdv1iNqhWXaKH4MqAkQtMoNfABIPJaZm0HaA415sv3aeuBWnD8J-Ui7Ah6cWafs3ZwwFKDFUUsWHSK-IPKxLGTkND09XyjORj_CHAgOPJ-Sd8ONQRnJvWn_hXV1BNMHzUjPyYwEsRhDhzjAD26imasOTsgruobpYGoQcXUwFDn7moXPRfDE8-NoQX7N7ZYM...
              ".AxY8DCtDaGlsbGljb3RoZQ".
              ".MKOle7UQrG6nSxTLX6Mqwt0orbHvAKeWnDYvpIAeZ72deHxz3roJDXQyhxx0wKaMHDjUEOKIwrtkHthpqEanSBNYHZgmNOV7sln1Eu9g3J8".
              ".fiK51VwhsxJ-siBMR-YFiA";

    my ($header, $payload) = decode_jwt(token=>$jwt, key=>$rsa_jwk, decode_header=>1, verify_exp=>0);
    is($header->{alg}, "RSA1_5",        "Appendix A.1 header alg");
    is($header->{enc}, "A128CBC-HS256", "Appendix A.1 header enc");
    is($payload->{iss}, "joe",          "Appendix A.1 payload iss");
    is($payload->{exp}, 1300819380,     "Appendix A.1 payload exp");
    ok($payload->{"http://example.com/is_root"}, "Appendix A.1 payload http://example.com/is_root");
}

#----------------------------------------------------------------------
# Appendix A.2: Nested JWT (JWE wrapping a JWS).
# The plaintext of the JWE is the JWS from RFC 7515 Appendix A.2; the
# JWE uses RSA1_5 + A128CBC-HS256 with cty=JWT to mark nesting.
# Crypt::JWT walks the cty=JWT chain when decode_payload defaults
# apply, so the inner JWS payload is what comes back.
#----------------------------------------------------------------------
{
    my $jwt = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiY3R5IjoiSldUIn0".
              ".g_hEwksO1Ax8Qn7HoN-BVeBoa8FXe0kpyk_XdcSmxvcM5_P296JXXtoHISr_DD_MqewaQSH4dZOQHoUgKLeFly-9RI11TG-_Ge1bZFazBPwKC5lJ6OLANLMd0QSL4fYEb9ERe-epKYE3xb2jfY1AltHqBO-PM6j23Guj2yDKnFv6WO72tteVzm_2n17SBFvhDuR9a2nHTE67pe0XGBUS_TK7ecA-iVq5COeVdJR4U4...
              ".UmVkbW9uZCBXQSA5ODA1Mg".
              ".VwHERHPvCNcHHpTjkoigx3_ExK0Qc71RMEParpatm0X_qpg-w8kozSjfNIPPXiTBBLXR65CIPkFqz4l1Ae9w_uowKiwyi9acgVztAi-pSL8GQSXnaamh9kX1mdh3M_TT-FZGQFQsFhu0Z72gJKGdfGE-OE7hS1zuBD5oEUfk0Dmb0VzWEzpxxiSSBbBAzP10l56pPfAtrjEYw-7ygeMkwBl6Z_mLS6w6xUgKlvW6UL...
              ".AVO9iT5AV4CzvDJCdhSFlQ";

    # Decrypt the outer JWE; the plaintext is the inner JWS compact form.
    my ($header, $inner) = decode_jwt(token=>$jwt, key=>$rsa_jwk,
                                      decode_header=>1, decode_payload=>0,
                                      verify_exp=>0);
    is($header->{alg}, "RSA1_5",        "Appendix A.2 outer alg");
    is($header->{enc}, "A128CBC-HS256", "Appendix A.2 outer enc");
    is($header->{cty}, "JWT",           "Appendix A.2 outer cty=JWT (nested)");

    # The inner JWS is the RFC 7515 Appendix A.2 example.
    my $expected_inner = "eyJhbGciOiJSUzI1NiJ9".
        ".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ".
        ".cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqt...
    is($inner, $expected_inner, "Appendix A.2 inner JWS recovered byte-for-byte");

    # The inner JWS is signed with the RSA key from RFC 7515 Appendix A.2
    # (a different key than the one that wraps the JWE above).

t/rfc7520.t  view on Meta::CPAN

    my $flat =
        '{"payload":"'.$sig_payload_b64u.'",'.
        '"protected":"eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9",'.
        '"signature":"s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0"}';
    my ($header, $payload) = decode_jwt(token=>$flat, key=>$jwk_hs256, decode_header=>1, decode_payload=>0);
    is($header->{alg}, "HS256",  "§4.4 flattened JSON header alg");
    is($payload, $sig_payload,   "§4.4 flattened JSON payload");
}

#----------------------------------------------------------------------
# §5.1 RSA1_5 + A128CBC-HS256 — randomised RSA wrap, decode-only
#----------------------------------------------------------------------
my $rfc7520_plaintext =
    "You can trust us to stick with you through thick and ".
    "thin\x{2013}to the bitter end. And you can trust us to ".
    "keep any secret of yours\x{2013}closer than you keep it ".
    "yourself. But you cannot trust us to let you face trouble ".
    "alone, and go off without a word. We are your friends, Frodo.";
utf8::encode($rfc7520_plaintext);

# §5.1 RSA 2048 key (Figure 73)

t/rfc7520.t  view on Meta::CPAN


{
    my $jwe =
        "eyJhbGciOiJSU0ExXzUiLCJraWQiOiJmcm9kby5iYWdnaW5zQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0".
        ".laLxI0j-nLH-_BgLOXMozKxmy9gffy2gTdvqzfTihJBuuzxg0V7yk1WClnQePFvG2K-pvSlWc9BRIazDrn50RcRai__3TDON395H3c62tIouJJ4XaRvYHFjZTZ2GXfz8YAImcc91Tfk0WXC2F5Xbb71ClQ1DDH151tlpH77f2ff7xiSxh9oSewYrcGTSLUeeCt36r1Kt3OSj7EyBQXoZlN7IxbyhMAfgIe7Mv1rOTOI5I8NQ...
        ".bbd5sTkYwhAIqfHsx8DayA".
        ".0fys_TY_na7f8dwSfXLiYdHaA2DxUjD67ieF7fcVbIR62JhJvGZ4_FNVSiGc_raa0HnLQ6s1P2sv3Xzl1p1l_o5wR_RsSzrS8Z-wnI3Jvo0mkpEEnlDmZvDu_k8OWzJv7eZVEqiWKdyVzFhPpiyQU28GLOpRc2VbVbK4dQKPdNTjPPEmRqcaGeTWZVyeSUvf5k59yJZxRuSvWFf6KrNtmRdZ8R4mDOjHSrM_s8uwIFcqt4r5...
        ".kvKuFBXHe5mQr4lqgobAUg";
    my ($header, $payload) = decode_jwt(token=>$jwe, key=>$jwk_rsa_frodo, decode_header=>1, decode_payload=>0);
    is($header->{alg}, "RSA1_5",         "§5.1 header alg");
    is($header->{enc}, "A128CBC-HS256",  "§5.1 header enc");
    is($payload, $rfc7520_plaintext,     "§5.1 plaintext recovered");
}

#----------------------------------------------------------------------
# §5.2 RSA-OAEP + A256GCM — RSA-OAEP is randomised, decode-only
#----------------------------------------------------------------------
my $jwk_rsa_samwise = {
    kty => "RSA",
    kid => "samwise.gamgee\@hobbiton.example",
    use => "enc",

t/rfc7520.t  view on Meta::CPAN

        ".o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6UJuJowOHC5ytjqYgRL-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYxrXfVzIIaRdhYtEMRBvBWbEwP7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lhhNcizPersuhw5f-pGYzseva-TUaL8iWnctc-sSwy7SQmRkfhDjwbz0fz6kFovEgj64X1I5s7E6GLp5fnbYG...
        ".UCGiqJxhBI3IFVdPalHHvA";

    my ($header, $payload) = decode_jwt(token=>$jwe, key=>$jwk_rsa_samwise, decode_header=>1, decode_payload=>0);
    is($header->{alg}, "RSA-OAEP",  "§5.2 header alg");
    is($header->{enc}, "A256GCM",   "§5.2 header enc");
    is($payload, $rfc7520_plaintext, "§5.2 plaintext recovered");
}

#----------------------------------------------------------------------
# §5.3 PBES2-HS512+A256KW + A128CBC-HS256 — decode-only
#----------------------------------------------------------------------
{
    my $password = "entrap_o\x{2013}peter_long\x{2013}credit_tun";
    utf8::encode($password);

    my $jwe =
        "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOiI4UTFTemluYXNSM3hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0".
        ".d3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPWdgtURtmeDV1g".
        ".VBiCzVHNoLiR3F4V82uoTQ".
        ".23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IRsfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6lTF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJb...
        ".0HlwodAhOCILG5SQ2LQ9dg";

    my ($header, $payload) = decode_jwt(token=>$jwe, key=>$password,
                                        decode_header=>1, decode_payload=>0);
    is($header->{alg}, "PBES2-HS512+A256KW", "§5.3 header alg");
    is($header->{enc}, "A128CBC-HS256",      "§5.3 header enc");
    is($header->{p2c}, 8192,                 "§5.3 PBES2 iteration count");
    is($header->{cty}, "jwk-set+json",       "§5.3 cty");
    like($payload, qr/"keys"\s*:/,           "§5.3 plaintext is a JWK Set");
}

#----------------------------------------------------------------------
# §5.4 ECDH-ES+A128KW + A128GCM — decode-only (random ephemeral)
#----------------------------------------------------------------------
my $jwk_ec_p384 = {
    kty => "EC",



( run in 1.138 second using v1.01-cache-2.11-cpan-df04353d9ac )