Crypt-JWT

 view release on metacpan or  search on metacpan

t/rfc7520.t  view on Meta::CPAN

#----------------------------------------------------------------------
# §4.2 RSA-PSS (PS384) Signature — randomised, decode-only
#----------------------------------------------------------------------
{
    my $jws =
        "eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9".
        ".$sig_payload_b64u".
        ".cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2IpN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXUvdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8c...

    my ($header, $payload) = decode_jwt(token=>$jws, key=>$jwk_rsa_bilbo, decode_header=>1, decode_payload=>0);
    is($header->{alg}, "PS384",  "§4.2 header alg");
    is($payload, $sig_payload,   "§4.2 RSA-PSS payload recovered");
}

#----------------------------------------------------------------------
# §4.3 ECDSA P-521 (ES512) Signature — randomised, decode-only
#----------------------------------------------------------------------
{
    my $jws =
        "eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9".
        ".$sig_payload_b64u".
        ".AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvbu9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kvAD890jl8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2";

    my ($header, $payload) = decode_jwt(token=>$jws, key=>$jwk_ec_p521, decode_header=>1, decode_payload=>0);
    is($header->{alg}, "ES512",  "§4.3 header alg");
    is($payload, $sig_payload,   "§4.3 ECDSA payload recovered");
}

#----------------------------------------------------------------------
# §4.4 HMAC-SHA256 (HS256) — deterministic, also test signature byte
#----------------------------------------------------------------------
{
    my $protected_b64u = "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9";
    my $sig_b64u       = "s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0";
    my $jws            = "$protected_b64u.$sig_payload_b64u.$sig_b64u";

    my ($header, $payload) = decode_jwt(token=>$jws, key=>$jwk_hs256, decode_header=>1, decode_payload=>0);
    is($header->{alg}, "HS256", "§4.4 header alg");
    is($payload, $sig_payload,  "§4.4 HMAC payload recovered");

    # Recompute the HMAC over the RFC's exact signing input.
    my $key_raw = decode_b64u($jwk_hs256->{k});
    my $sig = encode_b64u(hmac('SHA256', $key_raw, "$protected_b64u.$sig_payload_b64u"));
    is($sig, $sig_b64u, "§4.4 HMAC signature reproduced byte-for-byte");
}

#----------------------------------------------------------------------
# §4.4 — Flattened JWS JSON Serialization (Figure 36)
#----------------------------------------------------------------------
{
    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)
my $jwk_rsa_frodo = {
    kty => "RSA",
    kid => "frodo.baggins\@hobbiton.example",
    use => "enc",
    n => "maxhbsmBtdQ3CNrKvprUE6n9lYcregDMLYNeTAWcLj8NnPU9XIYegTHVHQjxKDSHP2l-F5jS7sppG1wgdAqZyhnWvXhYNvcM7RfgKxqNx_xAHx6f3yy7s-M9PSNCwPC2lh6UAkR4I00EhV9lrypM9Pi4lBUop9t5fS9W5UNwaAllhrd-osQGPjIeI1deHTwx-ZTHu3C60Pu_LJIl6hKn9wbwaUmA4cR5Bd2pgbaY7ASgsjCU...
    e => "AQAB",
    d => "Kn9tgoHfiTVi8uPu5b9TnwyHwG5dK6RE0uFdlpCGnJN7ZEi963R7wybQ1PLAHmpIbNTztfrheoAniRV1NCIqXaW_qS461xiDTp4ntEPnqcKsyO5jMAji7-CL8vhpYYowNFvIesgMoVaPRYMYT9TW63hNM0aWs7USZ_hLg6Oe1mY0vHTI3FucjSM86Nff4oIENt43r2fspgEPGRrdE6fpLc9Oaq-qeP1GFULimrRdndm-P8q8...
    p => "2DwQmZ43FoTnQ8IkUj3BmKRf5Eh2mizZA5xEJ2MinUE3sdTYKSLtaEoekX9vbBZuWxHdVhM6UnKCJ_2iNk8Z0ayLYHL0_G21aXf9-unynEpUsH7HHTklLpYAzOOx1ZgVljoxAdWNn3hiEFrjZLZGS7lOH-a3QQlDDQoJOJ2VFmU",
    q => "te8LY4-W7IyaqH1ExujjMqkTAlTeRbv0VLQnfLY2xINnrWdwiQ93_VF099aP1ESeLja2nw-6iKIe-qT7mtCPozKfVtUYfz5HrJ_XY2kfexJINb9lhZHMv5p1skZpeIS-GPHCC6gRlKo1q-idn_qxyusfWv7WAxlSVfQfk8d6Et0",
    dp => "UfYKcL_or492vVc0PzwLSplbg4L3-Z5wL48mwiswbpzOyIgd2xHTHQmjJpFAIZ8q-zf9RmgJXkDrFs9rkdxPtAsL1WYdeCT5c125Fkdg317JVRDo1inX7x2Kdh8ERCreW8_4zXItuTl_KiXZNU5lvMQjWbIw2eTx1lpsflo0rYU",
    dq => "iEgcO-QfpepdH8FWd7mUFyrXdnOkXJBCogChY6YKuIHGc_p8Le9MbpFKESzEaLlN1Ehf3B6oGBl5Iz_ayUlZj2IoQZ82znoUrpa9fVYNot87ACfzIG7q9Mv7RiPAderZi03tkVXAdaBau_9vs5rS-7HMtxkVrxSUvJY14TkXlHE",
    qi => "kC-lzZOqoFaZCr5l0tOVtREKoVqaAYhQiqIRGL-MzS4sCmRkxm5vZlXYx6RtE1n_AagjqajlkjieGlxTTThHD8Iga6foGBMaAr5uR1hGQpSc7Gl7CF1DZkBJMTQN6EshYzZfxW08mIO8M6Rzuh0beL6fG9mkDcIyPrBXx2bQ_mM",
};

{
    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",
    alg => "RSA-OAEP",
    n => "wbdxI55VaanZXPY29Lg5hdmv2XhvqAhoxUkanfzf2-5zVUxa6prHRrI4pP1AhoqJRlZfYtWWd5mmHRG2pAHIlh0ySJ9wi0BioZBl1XP2e-C-FyXJGcTy0HdKQWlrfhTm42EW7Vv04r4gfao6uxjLGwfpGrZLarohiWCPnkNrg71S2CuNZSQBIPGjXfkmIy2tl_VWgGnL22GplyXj5YlBLdxXp3XeStsqo571utNfoUTU8E4q...
    e => "AQAB",
    d => "n7fzJc3_WG59VEOBTkayzuSMM780OJQuZjN_KbH8lOZG25ZoA7T4Bxcc0xQn5oZE5uSCIwg91oCt0JvxPcpmqzaJZg1nirjcWZ-oBtVk7gCAWq-B3qhfF3izlbkosrzjHajIcY33HBhsy4_WerrXg4MDNE4HYojy68TcxT2LYQRxUOCf5TtJXvM8olexlSGtVnQnDRutxEUCwiewfmmrfveEogLx9EA-KMgAjTiISXxqIXQh...
    p => "7_2v3OQZzlPFcHyYfLABQ3XP85Es4hCdwCkbDeltaUXgVy9l9etKghvM4hRkOvbb01kYVuLFmxIkCDtpi-zLCYAdXKrAK3PtSbtzld_XZ9nlsYa_QZWpXB_IrtFjVfdKUdMz94pHUhFGFj7nr6NNxfpiHSHWFE1zD_AC3mY46J961Y2LRnreVwAGNw53p07Db8yD_92pDa97vqcZOdgtybH9q6uma-RFNhO1AoiJhYZj69hj...
    q => "zqOHk1P6WN_rHuM7ZF1cXH0x6RuOHq67WuHiSknqQeefGBA9PWs6ZyKQCO-O6mKXtcgE8_Q_hA2kMRcKOcvHil1hqMCNSXlflM7WPRPZu2qCDcqssd_uMbP-DqYthH_EzwL9KnYoH7JQFxxmcv5An8oXUtTwk4knKjkIYGRuUwfQTus0w1NfjFAyxOOiAQ37ussIcE6C6ZSsM3n41UlbJ7TCqewzVJaPJN5cxjySPZPD3Vp0...
    dp => "19oDkBh1AXelMIxQFm2zZTqUhAzCIr4xNIGEPNoDt1jK83_FJA-xnx5kA7-1erdHdms_Ef67HsONNv5A60JaR7w8LHnDiBGnjdaUmmuO8XAxQJ_ia5mxjxNjS6E2yD44USo2JmHvzeeNczq25elqbTPLhUpGo1IZuG72FZQ5gTjXoTXC2-xtCDEUZfaUNh4IeAipfLugbpe0JAFlFfrTDAMUFpC3iXjxqzbEanflwPvj6V9...
    dq => "S6p59KrlmzGzaQYQM3o0XfHCGvfqHLYjCO557HYQf72O9kLMCfd_1VBEqeD-1jjwELKDjck8kOBl5UvohK1oDfSP1DleAy-cnmL29DqWmhgwM1ip0CCNmkmsmDSlqkUXDi6sAaZuntyukyflI-qSQ3C_BafPyFaKrt1fgdyEwYa08pESKwwWisy7KnmoUvaJ3SaHmohFS78TJ25cfc10wZ9hQNOrIChZlkiOdFCtxDqdmCq...
    qi => "FZhClBMywVVjnuUud-05qd5CYU0dK79akAgy9oX6RX6I3IIIPckCciRrokxglZn-omAY5CnCe4KdrnjFOT5YUZE7G_Pg44XgCXaarLQf4hl80oPEf6-jJ5Iy6wPRx7G2e8qLxnh9cOdf-kRqgOS3F48Ucvw3ma5V6KGMwQqWFeV31XtZ8l5cVI-I3NzBS7qltpUVgz2Ju021eyc7IlqgzR98qKONl27DuEES0aK0WE97jns...
};

{
    my $jwe =
        "eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2FtZ2VlQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0".
        ".rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQibeYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyucvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58-Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h...
        ".-nBoKLH0YkLZPSI9".
        ".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",
    kid => "peregrin.took\@tuckborough.example",
    use => "enc",
    crv => "P-384",
    x => "YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQLpe2FpxBmu2",
    y => "A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-SkgaFL1ETP",
    d => "iTx2pk7wW-GqJkHcEkFQb2EFyYcO7RugmaW3mRrQVAOUiPommT0IdnYK2xDlZh-j",
};

{
    my $jwe =
        "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdHVja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NHhBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR...
        ".0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2".
        ".mH-G2zVqgztUtnW_".
        ".tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cPWJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkcY9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_...
        ".WuGzxmcreYjpHGJoa17EBg";

    my ($header, $payload) = decode_jwt(token=>$jwe, key=>$jwk_ec_p384,
                                        decode_header=>1, decode_payload=>0);
    is($header->{alg}, "ECDH-ES+A128KW", "§5.4 header alg");
    is($header->{enc}, "A128GCM",        "§5.4 header enc");
    is($header->{epk}{crv}, "P-384",     "§5.4 epk uses P-384");
    is($payload, $rfc7520_plaintext,     "§5.4 plaintext recovered");
}

#----------------------------------------------------------------------
# §5.8 A128KW + A128GCM — decode (deterministic key wrap, but encode
# can't pin the IV; verify-only suffices)
#----------------------------------------------------------------------
my $jwk_a128kw = {
    kty => "oct",
    kid => "81b20965-8332-43d9-a468-82160ad91ac8",
    use => "enc",
    alg => "A128KW",
    k   => "GZy6sIZ6wl9NJOKB-jnmVQ",
};

{
    my $jwe =
        "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0".
        ".CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx".
        ".Qx0pmsDa8KnJc9Jo".
        ".AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD61A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfeF0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8REwOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0Jq...
        ".ER7MWJZ1FBI_NKvn7Zb1Lw";

    my ($header, $payload) = decode_jwt(token=>$jwe, key=>$jwk_a128kw,
                                        decode_header=>1, decode_payload=>0);
    is($header->{alg}, "A128KW",  "§5.8 header alg");
    is($header->{enc}, "A128GCM", "§5.8 header enc");
    is($payload, $rfc7520_plaintext, "§5.8 plaintext recovered");
}



( run in 0.472 second using v1.01-cache-2.11-cpan-e1769b4cff6 )