jsonwebtoken-8.3.0/.cargo_vcs_info.json0000644000000001360000000000100135540ustar { "git": { "sha1": "8fba79b25459eacc33a80e1ee37ff8eba64079ca" }, "path_in_vcs": "" }jsonwebtoken-8.3.0/CHANGELOG.md000064400000000000000000000074011046102023000141570ustar 00000000000000# Changelog ## 8.3.0 (2023-03-15) - Update base64 - Implement Clone for TokenData if T impls Clone ## 8.2.0 (2022-12-03) - Add DecodingKey::from_jwk - Can now use PEM certificates if you have the `use_pem` feature enabled ## 8.1.1 (2022-06-17) - Fix invalid field name on OctetKeyParameters ## 8.1.0 (2022-04-12) - Make optional fields in the spec really optional - Implements `Hash` for `Header` ## 8.0.1 (2022-02-03) - Fix documentation of leeway ## 8.0.0 (2022-02-02) - Add EdDSA algorithm - `sign`/`verify` now takes a `&[u8]` instead of `&str` to be more flexible - `DecodingKey` now own its data - Remove deprecated `dangerous_unsafe_decode` - `Validation::iss` is now a `HashSet` instead of a single value - `decode` will now error if `Validation::algorithms` is empty - Add JWKs types for easy interop with various Oauth provider, see `examples/auth0.rs` for an example - Removed `decode_*` functions in favour of using the `Validation` struct - Allow float values for `exp` and `nbf`, yes it's in the spec... floats will be rounded and converted to u64 - Error now implements Clone/Eq - Change default leeway from 0s to 60s - Add `Validation::require_spec_claims` to validate presence of the spec claims - Add default feature for pem decoding named `use_pem` that can be disabled to avoid 2 dependencies ## 7.2.0 (2020-06-30) - Add `dangerous_insecure_decode` to replace `dangerous_unsafe_decode`, which is now deprecated - Add `dangerous_insecure_decode_with_validation` ## 7.1.2 (2020-06-16) - Derive `Hash` for `Header` and `Algorithm` ## 7.1.1 (2020-06-09) - Update dependencies ## 7.1.0 (2020-03-01) - Add `into_static` to `DecodingKey` for easier re-use # 7.0.0 (2020-01-28) - Add support for PS256, PS384 and PS512 - Add support for verifying with modulus/exponent components for RSA - Update to 2018 edition - Changed aud field type in Validation to `Option>`. Audience validation now tests for "any-of-these" audience membership. - Add support for keys in PEM format - Add EncodingKey/DecodingKey API to improve performance and UX ## 6.0.1 (2019-05-10) - Fix Algorithm mapping in FromStr for RSA ## 6.0.0 (2019-04-21) - Update Ring to 0.14 - Remove `iat` check to match the JWT spec - Add ES256 and ES384 signing decoding ## 5.0.1 (2018-09-10) - Add implementation of FromStr for Algorithm ## 5.0.0 (2018-08-13) - Update ring - Change error handling to be based on simple struct/enum rather than error-chain - Fix validations not being called properly in some cases - Default validation is not checking `iat` and `nbf` anymore ## 4.0.1 (2018-03-19) - Add method to decode a token without signature verification ## 4.0.0 (2017-11-22) ### Breaking changes - Make it mandatory to specify the algorithm in `decode` ## 3.0.0 (2017-09-08) ### Breaking changes - Remove `validate_signature` from `Validation`, use `decode_header` instead if you don't know the alg used - Make `typ` optional in header, some providers apparently don't use it ### Others - Update ring & error-chain - Fix documentation about `leeway` being in seconds and not milliseconds - Add `decode_header` to only decode the header: replaces the use case of `validate_signature` ## 2.0.3 (2017-07-18) - Make `TokenData` public ## 2.0.2 (2017-06-24) - Update ring & chrono ## 2.0.1 (2017-05-09) - Update ring ## 2.0.0 (2017-04-23) - Use Serde instead of rustc_serialize - Add RSA support - API overhaul, see README for new usage - Add validation - Update all dependencies ## Previous - 1.1.7: update ring - 1.1.6: update ring - 1.1.5: update ring version - 1.1.4: use ring instead of rust-crypto - 1.1.3: Make sign and verify public - 1.1.2: Update rust-crypto to 0.2.35 - 1.1.1: Don't serialize empty fields in header - 1.1.0: Impl Error for jsonwebtoken errors - 1.0: Initial release jsonwebtoken-8.3.0/Cargo.lock0000644000000446030000000000100115360ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi 0.1.19", "libc", "winapi", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ciborium" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" [[package]] name = "ciborium-ll" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" dependencies = [ "ciborium-io", "half", ] [[package]] name = "clap" version = "3.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "bitflags", "clap_lex", "indexmap", "textwrap", ] [[package]] name = "clap_lex" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] [[package]] name = "criterion" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" dependencies = [ "anes", "atty", "cast", "ciborium", "clap", "criterion-plot", "itertools", "lazy_static", "num-traits", "oorandom", "plotters", "rayon", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-channel" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" dependencies = [ "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if", ] [[package]] name = "either" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "half" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "hermit-abi" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", ] [[package]] name = "indexmap" version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] [[package]] name = "jsonwebtoken" version = "8.3.0" dependencies = [ "base64 0.21.0", "criterion", "pem", "ring", "serde", "serde_json", "simple_asn1", "time", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "memoffset" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ "autocfg", ] [[package]] name = "num-bigint" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-integer" version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", "num-traits", ] [[package]] name = "num-traits" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ "hermit-abi 0.2.6", "libc", ] [[package]] name = "once_cell" version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "os_str_bytes" version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] name = "pem" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" dependencies = [ "base64 0.13.1", ] [[package]] name = "plotters" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" [[package]] name = "plotters-svg" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" dependencies = [ "plotters-backend", ] [[package]] name = "proc-macro2" version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", "num_cpus", ] [[package]] name = "regex" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "ring" version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ "cc", "libc", "once_cell", "spin", "untrusted", "web-sys", "winapi", ] [[package]] name = "ryu" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" version = "1.0.156" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.156" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "simple_asn1" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ "num-bigint", "num-traits", "thiserror", "time", ] [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "textwrap" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "time" version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" dependencies = [ "itoa", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" dependencies = [ "time-core", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "unicode-ident" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "walkdir" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", "winapi", "winapi-util", ] [[package]] name = "wasm-bindgen" version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "web-sys" version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" jsonwebtoken-8.3.0/Cargo.toml0000644000000030520000000000100115520ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.56.0" name = "jsonwebtoken" version = "8.3.0" authors = ["Vincent Prouillet "] include = [ "src/**/*", "benches/**/*", "tests/**/*", "LICENSE", "README.md", "CHANGELOG.md", ] description = "Create and decode JWTs in a strongly typed way." homepage = "https://github.com/Keats/jsonwebtoken" readme = "README.md" keywords = [ "jwt", "api", "token", "jwk", ] license = "MIT" repository = "https://github.com/Keats/jsonwebtoken" [[bench]] name = "jwt" harness = false [dependencies.base64] version = "0.21.0" [dependencies.pem] version = "1" optional = true [dependencies.ring] version = "0.16.5" features = ["std"] [dependencies.serde] version = "1.0" features = ["derive"] [dependencies.serde_json] version = "1.0" [dependencies.simple_asn1] version = "0.6" optional = true [dev-dependencies.criterion] version = "0.4" [dev-dependencies.time] version = "0.3" [features] default = ["use_pem"] use_pem = [ "pem", "simple_asn1", ] [badges.maintenance] status = "passively-maintained" jsonwebtoken-8.3.0/Cargo.toml.orig000064400000000000000000000017501046102023000152360ustar 00000000000000[package] name = "jsonwebtoken" version = "8.3.0" authors = ["Vincent Prouillet "] license = "MIT" readme = "README.md" description = "Create and decode JWTs in a strongly typed way." homepage = "https://github.com/Keats/jsonwebtoken" repository = "https://github.com/Keats/jsonwebtoken" keywords = ["jwt", "api", "token", "jwk"] edition = "2021" include = ["src/**/*", "benches/**/*", "tests/**/*", "LICENSE", "README.md", "CHANGELOG.md"] rust-version = "1.56.0" [dependencies] serde_json = "1.0" serde = {version = "1.0", features = ["derive"] } ring = { version = "0.16.5", features = ["std"] } base64 = "0.21.0" # For PEM decoding pem = {version = "1", optional = true} simple_asn1 = {version = "0.6", optional = true} [dev-dependencies] # For the custom time example time = "0.3" criterion = "0.4" [features] default = ["use_pem"] use_pem = ["pem", "simple_asn1"] [[bench]] name = "jwt" harness = false [badges] maintenance = { status = "passively-maintained" } jsonwebtoken-8.3.0/LICENSE000064400000000000000000000020741046102023000133540ustar 00000000000000The MIT License (MIT) Copyright (c) 2015 Vincent Prouillet Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. jsonwebtoken-8.3.0/README.md000064400000000000000000000134131046102023000136250ustar 00000000000000# jsonwebtoken [API documentation on docs.rs](https://docs.rs/jsonwebtoken/) See [JSON Web Tokens](https://en.wikipedia.org/wiki/JSON_Web_Token) for more information on what JSON Web Tokens are. ## Installation Add the following to Cargo.toml: ```toml jsonwebtoken = "8" # If you do not need pem decoding, you can disable the default feature `use_pem` that way: # jsonwebtoken = {version = "8", default-features = false } serde = {version = "1.0", features = ["derive"] } ``` The minimum required Rust version is 1.56. ## Algorithms This library currently supports the following: - HS256 - HS384 - HS512 - RS256 - RS384 - RS512 - PS256 - PS384 - PS512 - ES256 - ES384 - EdDSA ## How to use Complete examples are available in the examples directory: a basic one and one with a custom header. In terms of imports and structs: ```rust use serde::{Serialize, Deserialize}; use jsonwebtoken::{encode, decode, Header, Algorithm, Validation, EncodingKey, DecodingKey}; /// Our claims struct, it needs to derive `Serialize` and/or `Deserialize` #[derive(Debug, Serialize, Deserialize)] struct Claims { sub: String, company: String, exp: usize, } ``` ### Claims The claims fields which can be validated. (see [validation](#validation)) ```rust #[derive(Debug, Serialize, Deserialize)] struct Claims { aud: String, // Optional. Audience exp: usize, // Required (validate_exp defaults to true in validation). Expiration time (as UTC timestamp) iat: usize, // Optional. Issued at (as UTC timestamp) iss: String, // Optional. Issuer nbf: usize, // Optional. Not Before (as UTC timestamp) sub: String, // Optional. Subject (whom token refers to) } ``` ### Header The default algorithm is HS256, which uses a shared secret. ```rust let token = encode(&Header::default(), &my_claims, &EncodingKey::from_secret("secret".as_ref()))?; ``` #### Custom headers & changing algorithm All the parameters from the RFC are supported but the default header only has `typ` and `alg` set. If you want to set the `kid` parameter or change the algorithm for example: ```rust let mut header = Header::new(Algorithm::HS512); header.kid = Some("blabla".to_owned()); let token = encode(&header, &my_claims, &EncodingKey::from_secret("secret".as_ref()))?; ``` Look at `examples/custom_header.rs` for a full working example. ### Encoding ```rust // HS256 let token = encode(&Header::default(), &my_claims, &EncodingKey::from_secret("secret".as_ref()))?; // RSA let token = encode(&Header::new(Algorithm::RS256), &my_claims, &EncodingKey::from_rsa_pem(include_bytes!("privkey.pem"))?)?; ``` Encoding a JWT takes 3 parameters: - a header: the `Header` struct - some claims: your own struct - a key/secret When using HS256, HS2384 or HS512, the key is always a shared secret like in the example above. When using RSA/EC, the key should always be the content of the private key in the PEM or DER format. If your key is in PEM format, it is better performance wise to generate the `EncodingKey` once in a `lazy_static` or something similar and reuse it. ### Decoding ```rust // `token` is a struct with 2 fields: `header` and `claims` where `claims` is your own struct. let token = decode::(&token, &DecodingKey::from_secret("secret".as_ref()), &Validation::default())?; ``` `decode` can error for a variety of reasons: - the token or its signature is invalid - the token had invalid base64 - validation of at least one reserved claim failed As with encoding, when using HS256, HS2384 or HS512, the key is always a shared secret like in the example above. When using RSA/EC, the key should always be the content of the public key in the PEM (or certificate in this case) or DER format. In some cases, for example if you don't know the algorithm used or need to grab the `kid`, you can choose to decode only the header: ```rust let header = decode_header(&token)?; ``` This does not perform any signature verification or validate the token claims. You can also decode a token using the public key components of a RSA key in base64 format. The main use-case is for JWK where your public key is in a JSON format like so: ```json { "kty":"RSA", "e":"AQAB", "kid":"6a7a119f-0876-4f7e-8d0f-bf3ea1391dd8", "n":"yRE6rHuNR0QbHO3H3Kt2pOKGVhQqGZXInOduQNxXzuKlvQTLUTv4l4sggh5_CYYi_cvI-SXVT9kPWSKXxJXBXd_4LkvcPuUakBoAkfh-eiFVMh2VrUyWyj3MFl0HTVF9KwRXLAcwkREiS3npThHRyIxuy0ZMeZfxVL5arMhw1SRELB8HoGfG_AtH89BIE9jDBHZ9dLelK9a184zAf8LwoPLxvJb3Il5nncqPcSfKDDodMFBIMc4lQzDKL5gvmiXLXB1AGLm8KBjfE8s3L5xqi-yUod-j8MtvIj812dkS4QMiRVN_by2h3ZY8LYVGrqZXZTcgn2ujn8uKjXLZVD5TdQ" } ``` ```rust // `token` is a struct with 2 fields: `header` and `claims` where `claims` is your own struct. let token = decode::(&token, &DecodingKey::from_rsa_components(jwk["n"], jwk["e"]), &Validation::new(Algorithm::RS256))?; ``` If your key is in PEM format, it is better performance wise to generate the `DecodingKey` once in a `lazy_static` or something similar and reuse it. ### Convert SEC1 private key to PKCS8 `jsonwebtoken` currently only supports PKCS8 format for private EC keys. If your key has `BEGIN EC PRIVATE KEY` at the top, this is a SEC1 type and can be converted to PKCS8 like so: ```bash openssl pkcs8 -topk8 -nocrypt -in sec1.pem -out pkcs8.pem ``` ## Validation This library validates automatically the `exp` claim and `nbf` is validated if present. You can also validate the `sub`, `iss` and `aud` but those require setting the expected value in the `Validation` struct. Since validating time fields is always a bit tricky due to clock skew, you can add some leeway to the `iat`, `exp` and `nbf` validation by setting the `leeway` field. Last but not least, you will need to set the algorithm(s) allowed for this token if you are not using `HS256`. Look at `examples/validation.rs` for a full working example. jsonwebtoken-8.3.0/benches/jwt.rs000064400000000000000000000023431046102023000151270ustar 00000000000000use criterion::{black_box, criterion_group, criterion_main, Criterion}; use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation}; use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] struct Claims { sub: String, company: String, } fn bench_encode(c: &mut Criterion) { let claim = Claims { sub: "b@b.com".to_owned(), company: "ACME".to_owned() }; let key = EncodingKey::from_secret("secret".as_ref()); c.bench_function("bench_encode", |b| { b.iter(|| encode(black_box(&Header::default()), black_box(&claim), black_box(&key))) }); } fn bench_decode(c: &mut Criterion) { let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"; let key = DecodingKey::from_secret("secret".as_ref()); c.bench_function("bench_decode", |b| { b.iter(|| { decode::( black_box(token), black_box(&key), black_box(&Validation::new(Algorithm::HS256)), ) }) }); } criterion_group!(benches, bench_encode, bench_decode); criterion_main!(benches); jsonwebtoken-8.3.0/src/algorithms.rs000064400000000000000000000056751046102023000156670ustar 00000000000000use crate::errors::{Error, ErrorKind, Result}; use serde::{Deserialize, Serialize}; use std::str::FromStr; #[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)] pub(crate) enum AlgorithmFamily { Hmac, Rsa, Ec, Ed, } /// The algorithms supported for signing/verifying JWTs #[allow(clippy::upper_case_acronyms)] #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)] pub enum Algorithm { /// HMAC using SHA-256 HS256, /// HMAC using SHA-384 HS384, /// HMAC using SHA-512 HS512, /// ECDSA using SHA-256 ES256, /// ECDSA using SHA-384 ES384, /// RSASSA-PKCS1-v1_5 using SHA-256 RS256, /// RSASSA-PKCS1-v1_5 using SHA-384 RS384, /// RSASSA-PKCS1-v1_5 using SHA-512 RS512, /// RSASSA-PSS using SHA-256 PS256, /// RSASSA-PSS using SHA-384 PS384, /// RSASSA-PSS using SHA-512 PS512, /// Edwards-curve Digital Signature Algorithm (EdDSA) EdDSA, } impl Default for Algorithm { fn default() -> Self { Algorithm::HS256 } } impl FromStr for Algorithm { type Err = Error; fn from_str(s: &str) -> Result { match s { "HS256" => Ok(Algorithm::HS256), "HS384" => Ok(Algorithm::HS384), "HS512" => Ok(Algorithm::HS512), "ES256" => Ok(Algorithm::ES256), "ES384" => Ok(Algorithm::ES384), "RS256" => Ok(Algorithm::RS256), "RS384" => Ok(Algorithm::RS384), "PS256" => Ok(Algorithm::PS256), "PS384" => Ok(Algorithm::PS384), "PS512" => Ok(Algorithm::PS512), "RS512" => Ok(Algorithm::RS512), "EdDSA" => Ok(Algorithm::EdDSA), _ => Err(ErrorKind::InvalidAlgorithmName.into()), } } } impl Algorithm { pub(crate) fn family(self) -> AlgorithmFamily { match self { Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => AlgorithmFamily::Hmac, Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 | Algorithm::PS256 | Algorithm::PS384 | Algorithm::PS512 => AlgorithmFamily::Rsa, Algorithm::ES256 | Algorithm::ES384 => AlgorithmFamily::Ec, Algorithm::EdDSA => AlgorithmFamily::Ed, } } } #[cfg(test)] mod tests { use super::*; #[test] fn generate_algorithm_enum_from_str() { assert!(Algorithm::from_str("HS256").is_ok()); assert!(Algorithm::from_str("HS384").is_ok()); assert!(Algorithm::from_str("HS512").is_ok()); assert!(Algorithm::from_str("RS256").is_ok()); assert!(Algorithm::from_str("RS384").is_ok()); assert!(Algorithm::from_str("RS512").is_ok()); assert!(Algorithm::from_str("PS256").is_ok()); assert!(Algorithm::from_str("PS384").is_ok()); assert!(Algorithm::from_str("PS512").is_ok()); assert!(Algorithm::from_str("").is_err()); } } jsonwebtoken-8.3.0/src/crypto/ecdsa.rs000064400000000000000000000026161046102023000161050ustar 00000000000000use ring::{rand, signature}; use crate::algorithms::Algorithm; use crate::errors::Result; use crate::serialization::b64_encode; /// Only used internally when validating EC, to map from our enum to the Ring EcdsaVerificationAlgorithm structs. pub(crate) fn alg_to_ec_verification( alg: Algorithm, ) -> &'static signature::EcdsaVerificationAlgorithm { match alg { Algorithm::ES256 => &signature::ECDSA_P256_SHA256_FIXED, Algorithm::ES384 => &signature::ECDSA_P384_SHA384_FIXED, _ => unreachable!("Tried to get EC alg for a non-EC algorithm"), } } /// Only used internally when signing EC, to map from our enum to the Ring EcdsaVerificationAlgorithm structs. pub(crate) fn alg_to_ec_signing(alg: Algorithm) -> &'static signature::EcdsaSigningAlgorithm { match alg { Algorithm::ES256 => &signature::ECDSA_P256_SHA256_FIXED_SIGNING, Algorithm::ES384 => &signature::ECDSA_P384_SHA384_FIXED_SIGNING, _ => unreachable!("Tried to get EC alg for a non-EC algorithm"), } } /// The actual ECDSA signing + encoding /// The key needs to be in PKCS8 format pub fn sign( alg: &'static signature::EcdsaSigningAlgorithm, key: &[u8], message: &[u8], ) -> Result { let signing_key = signature::EcdsaKeyPair::from_pkcs8(alg, key)?; let rng = rand::SystemRandom::new(); let out = signing_key.sign(&rng, message)?; Ok(b64_encode(out)) } jsonwebtoken-8.3.0/src/crypto/eddsa.rs000064400000000000000000000015751046102023000161110ustar 00000000000000use ring::signature; use crate::algorithms::Algorithm; use crate::errors::Result; use crate::serialization::b64_encode; /// Only used internally when signing or validating EdDSA, to map from our enum to the Ring EdDSAParameters structs. pub(crate) fn alg_to_ec_verification(alg: Algorithm) -> &'static signature::EdDSAParameters { // To support additional key subtypes, like Ed448, we would need to match on the JWK's ("crv") // parameter. match alg { Algorithm::EdDSA => &signature::ED25519, _ => unreachable!("Tried to get EdDSA alg for a non-EdDSA algorithm"), } } /// The actual EdDSA signing + encoding /// The key needs to be in PKCS8 format pub fn sign(key: &[u8], message: &[u8]) -> Result { let signing_key = signature::Ed25519KeyPair::from_pkcs8_maybe_unchecked(key)?; let out = signing_key.sign(message); Ok(b64_encode(out)) } jsonwebtoken-8.3.0/src/crypto/mod.rs000064400000000000000000000072341046102023000156060ustar 00000000000000use ring::constant_time::verify_slices_are_equal; use ring::{hmac, signature}; use crate::algorithms::Algorithm; use crate::decoding::{DecodingKey, DecodingKeyKind}; use crate::encoding::EncodingKey; use crate::errors::Result; use crate::serialization::{b64_decode, b64_encode}; pub(crate) mod ecdsa; pub(crate) mod eddsa; pub(crate) mod rsa; /// The actual HS signing + encoding /// Could be in its own file to match RSA/EC but it's 2 lines... pub(crate) fn sign_hmac(alg: hmac::Algorithm, key: &[u8], message: &[u8]) -> String { let digest = hmac::sign(&hmac::Key::new(alg, key), message); b64_encode(digest) } /// Take the payload of a JWT, sign it using the algorithm given and return /// the base64 url safe encoded of the result. /// /// If you just want to encode a JWT, use `encode` instead. pub fn sign(message: &[u8], key: &EncodingKey, algorithm: Algorithm) -> Result { match algorithm { Algorithm::HS256 => Ok(sign_hmac(hmac::HMAC_SHA256, key.inner(), message)), Algorithm::HS384 => Ok(sign_hmac(hmac::HMAC_SHA384, key.inner(), message)), Algorithm::HS512 => Ok(sign_hmac(hmac::HMAC_SHA512, key.inner(), message)), Algorithm::ES256 | Algorithm::ES384 => { ecdsa::sign(ecdsa::alg_to_ec_signing(algorithm), key.inner(), message) } Algorithm::EdDSA => eddsa::sign(key.inner(), message), Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 | Algorithm::PS256 | Algorithm::PS384 | Algorithm::PS512 => rsa::sign(rsa::alg_to_rsa_signing(algorithm), key.inner(), message), } } /// See Ring docs for more details fn verify_ring( alg: &'static dyn signature::VerificationAlgorithm, signature: &str, message: &[u8], key: &[u8], ) -> Result { let signature_bytes = b64_decode(signature)?; let public_key = signature::UnparsedPublicKey::new(alg, key); let res = public_key.verify(message, &signature_bytes); Ok(res.is_ok()) } /// Compares the signature given with a re-computed signature for HMAC or using the public key /// for RSA/EC. /// /// If you just want to decode a JWT, use `decode` instead. /// /// `signature` is the signature part of a jwt (text after the second '.') /// /// `message` is base64(header) + "." + base64(claims) pub fn verify( signature: &str, message: &[u8], key: &DecodingKey, algorithm: Algorithm, ) -> Result { match algorithm { Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { // we just re-sign the message with the key and compare if they are equal let signed = sign(message, &EncodingKey::from_secret(key.as_bytes()), algorithm)?; Ok(verify_slices_are_equal(signature.as_ref(), signed.as_ref()).is_ok()) } Algorithm::ES256 | Algorithm::ES384 => verify_ring( ecdsa::alg_to_ec_verification(algorithm), signature, message, key.as_bytes(), ), Algorithm::EdDSA => verify_ring( eddsa::alg_to_ec_verification(algorithm), signature, message, key.as_bytes(), ), Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 | Algorithm::PS256 | Algorithm::PS384 | Algorithm::PS512 => { let alg = rsa::alg_to_rsa_parameters(algorithm); match &key.kind { DecodingKeyKind::SecretOrDer(bytes) => verify_ring(alg, signature, message, bytes), DecodingKeyKind::RsaModulusExponent { n, e } => { rsa::verify_from_components(alg, signature, message, (n, e)) } } } } } jsonwebtoken-8.3.0/src/crypto/rsa.rs000064400000000000000000000050521046102023000156100ustar 00000000000000use ring::{rand, signature}; use crate::algorithms::Algorithm; use crate::errors::{ErrorKind, Result}; use crate::serialization::{b64_decode, b64_encode}; /// Only used internally when validating RSA, to map from our enum to the Ring param structs. pub(crate) fn alg_to_rsa_parameters(alg: Algorithm) -> &'static signature::RsaParameters { match alg { Algorithm::RS256 => &signature::RSA_PKCS1_2048_8192_SHA256, Algorithm::RS384 => &signature::RSA_PKCS1_2048_8192_SHA384, Algorithm::RS512 => &signature::RSA_PKCS1_2048_8192_SHA512, Algorithm::PS256 => &signature::RSA_PSS_2048_8192_SHA256, Algorithm::PS384 => &signature::RSA_PSS_2048_8192_SHA384, Algorithm::PS512 => &signature::RSA_PSS_2048_8192_SHA512, _ => unreachable!("Tried to get RSA signature for a non-rsa algorithm"), } } /// Only used internally when signing with RSA, to map from our enum to the Ring signing structs. pub(crate) fn alg_to_rsa_signing(alg: Algorithm) -> &'static dyn signature::RsaEncoding { match alg { Algorithm::RS256 => &signature::RSA_PKCS1_SHA256, Algorithm::RS384 => &signature::RSA_PKCS1_SHA384, Algorithm::RS512 => &signature::RSA_PKCS1_SHA512, Algorithm::PS256 => &signature::RSA_PSS_SHA256, Algorithm::PS384 => &signature::RSA_PSS_SHA384, Algorithm::PS512 => &signature::RSA_PSS_SHA512, _ => unreachable!("Tried to get RSA signature for a non-rsa algorithm"), } } /// The actual RSA signing + encoding /// The key needs to be in PKCS8 format /// Taken from Ring doc https://docs.rs/ring/latest/ring/signature/index.html pub(crate) fn sign( alg: &'static dyn signature::RsaEncoding, key: &[u8], message: &[u8], ) -> Result { let key_pair = signature::RsaKeyPair::from_der(key) .map_err(|e| ErrorKind::InvalidRsaKey(e.description_()))?; let mut signature = vec![0; key_pair.public_modulus_len()]; let rng = rand::SystemRandom::new(); key_pair.sign(alg, &rng, message, &mut signature).map_err(|_| ErrorKind::RsaFailedSigning)?; Ok(b64_encode(signature)) } /// Checks that a signature is valid based on the (n, e) RSA pubkey components pub(crate) fn verify_from_components( alg: &'static signature::RsaParameters, signature: &str, message: &[u8], components: (&[u8], &[u8]), ) -> Result { let signature_bytes = b64_decode(signature)?; let pubkey = signature::RsaPublicKeyComponents { n: components.0, e: components.1 }; let res = pubkey.verify(alg, message, &signature_bytes); Ok(res.is_ok()) } jsonwebtoken-8.3.0/src/decoding.rs000064400000000000000000000226121046102023000152600ustar 00000000000000use base64::{engine::general_purpose::STANDARD, Engine}; use serde::de::DeserializeOwned; use crate::algorithms::AlgorithmFamily; use crate::crypto::verify; use crate::errors::{new_error, ErrorKind, Result}; use crate::header::Header; use crate::jwk::{AlgorithmParameters, Jwk}; #[cfg(feature = "use_pem")] use crate::pem::decoder::PemEncodedKey; use crate::serialization::{b64_decode, DecodedJwtPartClaims}; use crate::validation::{validate, Validation}; /// The return type of a successful call to [decode](fn.decode.html). #[derive(Debug)] pub struct TokenData { /// The decoded JWT header pub header: Header, /// The decoded JWT claims pub claims: T, } impl Clone for TokenData where T: Clone, { fn clone(&self) -> Self { Self { header: self.header.clone(), claims: self.claims.clone() } } } /// Takes the result of a rsplit and ensure we only get 2 parts /// Errors if we don't macro_rules! expect_two { ($iter:expr) => {{ let mut i = $iter; match (i.next(), i.next(), i.next()) { (Some(first), Some(second), None) => (first, second), _ => return Err(new_error(ErrorKind::InvalidToken)), } }}; } #[derive(Clone)] pub(crate) enum DecodingKeyKind { SecretOrDer(Vec), RsaModulusExponent { n: Vec, e: Vec }, } /// All the different kind of keys we can use to decode a JWT. /// This key can be re-used so make sure you only initialize it once if you can for better performance. #[derive(Clone)] pub struct DecodingKey { pub(crate) family: AlgorithmFamily, pub(crate) kind: DecodingKeyKind, } impl DecodingKey { /// If you're using HMAC, use this. pub fn from_secret(secret: &[u8]) -> Self { DecodingKey { family: AlgorithmFamily::Hmac, kind: DecodingKeyKind::SecretOrDer(secret.to_vec()), } } /// If you're using HMAC with a base64 encoded secret, use this. pub fn from_base64_secret(secret: &str) -> Result { let out = STANDARD.decode(secret)?; Ok(DecodingKey { family: AlgorithmFamily::Hmac, kind: DecodingKeyKind::SecretOrDer(out) }) } /// If you are loading a public RSA key in a PEM format, use this. /// Only exists if the feature `use_pem` is enabled. #[cfg(feature = "use_pem")] pub fn from_rsa_pem(key: &[u8]) -> Result { let pem_key = PemEncodedKey::new(key)?; let content = pem_key.as_rsa_key()?; Ok(DecodingKey { family: AlgorithmFamily::Rsa, kind: DecodingKeyKind::SecretOrDer(content.to_vec()), }) } /// If you have (n, e) RSA public key components as strings, use this. pub fn from_rsa_components(modulus: &str, exponent: &str) -> Result { let n = b64_decode(modulus)?; let e = b64_decode(exponent)?; Ok(DecodingKey { family: AlgorithmFamily::Rsa, kind: DecodingKeyKind::RsaModulusExponent { n, e }, }) } /// If you have (n, e) RSA public key components already decoded, use this. pub fn from_rsa_raw_components(modulus: &[u8], exponent: &[u8]) -> Self { DecodingKey { family: AlgorithmFamily::Rsa, kind: DecodingKeyKind::RsaModulusExponent { n: modulus.to_vec(), e: exponent.to_vec() }, } } /// If you have a ECDSA public key in PEM format, use this. /// Only exists if the feature `use_pem` is enabled. #[cfg(feature = "use_pem")] pub fn from_ec_pem(key: &[u8]) -> Result { let pem_key = PemEncodedKey::new(key)?; let content = pem_key.as_ec_public_key()?; Ok(DecodingKey { family: AlgorithmFamily::Ec, kind: DecodingKeyKind::SecretOrDer(content.to_vec()), }) } /// If you have (x,y) ECDSA key components pub fn from_ec_components(x: &str, y: &str) -> Result { let x_cmp = b64_decode(x)?; let y_cmp = b64_decode(y)?; let mut public_key = Vec::with_capacity(1 + x.len() + y.len()); public_key.push(0x04); public_key.extend_from_slice(&x_cmp); public_key.extend_from_slice(&y_cmp); Ok(DecodingKey { family: AlgorithmFamily::Ec, kind: DecodingKeyKind::SecretOrDer(public_key), }) } /// If you have a EdDSA public key in PEM format, use this. /// Only exists if the feature `use_pem` is enabled. #[cfg(feature = "use_pem")] pub fn from_ed_pem(key: &[u8]) -> Result { let pem_key = PemEncodedKey::new(key)?; let content = pem_key.as_ed_public_key()?; Ok(DecodingKey { family: AlgorithmFamily::Ed, kind: DecodingKeyKind::SecretOrDer(content.to_vec()), }) } /// If you know what you're doing and have a RSA DER encoded public key, use this. pub fn from_rsa_der(der: &[u8]) -> Self { DecodingKey { family: AlgorithmFamily::Rsa, kind: DecodingKeyKind::SecretOrDer(der.to_vec()), } } /// If you know what you're doing and have a RSA EC encoded public key, use this. pub fn from_ec_der(der: &[u8]) -> Self { DecodingKey { family: AlgorithmFamily::Ec, kind: DecodingKeyKind::SecretOrDer(der.to_vec()), } } /// If you know what you're doing and have a Ed DER encoded public key, use this. pub fn from_ed_der(der: &[u8]) -> Self { DecodingKey { family: AlgorithmFamily::Ed, kind: DecodingKeyKind::SecretOrDer(der.to_vec()), } } /// From x part (base64 encoded) of the JWK encoding pub fn from_ed_components(x: &str) -> Result { let x_decoded = b64_decode(x)?; Ok(DecodingKey { family: AlgorithmFamily::Ed, kind: DecodingKeyKind::SecretOrDer(x_decoded), }) } /// If you have a key in Jwk format pub fn from_jwk(jwk: &Jwk) -> Result { match &jwk.algorithm { AlgorithmParameters::RSA(params) => { DecodingKey::from_rsa_components(¶ms.n, ¶ms.e) } AlgorithmParameters::EllipticCurve(params) => { DecodingKey::from_ec_components(¶ms.x, ¶ms.y) } AlgorithmParameters::OctetKeyPair(params) => DecodingKey::from_ed_components(¶ms.x), AlgorithmParameters::OctetKey(params) => DecodingKey::from_base64_secret(¶ms.value), } } pub(crate) fn as_bytes(&self) -> &[u8] { match &self.kind { DecodingKeyKind::SecretOrDer(b) => b, DecodingKeyKind::RsaModulusExponent { .. } => unreachable!(), } } } /// Verify signature of a JWT, and return header object and raw payload /// /// If the token or its signature is invalid, it will return an error. fn verify_signature<'a>( token: &'a str, key: &DecodingKey, validation: &Validation, ) -> Result<(Header, &'a str)> { if validation.validate_signature && validation.algorithms.is_empty() { return Err(new_error(ErrorKind::MissingAlgorithm)); } if validation.validate_signature { for alg in &validation.algorithms { if key.family != alg.family() { return Err(new_error(ErrorKind::InvalidAlgorithm)); } } } let (signature, message) = expect_two!(token.rsplitn(2, '.')); let (payload, header) = expect_two!(message.rsplitn(2, '.')); let header = Header::from_encoded(header)?; if validation.validate_signature && !validation.algorithms.contains(&header.alg) { return Err(new_error(ErrorKind::InvalidAlgorithm)); } if validation.validate_signature && !verify(signature, message.as_bytes(), key, header.alg)? { return Err(new_error(ErrorKind::InvalidSignature)); } Ok((header, payload)) } /// Decode and validate a JWT /// /// If the token or its signature is invalid or the claims fail validation, it will return an error. /// /// ```rust /// use serde::{Deserialize, Serialize}; /// use jsonwebtoken::{decode, DecodingKey, Validation, Algorithm}; /// /// #[derive(Debug, Serialize, Deserialize)] /// struct Claims { /// sub: String, /// company: String /// } /// /// let token = "a.jwt.token".to_string(); /// // Claims is a struct that implements Deserialize /// let token_message = decode::(&token, &DecodingKey::from_secret("secret".as_ref()), &Validation::new(Algorithm::HS256)); /// ``` pub fn decode( token: &str, key: &DecodingKey, validation: &Validation, ) -> Result> { match verify_signature(token, key, validation) { Err(e) => Err(e), Ok((header, claims)) => { let decoded_claims = DecodedJwtPartClaims::from_jwt_part_claims(claims)?; let claims = decoded_claims.deserialize()?; validate(decoded_claims.deserialize()?, validation)?; Ok(TokenData { header, claims }) } } } /// Decode a JWT without any signature verification/validations and return its [Header](struct.Header.html). /// /// If the token has an invalid format (ie 3 parts separated by a `.`), it will return an error. /// /// ```rust /// use jsonwebtoken::decode_header; /// /// let token = "a.jwt.token".to_string(); /// let header = decode_header(&token); /// ``` pub fn decode_header(token: &str) -> Result
{ let (_, message) = expect_two!(token.rsplitn(2, '.')); let (_, header) = expect_two!(message.rsplitn(2, '.')); Header::from_encoded(header) } jsonwebtoken-8.3.0/src/encoding.rs000064400000000000000000000114061046102023000152710ustar 00000000000000use base64::{engine::general_purpose::STANDARD, Engine}; use serde::ser::Serialize; use crate::algorithms::AlgorithmFamily; use crate::crypto; use crate::errors::{new_error, ErrorKind, Result}; use crate::header::Header; #[cfg(feature = "use_pem")] use crate::pem::decoder::PemEncodedKey; use crate::serialization::b64_encode_part; /// A key to encode a JWT with. Can be a secret, a PEM-encoded key or a DER-encoded key. /// This key can be re-used so make sure you only initialize it once if you can for better performance. #[derive(Clone)] pub struct EncodingKey { pub(crate) family: AlgorithmFamily, content: Vec, } impl EncodingKey { /// If you're using a HMAC secret that is not base64, use that. pub fn from_secret(secret: &[u8]) -> Self { EncodingKey { family: AlgorithmFamily::Hmac, content: secret.to_vec() } } /// If you have a base64 HMAC secret, use that. pub fn from_base64_secret(secret: &str) -> Result { let out = STANDARD.decode(secret)?; Ok(EncodingKey { family: AlgorithmFamily::Hmac, content: out }) } /// If you are loading a RSA key from a .pem file. /// This errors if the key is not a valid RSA key. /// Only exists if the feature `use_pem` is enabled. /// /// # NOTE /// /// According to the [ring doc](https://docs.rs/ring/latest/ring/signature/struct.RsaKeyPair.html#method.from_pkcs8), /// the key should be at least 2047 bits. /// #[cfg(feature = "use_pem")] pub fn from_rsa_pem(key: &[u8]) -> Result { let pem_key = PemEncodedKey::new(key)?; let content = pem_key.as_rsa_key()?; Ok(EncodingKey { family: AlgorithmFamily::Rsa, content: content.to_vec() }) } /// If you are loading a ECDSA key from a .pem file /// This errors if the key is not a valid private EC key /// Only exists if the feature `use_pem` is enabled. /// /// # NOTE /// /// The key should be in PKCS#8 form. /// /// You can generate a key with the following: /// /// ```sh /// openssl ecparam -genkey -noout -name prime256v1 \ /// | openssl pkcs8 -topk8 -nocrypt -out ec-private.pem /// ``` #[cfg(feature = "use_pem")] pub fn from_ec_pem(key: &[u8]) -> Result { let pem_key = PemEncodedKey::new(key)?; let content = pem_key.as_ec_private_key()?; Ok(EncodingKey { family: AlgorithmFamily::Ec, content: content.to_vec() }) } /// If you are loading a EdDSA key from a .pem file /// This errors if the key is not a valid private Ed key /// Only exists if the feature `use_pem` is enabled. #[cfg(feature = "use_pem")] pub fn from_ed_pem(key: &[u8]) -> Result { let pem_key = PemEncodedKey::new(key)?; let content = pem_key.as_ed_private_key()?; Ok(EncodingKey { family: AlgorithmFamily::Ed, content: content.to_vec() }) } /// If you know what you're doing and have the DER-encoded key, for RSA only pub fn from_rsa_der(der: &[u8]) -> Self { EncodingKey { family: AlgorithmFamily::Rsa, content: der.to_vec() } } /// If you know what you're doing and have the DER-encoded key, for ECDSA pub fn from_ec_der(der: &[u8]) -> Self { EncodingKey { family: AlgorithmFamily::Ec, content: der.to_vec() } } /// If you know what you're doing and have the DER-encoded key, for EdDSA pub fn from_ed_der(der: &[u8]) -> Self { EncodingKey { family: AlgorithmFamily::Ed, content: der.to_vec() } } pub(crate) fn inner(&self) -> &[u8] { &self.content } } /// Encode the header and claims given and sign the payload using the algorithm from the header and the key. /// If the algorithm given is RSA or EC, the key needs to be in the PEM format. /// /// ```rust /// use serde::{Deserialize, Serialize}; /// use jsonwebtoken::{encode, Algorithm, Header, EncodingKey}; /// /// #[derive(Debug, Serialize, Deserialize)] /// struct Claims { /// sub: String, /// company: String /// } /// /// let my_claims = Claims { /// sub: "b@b.com".to_owned(), /// company: "ACME".to_owned() /// }; /// /// // my_claims is a struct that implements Serialize /// // This will create a JWT using HS256 as algorithm /// let token = encode(&Header::default(), &my_claims, &EncodingKey::from_secret("secret".as_ref())).unwrap(); /// ``` pub fn encode(header: &Header, claims: &T, key: &EncodingKey) -> Result { if key.family != header.alg.family() { return Err(new_error(ErrorKind::InvalidAlgorithm)); } let encoded_header = b64_encode_part(header)?; let encoded_claims = b64_encode_part(claims)?; let message = [encoded_header, encoded_claims].join("."); let signature = crypto::sign(message.as_bytes(), key, header.alg)?; Ok([message, signature].join(".")) } jsonwebtoken-8.3.0/src/errors.rs000064400000000000000000000144101046102023000150150ustar 00000000000000use std::error::Error as StdError; use std::fmt; use std::result; use std::sync::Arc; /// A crate private constructor for `Error`. pub(crate) fn new_error(kind: ErrorKind) -> Error { Error(Box::new(kind)) } /// A type alias for `Result`. pub type Result = result::Result; /// An error that can occur when encoding/decoding JWTs #[derive(Clone, Debug, Eq, PartialEq)] pub struct Error(Box); impl Error { /// Return the specific type of this error. pub fn kind(&self) -> &ErrorKind { &self.0 } /// Unwrap this error into its underlying type. pub fn into_kind(self) -> ErrorKind { *self.0 } } /// The specific type of an error. /// /// This enum may grow additional variants, the `#[non_exhaustive]` /// attribute makes sure clients don't count on exhaustive matching. /// (Otherwise, adding a new variant could break existing code.) #[non_exhaustive] #[derive(Clone, Debug)] pub enum ErrorKind { /// When a token doesn't have a valid JWT shape InvalidToken, /// When the signature doesn't match InvalidSignature, /// When the secret given is not a valid ECDSA key InvalidEcdsaKey, /// When the secret given is not a valid RSA key InvalidRsaKey(&'static str), /// We could not sign with the given key RsaFailedSigning, /// When the algorithm from string doesn't match the one passed to `from_str` InvalidAlgorithmName, /// When a key is provided with an invalid format InvalidKeyFormat, // Validation errors /// When a claim required by the validation is not present MissingRequiredClaim(String), /// When a token’s `exp` claim indicates that it has expired ExpiredSignature, /// When a token’s `iss` claim does not match the expected issuer InvalidIssuer, /// When a token’s `aud` claim does not match one of the expected audience values InvalidAudience, /// When a token’s `sub` claim does not match one of the expected subject values InvalidSubject, /// When a token’s `nbf` claim represents a time in the future ImmatureSignature, /// When the algorithm in the header doesn't match the one passed to `decode` or the encoding/decoding key /// used doesn't match the alg requested InvalidAlgorithm, /// When the Validation struct does not contain at least 1 algorithm MissingAlgorithm, // 3rd party errors /// An error happened when decoding some base64 text Base64(base64::DecodeError), /// An error happened while serializing/deserializing JSON Json(Arc), /// Some of the text was invalid UTF-8 Utf8(::std::string::FromUtf8Error), /// Something unspecified went wrong with crypto Crypto(::ring::error::Unspecified), } impl StdError for Error { fn cause(&self) -> Option<&dyn StdError> { match &*self.0 { ErrorKind::InvalidToken => None, ErrorKind::InvalidSignature => None, ErrorKind::InvalidEcdsaKey => None, ErrorKind::RsaFailedSigning => None, ErrorKind::InvalidRsaKey(_) => None, ErrorKind::ExpiredSignature => None, ErrorKind::MissingAlgorithm => None, ErrorKind::MissingRequiredClaim(_) => None, ErrorKind::InvalidIssuer => None, ErrorKind::InvalidAudience => None, ErrorKind::InvalidSubject => None, ErrorKind::ImmatureSignature => None, ErrorKind::InvalidAlgorithm => None, ErrorKind::InvalidAlgorithmName => None, ErrorKind::InvalidKeyFormat => None, ErrorKind::Base64(err) => Some(err), ErrorKind::Json(err) => Some(err.as_ref()), ErrorKind::Utf8(err) => Some(err), ErrorKind::Crypto(err) => Some(err), } } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &*self.0 { ErrorKind::InvalidToken | ErrorKind::InvalidSignature | ErrorKind::InvalidEcdsaKey | ErrorKind::ExpiredSignature | ErrorKind::RsaFailedSigning | ErrorKind::MissingAlgorithm | ErrorKind::InvalidIssuer | ErrorKind::InvalidAudience | ErrorKind::InvalidSubject | ErrorKind::ImmatureSignature | ErrorKind::InvalidAlgorithm | ErrorKind::InvalidKeyFormat | ErrorKind::InvalidAlgorithmName => write!(f, "{:?}", self.0), ErrorKind::MissingRequiredClaim(c) => write!(f, "Missing required claim: {}", c), ErrorKind::InvalidRsaKey(msg) => write!(f, "RSA key invalid: {}", msg), ErrorKind::Json(err) => write!(f, "JSON error: {}", err), ErrorKind::Utf8(err) => write!(f, "UTF-8 error: {}", err), ErrorKind::Crypto(err) => write!(f, "Crypto error: {}", err), ErrorKind::Base64(err) => write!(f, "Base64 error: {}", err), } } } impl PartialEq for ErrorKind { fn eq(&self, other: &Self) -> bool { format!("{:?}", self) == format!("{:?}", other) } } // Equality of ErrorKind is an equivalence relation: it is reflexive, symmetric and transitive. impl Eq for ErrorKind {} impl From for Error { fn from(err: base64::DecodeError) -> Error { new_error(ErrorKind::Base64(err)) } } impl From for Error { fn from(err: serde_json::Error) -> Error { new_error(ErrorKind::Json(Arc::new(err))) } } impl From<::std::string::FromUtf8Error> for Error { fn from(err: ::std::string::FromUtf8Error) -> Error { new_error(ErrorKind::Utf8(err)) } } impl From<::ring::error::Unspecified> for Error { fn from(err: ::ring::error::Unspecified) -> Error { new_error(ErrorKind::Crypto(err)) } } impl From<::ring::error::KeyRejected> for Error { fn from(_err: ::ring::error::KeyRejected) -> Error { new_error(ErrorKind::InvalidEcdsaKey) } } impl From for Error { fn from(kind: ErrorKind) -> Error { new_error(kind) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_error_rendering() { assert_eq!( "InvalidAlgorithmName", Error::from(ErrorKind::InvalidAlgorithmName).to_string() ); } } jsonwebtoken-8.3.0/src/header.rs000064400000000000000000000073661046102023000147450ustar 00000000000000use std::result; use base64::{engine::general_purpose::STANDARD, Engine}; use serde::{Deserialize, Serialize}; use crate::algorithms::Algorithm; use crate::errors::Result; use crate::jwk::Jwk; use crate::serialization::b64_decode; /// A basic JWT header, the alg defaults to HS256 and typ is automatically /// set to `JWT`. All the other fields are optional. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] pub struct Header { /// The type of JWS: it can only be "JWT" here /// /// Defined in [RFC7515#4.1.9](https://tools.ietf.org/html/rfc7515#section-4.1.9). #[serde(skip_serializing_if = "Option::is_none")] pub typ: Option, /// The algorithm used /// /// Defined in [RFC7515#4.1.1](https://tools.ietf.org/html/rfc7515#section-4.1.1). pub alg: Algorithm, /// Content type /// /// Defined in [RFC7519#5.2](https://tools.ietf.org/html/rfc7519#section-5.2). #[serde(skip_serializing_if = "Option::is_none")] pub cty: Option, /// JSON Key URL /// /// Defined in [RFC7515#4.1.2](https://tools.ietf.org/html/rfc7515#section-4.1.2). #[serde(skip_serializing_if = "Option::is_none")] pub jku: Option, /// JSON Web Key /// /// Defined in [RFC7515#4.1.3](https://tools.ietf.org/html/rfc7515#section-4.1.3). #[serde(skip_serializing_if = "Option::is_none")] pub jwk: Option, /// Key ID /// /// Defined in [RFC7515#4.1.4](https://tools.ietf.org/html/rfc7515#section-4.1.4). #[serde(skip_serializing_if = "Option::is_none")] pub kid: Option, /// X.509 URL /// /// Defined in [RFC7515#4.1.5](https://tools.ietf.org/html/rfc7515#section-4.1.5). #[serde(skip_serializing_if = "Option::is_none")] pub x5u: Option, /// X.509 certificate chain. A Vec of base64 encoded ASN.1 DER certificates. /// /// Defined in [RFC7515#4.1.6](https://tools.ietf.org/html/rfc7515#section-4.1.6). #[serde(skip_serializing_if = "Option::is_none")] pub x5c: Option>, /// X.509 SHA1 certificate thumbprint /// /// Defined in [RFC7515#4.1.7](https://tools.ietf.org/html/rfc7515#section-4.1.7). #[serde(skip_serializing_if = "Option::is_none")] pub x5t: Option, /// X.509 SHA256 certificate thumbprint /// /// Defined in [RFC7515#4.1.8](https://tools.ietf.org/html/rfc7515#section-4.1.8). /// /// This will be serialized/deserialized as "x5t#S256", as defined by the RFC. #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "x5t#S256")] pub x5t_s256: Option, } impl Header { /// Returns a JWT header with the algorithm given pub fn new(algorithm: Algorithm) -> Self { Header { typ: Some("JWT".to_string()), alg: algorithm, cty: None, jku: None, jwk: None, kid: None, x5u: None, x5c: None, x5t: None, x5t_s256: None, } } /// Converts an encoded part into the Header struct if possible pub(crate) fn from_encoded>(encoded_part: T) -> Result { let decoded = b64_decode(encoded_part)?; Ok(serde_json::from_slice(&decoded)?) } /// Decodes the X.509 certificate chain into ASN.1 DER format. pub fn x5c_der(&self) -> Result>>> { Ok(self .x5c .as_ref() .map(|b64_certs| { b64_certs.iter().map(|x| STANDARD.decode(x)).collect::>() }) .transpose()?) } } impl Default for Header { /// Returns a JWT header using the default Algorithm, HS256 fn default() -> Self { Header::new(Algorithm::default()) } } jsonwebtoken-8.3.0/src/jwk.rs000064400000000000000000000316301046102023000142770ustar 00000000000000#![allow(missing_docs)] ///! This crate contains types only for working JWK and JWK Sets ///! This is only meant to be used to deal with public JWK, not generate ones. ///! Most of the code in this file is taken from https://github.com/lawliet89/biscuit but /// tweaked to remove the private bits as it's not the goal for this crate currently. ///! use crate::Algorithm; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; /// The intended usage of the public `KeyType`. This enum is serialized `untagged` #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum PublicKeyUse { /// Indicates a public key is meant for signature verification Signature, /// Indicates a public key is meant for encryption Encryption, /// Other usage Other(String), } impl Serialize for PublicKeyUse { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let string = match self { PublicKeyUse::Signature => "sig", PublicKeyUse::Encryption => "enc", PublicKeyUse::Other(other) => other, }; serializer.serialize_str(string) } } impl<'de> Deserialize<'de> for PublicKeyUse { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct PublicKeyUseVisitor; impl<'de> de::Visitor<'de> for PublicKeyUseVisitor { type Value = PublicKeyUse; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { write!(formatter, "a string") } fn visit_str(self, v: &str) -> Result where E: de::Error, { Ok(match v { "sig" => PublicKeyUse::Signature, "enc" => PublicKeyUse::Encryption, other => PublicKeyUse::Other(other.to_string()), }) } } deserializer.deserialize_string(PublicKeyUseVisitor) } } /// Operations that the key is intended to be used for. This enum is serialized `untagged` #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum KeyOperations { /// Computer digital signature or MAC Sign, /// Verify digital signature or MAC Verify, /// Encrypt content Encrypt, /// Decrypt content and validate decryption, if applicable Decrypt, /// Encrypt key WrapKey, /// Decrypt key and validate decryption, if applicable UnwrapKey, /// Derive key DeriveKey, /// Derive bits not to be used as a key DeriveBits, /// Other operation Other(String), } impl Serialize for KeyOperations { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let string = match self { KeyOperations::Sign => "sign", KeyOperations::Verify => "verify", KeyOperations::Encrypt => "encrypt", KeyOperations::Decrypt => "decrypt", KeyOperations::WrapKey => "wrapKey", KeyOperations::UnwrapKey => "unwrapKey", KeyOperations::DeriveKey => "deriveKey", KeyOperations::DeriveBits => "deriveBits", KeyOperations::Other(other) => other, }; serializer.serialize_str(string) } } impl<'de> Deserialize<'de> for KeyOperations { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct KeyOperationsVisitor; impl<'de> de::Visitor<'de> for KeyOperationsVisitor { type Value = KeyOperations; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { write!(formatter, "a string") } fn visit_str(self, v: &str) -> Result where E: de::Error, { Ok(match v { "sign" => KeyOperations::Sign, "verify" => KeyOperations::Verify, "encrypt" => KeyOperations::Encrypt, "decrypt" => KeyOperations::Decrypt, "wrapKey" => KeyOperations::WrapKey, "unwrapKey" => KeyOperations::UnwrapKey, "deriveKey" => KeyOperations::DeriveKey, "deriveBits" => KeyOperations::DeriveBits, other => KeyOperations::Other(other.to_string()), }) } } deserializer.deserialize_string(KeyOperationsVisitor) } } /// Common JWK parameters #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Default, Hash)] pub struct CommonParameters { /// The intended use of the public key. Should not be specified with `key_operations`. /// See sections 4.2 and 4.3 of [RFC7517](https://tools.ietf.org/html/rfc7517). #[serde(rename = "use", skip_serializing_if = "Option::is_none", default)] pub public_key_use: Option, /// The "key_ops" (key operations) parameter identifies the operation(s) /// for which the key is intended to be used. The "key_ops" parameter is /// intended for use cases in which public, private, or symmetric keys /// may be present. /// Should not be specified with `public_key_use`. /// See sections 4.2 and 4.3 of [RFC7517](https://tools.ietf.org/html/rfc7517). #[serde(rename = "key_ops", skip_serializing_if = "Option::is_none", default)] pub key_operations: Option>, /// The algorithm intended for use with the key #[serde(rename = "alg", skip_serializing_if = "Option::is_none", default)] pub algorithm: Option, /// The case sensitive Key ID for the key #[serde(rename = "kid", skip_serializing_if = "Option::is_none", default)] pub key_id: Option, /// X.509 Public key cerfificate URL. This is currently not implemented (correctly). /// /// Serialized to `x5u`. #[serde(rename = "x5u", skip_serializing_if = "Option::is_none")] pub x509_url: Option, /// X.509 public key certificate chain. This is currently not implemented (correctly). /// /// Serialized to `x5c`. #[serde(rename = "x5c", skip_serializing_if = "Option::is_none")] pub x509_chain: Option>, /// X.509 Certificate SHA1 thumbprint. This is currently not implemented (correctly). /// /// Serialized to `x5t`. #[serde(rename = "x5t", skip_serializing_if = "Option::is_none")] pub x509_sha1_fingerprint: Option, /// X.509 Certificate SHA256 thumbprint. This is currently not implemented (correctly). /// /// Serialized to `x5t#S256`. #[serde(rename = "x5t#S256", skip_serializing_if = "Option::is_none")] pub x509_sha256_fingerprint: Option, } /// Key type value for an Elliptic Curve Key. /// This single value enum is a workaround for Rust not supporting associated constants. #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] pub enum EllipticCurveKeyType { /// Key type value for an Elliptic Curve Key. EC, } impl Default for EllipticCurveKeyType { fn default() -> Self { EllipticCurveKeyType::EC } } /// Type of cryptographic curve used by a key. This is defined in /// [RFC 7518 #7.6](https://tools.ietf.org/html/rfc7518#section-7.6) #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] pub enum EllipticCurve { /// P-256 curve #[serde(rename = "P-256")] P256, /// P-384 curve #[serde(rename = "P-384")] P384, /// P-521 curve -- unsupported by `ring`. #[serde(rename = "P-521")] P521, /// Ed25519 curve #[serde(rename = "Ed25519")] Ed25519, } impl Default for EllipticCurve { fn default() -> Self { EllipticCurve::P256 } } /// Parameters for an Elliptic Curve Key #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default, Hash)] pub struct EllipticCurveKeyParameters { /// Key type value for an Elliptic Curve Key. #[serde(rename = "kty")] pub key_type: EllipticCurveKeyType, /// The "crv" (curve) parameter identifies the cryptographic curve used /// with the key. #[serde(rename = "crv")] pub curve: EllipticCurve, /// The "x" (x coordinate) parameter contains the x coordinate for the /// Elliptic Curve point. pub x: String, /// The "y" (y coordinate) parameter contains the y coordinate for the /// Elliptic Curve point. pub y: String, } /// Key type value for an RSA Key. /// This single value enum is a workaround for Rust not supporting associated constants. #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] pub enum RSAKeyType { /// Key type value for an RSA Key. RSA, } impl Default for RSAKeyType { fn default() -> Self { RSAKeyType::RSA } } /// Parameters for a RSA Key #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default, Hash)] pub struct RSAKeyParameters { /// Key type value for a RSA Key #[serde(rename = "kty")] pub key_type: RSAKeyType, /// The "n" (modulus) parameter contains the modulus value for the RSA /// public key. pub n: String, /// The "e" (exponent) parameter contains the exponent value for the RSA /// public key. pub e: String, } /// Key type value for an Octet symmetric key. /// This single value enum is a workaround for Rust not supporting associated constants. #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] pub enum OctetKeyType { /// Key type value for an Octet symmetric key. #[serde(rename = "oct")] Octet, } impl Default for OctetKeyType { fn default() -> Self { OctetKeyType::Octet } } /// Parameters for an Octet Key #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default, Hash)] pub struct OctetKeyParameters { /// Key type value for an Octet Key #[serde(rename = "kty")] pub key_type: OctetKeyType, /// The octet key value #[serde(rename = "k")] pub value: String, } /// Key type value for an Octet Key Pair. /// This single value enum is a workaround for Rust not supporting associated constants. #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] pub enum OctetKeyPairType { /// Key type value for an Octet Key Pair. #[serde(rename = "OKP")] OctetKeyPair, } impl Default for OctetKeyPairType { fn default() -> Self { OctetKeyPairType::OctetKeyPair } } /// Parameters for an Octet Key Pair #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Default, Hash)] pub struct OctetKeyPairParameters { /// Key type value for an Octet Key Pair #[serde(rename = "kty")] pub key_type: OctetKeyPairType, /// The "crv" (curve) parameter identifies the cryptographic curve used /// with the key. #[serde(rename = "crv")] pub curve: EllipticCurve, /// The "x" parameter contains the base64 encoded public key pub x: String, } /// Algorithm specific parameters #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] #[serde(untagged)] pub enum AlgorithmParameters { EllipticCurve(EllipticCurveKeyParameters), RSA(RSAKeyParameters), OctetKey(OctetKeyParameters), OctetKeyPair(OctetKeyPairParameters), } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] pub struct Jwk { #[serde(flatten)] pub common: CommonParameters, /// Key algorithm specific parameters #[serde(flatten)] pub algorithm: AlgorithmParameters, } /// A JWK set #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct JwkSet { pub keys: Vec, } impl JwkSet { /// Find the key in the set that matches the given key id, if any. pub fn find(&self, kid: &str) -> Option<&Jwk> { self.keys .iter() .find(|jwk| jwk.common.key_id.is_some() && jwk.common.key_id.as_ref().unwrap() == kid) } } #[cfg(test)] mod tests { use crate::jwk::{AlgorithmParameters, JwkSet, OctetKeyType}; use crate::serialization::b64_encode; use crate::Algorithm; use serde_json::json; #[test] fn check_hs256() { let key = b64_encode("abcdefghijklmnopqrstuvwxyz012345"); let jwks_json = json!({ "keys": [ { "kty": "oct", "alg": "HS256", "kid": "abc123", "k": key } ] }); let set: JwkSet = serde_json::from_value(jwks_json).expect("Failed HS256 check"); assert_eq!(set.keys.len(), 1); let key = &set.keys[0]; assert_eq!(key.common.key_id, Some("abc123".to_string())); assert_eq!(key.common.algorithm, Some(Algorithm::HS256)); match &key.algorithm { AlgorithmParameters::OctetKey(key) => { assert_eq!(key.key_type, OctetKeyType::Octet); assert_eq!(key.value, key.value) } _ => panic!("Unexpected key algorithm"), } } } jsonwebtoken-8.3.0/src/lib.rs000064400000000000000000000012351046102023000142500ustar 00000000000000//! Create and parses JWT (JSON Web Tokens) //! //! Documentation: [stable](https://docs.rs/jsonwebtoken/) #![deny(missing_docs)] mod algorithms; /// Lower level functions, if you want to do something other than JWTs pub mod crypto; mod decoding; mod encoding; /// All the errors that can be encountered while encoding/decoding JWTs pub mod errors; mod header; pub mod jwk; #[cfg(feature = "use_pem")] mod pem; mod serialization; mod validation; pub use algorithms::Algorithm; pub use decoding::{decode, decode_header, DecodingKey, TokenData}; pub use encoding::{encode, EncodingKey}; pub use header::Header; pub use validation::{get_current_timestamp, Validation}; jsonwebtoken-8.3.0/src/pem/decoder.rs000064400000000000000000000216111046102023000156700ustar 00000000000000use crate::errors::{ErrorKind, Result}; /// Supported PEM files for EC and RSA Public and Private Keys #[derive(Debug, PartialEq)] enum PemType { EcPublic, EcPrivate, RsaPublic, RsaPrivate, EdPublic, EdPrivate, } #[derive(Debug, PartialEq)] enum Standard { // Only for RSA Pkcs1, // RSA/EC Pkcs8, } #[derive(Debug, PartialEq)] enum Classification { Ec, Ed, Rsa, } /// The return type of a successful PEM encoded key with `decode_pem` /// /// This struct gives a way to parse a string to a key for use in jsonwebtoken. /// A struct is necessary as it provides the lifetime of the key /// /// PEM public private keys are encoded PKCS#1 or PKCS#8 /// You will find that with PKCS#8 RSA keys that the PKCS#1 content /// is embedded inside. This is what is provided to ring via `Key::Der` /// For EC keys, they are always PKCS#8 on the outside but like RSA keys /// EC keys contain a section within that ultimately has the configuration /// that ring uses. /// Documentation about these formats is at /// PKCS#1: https://tools.ietf.org/html/rfc8017 /// PKCS#8: https://tools.ietf.org/html/rfc5958 #[derive(Debug)] pub(crate) struct PemEncodedKey { content: Vec, asn1: Vec, pem_type: PemType, standard: Standard, } impl PemEncodedKey { /// Read the PEM file for later key use pub fn new(input: &[u8]) -> Result { match pem::parse(input) { Ok(content) => { let pem_contents = content.contents; let asn1_content = match simple_asn1::from_der(pem_contents.as_slice()) { Ok(asn1) => asn1, Err(_) => return Err(ErrorKind::InvalidKeyFormat.into()), }; match content.tag.as_ref() { // This handles a PKCS#1 RSA Private key "RSA PRIVATE KEY" => Ok(PemEncodedKey { content: pem_contents, asn1: asn1_content, pem_type: PemType::RsaPrivate, standard: Standard::Pkcs1, }), "RSA PUBLIC KEY" => Ok(PemEncodedKey { content: pem_contents, asn1: asn1_content, pem_type: PemType::RsaPublic, standard: Standard::Pkcs1, }), // No "EC PRIVATE KEY" // https://security.stackexchange.com/questions/84327/converting-ecc-private-key-to-pkcs1-format // "there is no such thing as a "PKCS#1 format" for elliptic curve (EC) keys" // This handles PKCS#8 certificates and public & private keys tag @ "PRIVATE KEY" | tag @ "PUBLIC KEY" | tag @ "CERTIFICATE" => { match classify_pem(&asn1_content) { Some(c) => { let is_private = tag == "PRIVATE KEY"; let pem_type = match c { Classification::Ec => { if is_private { PemType::EcPrivate } else { PemType::EcPublic } } Classification::Ed => { if is_private { PemType::EdPrivate } else { PemType::EdPublic } } Classification::Rsa => { if is_private { PemType::RsaPrivate } else { PemType::RsaPublic } } }; Ok(PemEncodedKey { content: pem_contents, asn1: asn1_content, pem_type, standard: Standard::Pkcs8, }) } None => Err(ErrorKind::InvalidKeyFormat.into()), } } // Unknown/unsupported type _ => Err(ErrorKind::InvalidKeyFormat.into()), } } Err(_) => Err(ErrorKind::InvalidKeyFormat.into()), } } /// Can only be PKCS8 pub fn as_ec_private_key(&self) -> Result<&[u8]> { match self.standard { Standard::Pkcs1 => Err(ErrorKind::InvalidKeyFormat.into()), Standard::Pkcs8 => match self.pem_type { PemType::EcPrivate => Ok(self.content.as_slice()), _ => Err(ErrorKind::InvalidKeyFormat.into()), }, } } /// Can only be PKCS8 pub fn as_ec_public_key(&self) -> Result<&[u8]> { match self.standard { Standard::Pkcs1 => Err(ErrorKind::InvalidKeyFormat.into()), Standard::Pkcs8 => match self.pem_type { PemType::EcPublic => extract_first_bitstring(&self.asn1), _ => Err(ErrorKind::InvalidKeyFormat.into()), }, } } /// Can only be PKCS8 pub fn as_ed_private_key(&self) -> Result<&[u8]> { match self.standard { Standard::Pkcs1 => Err(ErrorKind::InvalidKeyFormat.into()), Standard::Pkcs8 => match self.pem_type { PemType::EdPrivate => Ok(self.content.as_slice()), _ => Err(ErrorKind::InvalidKeyFormat.into()), }, } } /// Can only be PKCS8 pub fn as_ed_public_key(&self) -> Result<&[u8]> { match self.standard { Standard::Pkcs1 => Err(ErrorKind::InvalidKeyFormat.into()), Standard::Pkcs8 => match self.pem_type { PemType::EdPublic => extract_first_bitstring(&self.asn1), _ => Err(ErrorKind::InvalidKeyFormat.into()), }, } } /// Can be PKCS1 or PKCS8 pub fn as_rsa_key(&self) -> Result<&[u8]> { match self.standard { Standard::Pkcs1 => Ok(self.content.as_slice()), Standard::Pkcs8 => match self.pem_type { PemType::RsaPrivate => extract_first_bitstring(&self.asn1), PemType::RsaPublic => extract_first_bitstring(&self.asn1), _ => Err(ErrorKind::InvalidKeyFormat.into()), }, } } } // This really just finds and returns the first bitstring or octet string // Which is the x coordinate for EC public keys // And the DER contents of an RSA key // Though PKCS#11 keys shouldn't have anything else. // It will get confusing with certificates. fn extract_first_bitstring(asn1: &[simple_asn1::ASN1Block]) -> Result<&[u8]> { for asn1_entry in asn1.iter() { match asn1_entry { simple_asn1::ASN1Block::Sequence(_, entries) => { if let Ok(result) = extract_first_bitstring(entries) { return Ok(result); } } simple_asn1::ASN1Block::BitString(_, _, value) => { return Ok(value.as_ref()); } simple_asn1::ASN1Block::OctetString(_, value) => { return Ok(value.as_ref()); } _ => (), } } Err(ErrorKind::InvalidEcdsaKey.into()) } /// Find whether this is EC, RSA, or Ed fn classify_pem(asn1: &[simple_asn1::ASN1Block]) -> Option { // These should be constant but the macro requires // #![feature(const_vec_new)] let ec_public_key_oid = simple_asn1::oid!(1, 2, 840, 10_045, 2, 1); let rsa_public_key_oid = simple_asn1::oid!(1, 2, 840, 113_549, 1, 1, 1); let ed25519_oid = simple_asn1::oid!(1, 3, 101, 112); for asn1_entry in asn1.iter() { match asn1_entry { simple_asn1::ASN1Block::Sequence(_, entries) => { if let Some(classification) = classify_pem(entries) { return Some(classification); } } simple_asn1::ASN1Block::ObjectIdentifier(_, oid) => { if oid == ec_public_key_oid { return Some(Classification::Ec); } if oid == rsa_public_key_oid { return Some(Classification::Rsa); } if oid == ed25519_oid { return Some(Classification::Ed); } } _ => {} } } None } jsonwebtoken-8.3.0/src/pem/mod.rs000064400000000000000000000000301046102023000150320ustar 00000000000000pub(crate) mod decoder; jsonwebtoken-8.3.0/src/serialization.rs000064400000000000000000000022071046102023000163570ustar 00000000000000use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use serde::{Deserialize, Serialize}; use crate::errors::Result; pub(crate) fn b64_encode>(input: T) -> String { URL_SAFE_NO_PAD.encode(input) } pub(crate) fn b64_decode>(input: T) -> Result> { URL_SAFE_NO_PAD.decode(input).map_err(|e| e.into()) } /// Serializes a struct to JSON and encodes it in base64 pub(crate) fn b64_encode_part(input: &T) -> Result { let json = serde_json::to_vec(input)?; Ok(b64_encode(json)) } /// This is used to decode from base64 then deserialize from JSON to several structs: /// - The user-provided struct /// - The ClaimsForValidation struct from this crate to run validation on pub(crate) struct DecodedJwtPartClaims { b64_decoded: Vec, } impl DecodedJwtPartClaims { pub fn from_jwt_part_claims(encoded_jwt_part_claims: impl AsRef<[u8]>) -> Result { Ok(Self { b64_decoded: b64_decode(encoded_jwt_part_claims)? }) } pub fn deserialize<'a, T: Deserialize<'a>>(&'a self) -> Result { Ok(serde_json::from_slice(&self.b64_decoded)?) } } jsonwebtoken-8.3.0/src/validation.rs000064400000000000000000000573751046102023000156540ustar 00000000000000use std::borrow::Cow; use std::collections::HashSet; use std::fmt; use std::marker::PhantomData; use std::time::{SystemTime, UNIX_EPOCH}; use serde::de::{self, Visitor}; use serde::{Deserialize, Deserializer}; use crate::algorithms::Algorithm; use crate::errors::{new_error, ErrorKind, Result}; /// Contains the various validations that are applied after decoding a JWT. /// /// All time validation happen on UTC timestamps as seconds. /// /// ```rust /// use jsonwebtoken::{Validation, Algorithm}; /// /// let mut validation = Validation::new(Algorithm::HS256); /// validation.leeway = 5; /// // Setting audience /// validation.set_audience(&["Me"]); // a single string /// validation.set_audience(&["Me", "You"]); // array of strings /// // or issuer /// validation.set_issuer(&["Me"]); // a single string /// validation.set_issuer(&["Me", "You"]); // array of strings /// ``` #[derive(Debug, Clone, PartialEq, Eq)] pub struct Validation { /// Which claims are required to be present before starting the validation. /// This does not interact with the various `validate_*`. If you remove `exp` from that list, you still need /// to set `validate_exp` to `false`. /// The only value that will be used are "exp", "nbf", "aud", "iss", "sub". Anything else will be ignored. /// /// Defaults to `{"exp"}` pub required_spec_claims: HashSet, /// Add some leeway (in seconds) to the `exp` and `nbf` validation to /// account for clock skew. /// /// Defaults to `60`. pub leeway: u64, /// Whether to validate the `exp` field. /// /// It will return an error if the time in the `exp` field is past. /// /// Defaults to `true`. pub validate_exp: bool, /// Whether to validate the `nbf` field. /// /// It will return an error if the current timestamp is before the time in the `nbf` field. /// /// Defaults to `false`. pub validate_nbf: bool, /// If it contains a value, the validation will check that the `aud` field is a member of the /// audience provided and will error otherwise. /// Use `set_audience` to set it /// /// Defaults to `None`. pub aud: Option>, /// If it contains a value, the validation will check that the `iss` field is a member of the /// iss provided and will error otherwise. /// Use `set_issuer` to set it /// /// Defaults to `None`. pub iss: Option>, /// If it contains a value, the validation will check that the `sub` field is the same as the /// one provided and will error otherwise. /// /// Defaults to `None`. pub sub: Option, /// The validation will check that the `alg` of the header is contained /// in the ones provided and will error otherwise. Will error if it is empty. /// /// Defaults to `vec![Algorithm::HS256]`. pub algorithms: Vec, /// Whether to validate the JWT signature. Very insecure to turn that off pub(crate) validate_signature: bool, } impl Validation { /// Create a default validation setup allowing the given alg pub fn new(alg: Algorithm) -> Validation { let mut required_claims = HashSet::with_capacity(1); required_claims.insert("exp".to_owned()); Validation { required_spec_claims: required_claims, algorithms: vec![alg], leeway: 60, validate_exp: true, validate_nbf: false, iss: None, sub: None, aud: None, validate_signature: true, } } /// `aud` is a collection of one or more acceptable audience members /// The simple usage is `set_audience(&["some aud name"])` pub fn set_audience(&mut self, items: &[T]) { self.aud = Some(items.iter().map(|x| x.to_string()).collect()) } /// `iss` is a collection of one or more acceptable issuers members /// The simple usage is `set_issuer(&["some iss name"])` pub fn set_issuer(&mut self, items: &[T]) { self.iss = Some(items.iter().map(|x| x.to_string()).collect()) } /// Which claims are required to be present for this JWT to be considered valid. /// The only values that will be considered are "exp", "nbf", "aud", "iss", "sub". /// The simple usage is `set_required_spec_claims(&["exp", "nbf"])`. /// If you want to have an empty set, do not use this function - set an empty set on the struct /// param directly. pub fn set_required_spec_claims(&mut self, items: &[T]) { self.required_spec_claims = items.iter().map(|x| x.to_string()).collect(); } /// Whether to validate the JWT cryptographic signature. /// Disabling validation is dangerous, only do it if you know what you're doing. /// With validation disabled you should not trust any of the values of the claims. pub fn insecure_disable_signature_validation(&mut self) { self.validate_signature = false; } } impl Default for Validation { fn default() -> Self { Self::new(Algorithm::HS256) } } /// Gets the current timestamp in the format expected by JWTs. pub fn get_current_timestamp() -> u64 { let start = SystemTime::now(); start.duration_since(UNIX_EPOCH).expect("Time went backwards").as_secs() } #[derive(Deserialize)] pub(crate) struct ClaimsForValidation<'a> { #[serde(deserialize_with = "numeric_type", default)] exp: TryParse, #[serde(deserialize_with = "numeric_type", default)] nbf: TryParse, #[serde(borrow)] sub: TryParse>, #[serde(borrow)] iss: TryParse>, #[serde(borrow)] aud: TryParse>, } #[derive(Debug)] enum TryParse { Parsed(T), FailedToParse, NotPresent, } impl<'de, T: Deserialize<'de>> Deserialize<'de> for TryParse { fn deserialize>( deserializer: D, ) -> std::result::Result { Ok(match Option::::deserialize(deserializer) { Ok(Some(value)) => TryParse::Parsed(value), Ok(None) => TryParse::NotPresent, Err(_) => TryParse::FailedToParse, }) } } impl Default for TryParse { fn default() -> Self { Self::NotPresent } } #[derive(Deserialize)] #[serde(untagged)] enum Audience<'a> { Single(#[serde(borrow)] Cow<'a, str>), Multiple(#[serde(borrow)] HashSet>), } #[derive(Deserialize)] #[serde(untagged)] enum Issuer<'a> { Single(#[serde(borrow)] Cow<'a, str>), Multiple(#[serde(borrow)] HashSet>), } /// Usually #[serde(borrow)] on `Cow` enables deserializing with no allocations where /// possible (no escapes in the original str) but it does not work on e.g. `HashSet>` /// We use this struct in this case. #[derive(Deserialize, PartialEq, Eq, Hash)] struct BorrowedCowIfPossible<'a>(#[serde(borrow)] Cow<'a, str>); impl std::borrow::Borrow for BorrowedCowIfPossible<'_> { fn borrow(&self) -> &str { &self.0 } } fn is_subset(reference: &HashSet, given: &HashSet>) -> bool { // Check that intersection is non-empty, favoring iterating on smallest if reference.len() < given.len() { reference.iter().any(|a| given.contains(&**a)) } else { given.iter().any(|a| reference.contains(&*a.0)) } } pub(crate) fn validate(claims: ClaimsForValidation, options: &Validation) -> Result<()> { let now = get_current_timestamp(); for required_claim in &options.required_spec_claims { let present = match required_claim.as_str() { "exp" => matches!(claims.exp, TryParse::Parsed(_)), "sub" => matches!(claims.sub, TryParse::Parsed(_)), "iss" => matches!(claims.iss, TryParse::Parsed(_)), "aud" => matches!(claims.aud, TryParse::Parsed(_)), "nbf" => matches!(claims.nbf, TryParse::Parsed(_)), _ => continue, }; if !present { return Err(new_error(ErrorKind::MissingRequiredClaim(required_claim.clone()))); } } if matches!(claims.exp, TryParse::Parsed(exp) if options.validate_exp && exp < now - options.leeway) { return Err(new_error(ErrorKind::ExpiredSignature)); } if matches!(claims.nbf, TryParse::Parsed(nbf) if options.validate_nbf && nbf > now + options.leeway) { return Err(new_error(ErrorKind::ImmatureSignature)); } if let (TryParse::Parsed(sub), Some(correct_sub)) = (claims.sub, options.sub.as_deref()) { if sub != correct_sub { return Err(new_error(ErrorKind::InvalidSubject)); } } match (claims.iss, options.iss.as_ref()) { (TryParse::Parsed(Issuer::Single(iss)), Some(correct_iss)) => { if !correct_iss.contains(&*iss) { return Err(new_error(ErrorKind::InvalidIssuer)); } } (TryParse::Parsed(Issuer::Multiple(iss)), Some(correct_iss)) => { if !is_subset(correct_iss, &iss) { return Err(new_error(ErrorKind::InvalidIssuer)); } } _ => {} } match (claims.aud, options.aud.as_ref()) { (TryParse::Parsed(Audience::Single(aud)), Some(correct_aud)) => { if !correct_aud.contains(&*aud) { return Err(new_error(ErrorKind::InvalidAudience)); } } (TryParse::Parsed(Audience::Multiple(aud)), Some(correct_aud)) => { if !is_subset(correct_aud, &aud) { return Err(new_error(ErrorKind::InvalidAudience)); } } _ => {} } Ok(()) } fn numeric_type<'de, D>(deserializer: D) -> std::result::Result, D::Error> where D: Deserializer<'de>, { struct NumericType(PhantomData TryParse>); impl<'de> Visitor<'de> for NumericType { type Value = TryParse; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("A NumericType that can be reasonably coerced into a u64") } fn visit_f64(self, value: f64) -> std::result::Result where E: de::Error, { if value.is_finite() && value >= 0.0 && value < (u64::MAX as f64) { Ok(TryParse::Parsed(value.round() as u64)) } else { Err(serde::de::Error::custom("NumericType must be representable as a u64")) } } fn visit_u64(self, value: u64) -> std::result::Result where E: de::Error, { Ok(TryParse::Parsed(value)) } } match deserializer.deserialize_any(NumericType(PhantomData)) { Ok(ok) => Ok(ok), Err(_) => Ok(TryParse::FailedToParse), } } #[cfg(test)] mod tests { use serde_json::json; use super::{get_current_timestamp, validate, ClaimsForValidation, Validation}; use crate::errors::ErrorKind; use crate::Algorithm; use std::collections::HashSet; fn deserialize_claims(claims: &serde_json::Value) -> ClaimsForValidation { serde::Deserialize::deserialize(claims).unwrap() } #[test] fn exp_in_future_ok() { let claims = json!({ "exp": get_current_timestamp() + 10000 }); let res = validate(deserialize_claims(&claims), &Validation::new(Algorithm::HS256)); assert!(res.is_ok()); } #[test] fn exp_float_in_future_ok() { let claims = json!({ "exp": (get_current_timestamp() as f64) + 10000.123 }); let res = validate(deserialize_claims(&claims), &Validation::new(Algorithm::HS256)); assert!(res.is_ok()); } #[test] fn exp_in_past_fails() { let claims = json!({ "exp": get_current_timestamp() - 100000 }); let res = validate(deserialize_claims(&claims), &Validation::new(Algorithm::HS256)); assert!(res.is_err()); match res.unwrap_err().kind() { ErrorKind::ExpiredSignature => (), _ => unreachable!(), }; } #[test] fn exp_float_in_past_fails() { let claims = json!({ "exp": (get_current_timestamp() as f64) - 100000.1234 }); let res = validate(deserialize_claims(&claims), &Validation::new(Algorithm::HS256)); assert!(res.is_err()); match res.unwrap_err().kind() { ErrorKind::ExpiredSignature => (), _ => unreachable!(), }; } #[test] fn exp_in_past_but_in_leeway_ok() { let claims = json!({ "exp": get_current_timestamp() - 500 }); let mut validation = Validation::new(Algorithm::HS256); validation.leeway = 1000 * 60; let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_ok()); } // https://github.com/Keats/jsonwebtoken/issues/51 #[test] fn validate_required_fields_are_present() { for spec_claim in ["exp", "nbf", "aud", "iss", "sub"] { let claims = json!({}); let mut validation = Validation::new(Algorithm::HS256); validation.set_required_spec_claims(&[spec_claim]); let res = validate(deserialize_claims(&claims), &validation).unwrap_err(); assert_eq!(res.kind(), &ErrorKind::MissingRequiredClaim(spec_claim.to_owned())); } } #[test] fn exp_validated_but_not_required_ok() { let claims = json!({}); let mut validation = Validation::new(Algorithm::HS256); validation.required_spec_claims = HashSet::new(); validation.validate_exp = true; let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_ok()); } #[test] fn exp_validated_but_not_required_fails() { let claims = json!({ "exp": (get_current_timestamp() as f64) - 100000.1234 }); let mut validation = Validation::new(Algorithm::HS256); validation.required_spec_claims = HashSet::new(); validation.validate_exp = true; let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_err()); } #[test] fn exp_required_but_not_validated_ok() { let claims = json!({ "exp": (get_current_timestamp() as f64) - 100000.1234 }); let mut validation = Validation::new(Algorithm::HS256); validation.set_required_spec_claims(&["exp"]); validation.validate_exp = false; let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_ok()); } #[test] fn exp_required_but_not_validated_fails() { let claims = json!({}); let mut validation = Validation::new(Algorithm::HS256); validation.set_required_spec_claims(&["exp"]); validation.validate_exp = false; let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_err()); } #[test] fn nbf_in_past_ok() { let claims = json!({ "nbf": get_current_timestamp() - 10000 }); let mut validation = Validation::new(Algorithm::HS256); validation.required_spec_claims = HashSet::new(); validation.validate_exp = false; validation.validate_nbf = true; let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_ok()); } #[test] fn nbf_float_in_past_ok() { let claims = json!({ "nbf": (get_current_timestamp() as f64) - 10000.1234 }); let mut validation = Validation::new(Algorithm::HS256); validation.required_spec_claims = HashSet::new(); validation.validate_exp = false; validation.validate_nbf = true; let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_ok()); } #[test] fn nbf_in_future_fails() { let claims = json!({ "nbf": get_current_timestamp() + 100000 }); let mut validation = Validation::new(Algorithm::HS256); validation.required_spec_claims = HashSet::new(); validation.validate_exp = false; validation.validate_nbf = true; let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_err()); match res.unwrap_err().kind() { ErrorKind::ImmatureSignature => (), _ => unreachable!(), }; } #[test] fn nbf_in_future_but_in_leeway_ok() { let claims = json!({ "nbf": get_current_timestamp() + 500 }); let mut validation = Validation::new(Algorithm::HS256); validation.required_spec_claims = HashSet::new(); validation.validate_exp = false; validation.validate_nbf = true; validation.leeway = 1000 * 60; let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_ok()); } #[test] fn iss_string_ok() { let claims = json!({"iss": ["Keats"]}); let mut validation = Validation::new(Algorithm::HS256); validation.required_spec_claims = HashSet::new(); validation.validate_exp = false; validation.set_issuer(&["Keats"]); let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_ok()); } #[test] fn iss_array_of_string_ok() { let claims = json!({"iss": ["UserA", "UserB"]}); let mut validation = Validation::new(Algorithm::HS256); validation.required_spec_claims = HashSet::new(); validation.validate_exp = false; validation.set_issuer(&["UserA", "UserB"]); let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_ok()); } #[test] fn iss_not_matching_fails() { let claims = json!({"iss": "Hacked"}); let mut validation = Validation::new(Algorithm::HS256); validation.required_spec_claims = HashSet::new(); validation.validate_exp = false; validation.set_issuer(&["Keats"]); let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_err()); match res.unwrap_err().kind() { ErrorKind::InvalidIssuer => (), _ => unreachable!(), }; } #[test] fn iss_missing_fails() { let claims = json!({}); let mut validation = Validation::new(Algorithm::HS256); validation.set_required_spec_claims(&["iss"]); validation.validate_exp = false; validation.set_issuer(&["Keats"]); let res = validate(deserialize_claims(&claims), &validation); match res.unwrap_err().kind() { ErrorKind::MissingRequiredClaim(claim) => assert_eq!(claim, "iss"), _ => unreachable!(), }; } #[test] fn sub_ok() { let claims = json!({"sub": "Keats"}); let mut validation = Validation::new(Algorithm::HS256); validation.required_spec_claims = HashSet::new(); validation.validate_exp = false; validation.sub = Some("Keats".to_owned()); let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_ok()); } #[test] fn sub_not_matching_fails() { let claims = json!({"sub": "Hacked"}); let mut validation = Validation::new(Algorithm::HS256); validation.required_spec_claims = HashSet::new(); validation.validate_exp = false; validation.sub = Some("Keats".to_owned()); let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_err()); match res.unwrap_err().kind() { ErrorKind::InvalidSubject => (), _ => unreachable!(), }; } #[test] fn sub_missing_fails() { let claims = json!({}); let mut validation = Validation::new(Algorithm::HS256); validation.validate_exp = false; validation.set_required_spec_claims(&["sub"]); validation.sub = Some("Keats".to_owned()); let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_err()); match res.unwrap_err().kind() { ErrorKind::MissingRequiredClaim(claim) => assert_eq!(claim, "sub"), _ => unreachable!(), }; } #[test] fn aud_string_ok() { let claims = json!({"aud": "Everyone"}); let mut validation = Validation::new(Algorithm::HS256); validation.validate_exp = false; validation.required_spec_claims = HashSet::new(); validation.set_audience(&["Everyone"]); let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_ok()); } #[test] fn aud_array_of_string_ok() { let claims = json!({"aud": ["UserA", "UserB"]}); let mut validation = Validation::new(Algorithm::HS256); validation.validate_exp = false; validation.required_spec_claims = HashSet::new(); validation.set_audience(&["UserA", "UserB"]); let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_ok()); } #[test] fn aud_type_mismatch_fails() { let claims = json!({"aud": ["Everyone"]}); let mut validation = Validation::new(Algorithm::HS256); validation.validate_exp = false; validation.required_spec_claims = HashSet::new(); validation.set_audience(&["UserA", "UserB"]); let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_err()); match res.unwrap_err().kind() { ErrorKind::InvalidAudience => (), _ => unreachable!(), }; } #[test] fn aud_correct_type_not_matching_fails() { let claims = json!({"aud": ["Everyone"]}); let mut validation = Validation::new(Algorithm::HS256); validation.validate_exp = false; validation.required_spec_claims = HashSet::new(); validation.set_audience(&["None"]); let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_err()); match res.unwrap_err().kind() { ErrorKind::InvalidAudience => (), _ => unreachable!(), }; } #[test] fn aud_missing_fails() { let claims = json!({}); let mut validation = Validation::new(Algorithm::HS256); validation.validate_exp = false; validation.set_required_spec_claims(&["aud"]); validation.set_audience(&["None"]); let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_err()); match res.unwrap_err().kind() { ErrorKind::MissingRequiredClaim(claim) => assert_eq!(claim, "aud"), _ => unreachable!(), }; } // https://github.com/Keats/jsonwebtoken/issues/51 #[test] fn does_validation_in_right_order() { let claims = json!({ "exp": get_current_timestamp() + 10000 }); let mut validation = Validation::new(Algorithm::HS256); validation.set_required_spec_claims(&["exp", "iss"]); validation.leeway = 5; validation.set_issuer(&["iss no check"]); validation.set_audience(&["iss no check"]); let res = validate(deserialize_claims(&claims), &validation); // It errors because it needs to validate iss/sub which are missing assert!(res.is_err()); match res.unwrap_err().kind() { ErrorKind::MissingRequiredClaim(claim) => assert_eq!(claim, "iss"), t => panic!("{:?}", t), }; } // https://github.com/Keats/jsonwebtoken/issues/110 #[test] fn aud_use_validation_struct() { let claims = json!({"aud": "my-googleclientid1234.apps.googleusercontent.com"}); let aud = "my-googleclientid1234.apps.googleusercontent.com".to_string(); let mut aud_hashset = std::collections::HashSet::new(); aud_hashset.insert(aud); let mut validation = Validation::new(Algorithm::HS256); validation.validate_exp = false; validation.required_spec_claims = HashSet::new(); validation.set_audience(&["my-googleclientid1234.apps.googleusercontent.com"]); let res = validate(deserialize_claims(&claims), &validation); assert!(res.is_ok()); } } jsonwebtoken-8.3.0/tests/ecdsa/mod.rs000064400000000000000000000113221046102023000157110ustar 00000000000000use jsonwebtoken::{ crypto::{sign, verify}, Algorithm, DecodingKey, EncodingKey, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "use_pem")] use jsonwebtoken::{decode, encode, Header, Validation}; #[cfg(feature = "use_pem")] use time::OffsetDateTime; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Claims { sub: String, company: String, exp: i64, } #[test] fn round_trip_sign_verification_pk8() { let privkey = include_bytes!("private_ecdsa_key.pk8"); let pubkey = include_bytes!("public_ecdsa_key.pk8"); let encrypted = sign(b"hello world", &EncodingKey::from_ec_der(privkey), Algorithm::ES256).unwrap(); let is_valid = verify(&encrypted, b"hello world", &DecodingKey::from_ec_der(pubkey), Algorithm::ES256) .unwrap(); assert!(is_valid); } #[cfg(feature = "use_pem")] #[test] fn round_trip_sign_verification_pem() { let privkey_pem = include_bytes!("private_ecdsa_key.pem"); let pubkey_pem = include_bytes!("public_ecdsa_key.pem"); let encrypted = sign(b"hello world", &EncodingKey::from_ec_pem(privkey_pem).unwrap(), Algorithm::ES256) .unwrap(); let is_valid = verify( &encrypted, b"hello world", &DecodingKey::from_ec_pem(pubkey_pem).unwrap(), Algorithm::ES256, ) .unwrap(); assert!(is_valid); } #[cfg(feature = "use_pem")] #[test] fn round_trip_claim() { let privkey_pem = include_bytes!("private_ecdsa_key.pem"); let pubkey_pem = include_bytes!("public_ecdsa_key.pem"); let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let token = encode( &Header::new(Algorithm::ES256), &my_claims, &EncodingKey::from_ec_pem(privkey_pem).unwrap(), ) .unwrap(); let token_data = decode::( &token, &DecodingKey::from_ec_pem(pubkey_pem).unwrap(), &Validation::new(Algorithm::ES256), ) .unwrap(); assert_eq!(my_claims, token_data.claims); } #[cfg(feature = "use_pem")] #[test] fn ec_x_y() { let privkey = include_str!("private_ecdsa_key.pem"); let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let x = "w7JAoU_gJbZJvV-zCOvU9yFJq0FNC_edCMRM78P8eQQ"; let y = "wQg1EytcsEmGrM70Gb53oluoDbVhCZ3Uq3hHMslHVb4"; let encrypted = encode( &Header::new(Algorithm::ES256), &my_claims, &EncodingKey::from_ec_pem(privkey.as_ref()).unwrap(), ) .unwrap(); let res = decode::( &encrypted, &DecodingKey::from_ec_components(x, y).unwrap(), &Validation::new(Algorithm::ES256), ); assert!(res.is_ok()); } #[cfg(feature = "use_pem")] #[test] fn ed_jwk() { use jsonwebtoken::jwk::Jwk; use serde_json::json; let privkey = include_str!("private_ecdsa_key.pem"); let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let jwk: Jwk = serde_json::from_value(json!({ "kty": "EC", "crv": "P-256", "x": "w7JAoU_gJbZJvV-zCOvU9yFJq0FNC_edCMRM78P8eQQ", "y": "wQg1EytcsEmGrM70Gb53oluoDbVhCZ3Uq3hHMslHVb4", "kid": "ec01", "alg": "ES256", "use": "sig" })) .unwrap(); let encrypted = encode( &Header::new(Algorithm::ES256), &my_claims, &EncodingKey::from_ec_pem(privkey.as_ref()).unwrap(), ) .unwrap(); let res = decode::( &encrypted, &DecodingKey::from_jwk(&jwk).unwrap(), &Validation::new(Algorithm::ES256), ); assert!(res.is_ok()); } // https://jwt.io/ is often used for examples so ensure their example works with jsonwebtoken #[cfg(feature = "use_pem")] #[test] fn roundtrip_with_jwtio_example() { // We currently do not support SEC1 so we use the converted PKCS8 formatted let privkey_pem = include_bytes!("private_jwtio_pkcs8.pem"); let pubkey_pem = include_bytes!("public_jwtio.pem"); let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let token = encode( &Header::new(Algorithm::ES384), &my_claims, &EncodingKey::from_ec_pem(privkey_pem).unwrap(), ) .unwrap(); let token_data = decode::( &token, &DecodingKey::from_ec_pem(pubkey_pem).unwrap(), &Validation::new(Algorithm::ES384), ) .unwrap(); assert_eq!(my_claims, token_data.claims); } jsonwebtoken-8.3.0/tests/ecdsa/private_ecdsa_key.pem000064400000000000000000000003611046102023000207510ustar 00000000000000-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWTFfCGljY6aw3Hrt kHmPRiazukxPLb6ilpRAewjW8nihRANCAATDskChT+Altkm9X7MI69T3IUmrQU0L 950IxEzvw/x5BMEINRMrXLBJhqzO9Bm+d6JbqA21YQmd1Kt4RzLJR1W+ -----END PRIVATE KEY----- jsonwebtoken-8.3.0/tests/ecdsa/private_ecdsa_key.pk8000064400000000000000000000002121046102023000206650ustar 0000000000000000*H=*H=m0k Y1_icczyF&LO-@{xDBò@O%I_!IAM Ly5+\Iw[ a ԫxG2GUjsonwebtoken-8.3.0/tests/ecdsa/private_jwtio.pem000064400000000000000000000004401046102023000201540ustar 00000000000000-----BEGIN EC PRIVATE KEY----- MIGkAgEBBDCAHpFQ62QnGCEvYh/pE9QmR1C9aLcDItRbslbmhen/h1tt8AyMhske enT+rAyyPhGgBwYFK4EEACKhZANiAAQLW5ZJePZzMIPAxMtZXkEWbDF0zo9f2n4+ T1h/2sh/fviblc/VTyrv10GEtIi5qiOy85Pf1RRw8lE5IPUWpgu553SteKigiKLU PeNpbqmYZUkWGh3MLfVzLmx85ii2vMU= -----END EC PRIVATE KEY----- jsonwebtoken-8.3.0/tests/ecdsa/private_jwtio_pkcs8.pem000064400000000000000000000004621046102023000212700ustar 00000000000000-----BEGIN PRIVATE KEY----- MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCAHpFQ62QnGCEvYh/p E9QmR1C9aLcDItRbslbmhen/h1tt8AyMhskeenT+rAyyPhGhZANiAAQLW5ZJePZz MIPAxMtZXkEWbDF0zo9f2n4+T1h/2sh/fviblc/VTyrv10GEtIi5qiOy85Pf1RRw 8lE5IPUWpgu553SteKigiKLUPeNpbqmYZUkWGh3MLfVzLmx85ii2vMU= -----END PRIVATE KEY----- jsonwebtoken-8.3.0/tests/ecdsa/public_ecdsa_key.pem000064400000000000000000000002621046102023000205550ustar 00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEw7JAoU/gJbZJvV+zCOvU9yFJq0FN C/edCMRM78P8eQTBCDUTK1ywSYaszvQZvneiW6gNtWEJndSreEcyyUdVvg== -----END PUBLIC KEY----- jsonwebtoken-8.3.0/tests/ecdsa/public_ecdsa_key.pk8000064400000000000000000000001011046102023000204660ustar 00000000000000ò@O%I_!IAM Ly5+\Iw[ a ԫxG2GUjsonwebtoken-8.3.0/tests/ecdsa/public_jwtio.pem000064400000000000000000000003271046102023000177640ustar 00000000000000-----BEGIN PUBLIC KEY----- MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEC1uWSXj2czCDwMTLWV5BFmwxdM6PX9p+ Pk9Yf9rIf374m5XP1U8q79dBhLSIuaojsvOT39UUcPJROSD1FqYLued0rXiooIii 1D3jaW6pmGVJFhodzC31cy5sfOYotrzF -----END PUBLIC KEY----- jsonwebtoken-8.3.0/tests/eddsa/mod.rs000064400000000000000000000073401046102023000157170ustar 00000000000000use jsonwebtoken::{ crypto::{sign, verify}, Algorithm, DecodingKey, EncodingKey, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "use_pem")] use jsonwebtoken::{decode, encode, Header, Validation}; #[cfg(feature = "use_pem")] use time::OffsetDateTime; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Claims { sub: String, company: String, exp: i64, } #[test] fn round_trip_sign_verification_pk8() { let privkey = include_bytes!("private_ed25519_key.pk8"); let pubkey = include_bytes!("public_ed25519_key.pk8"); let encrypted = sign(b"hello world", &EncodingKey::from_ed_der(privkey), Algorithm::EdDSA).unwrap(); let is_valid = verify(&encrypted, b"hello world", &DecodingKey::from_ed_der(pubkey), Algorithm::EdDSA) .unwrap(); assert!(is_valid); } #[cfg(feature = "use_pem")] #[test] fn round_trip_sign_verification_pem() { let privkey_pem = include_bytes!("private_ed25519_key.pem"); let pubkey_pem = include_bytes!("public_ed25519_key.pem"); let encrypted = sign(b"hello world", &EncodingKey::from_ed_pem(privkey_pem).unwrap(), Algorithm::EdDSA) .unwrap(); let is_valid = verify( &encrypted, b"hello world", &DecodingKey::from_ed_pem(pubkey_pem).unwrap(), Algorithm::EdDSA, ) .unwrap(); assert!(is_valid); } #[cfg(feature = "use_pem")] #[test] fn round_trip_claim() { let privkey_pem = include_bytes!("private_ed25519_key.pem"); let pubkey_pem = include_bytes!("public_ed25519_key.pem"); let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let token = encode( &Header::new(Algorithm::EdDSA), &my_claims, &EncodingKey::from_ed_pem(privkey_pem).unwrap(), ) .unwrap(); let token_data = decode::( &token, &DecodingKey::from_ed_pem(pubkey_pem).unwrap(), &Validation::new(Algorithm::EdDSA), ) .unwrap(); assert_eq!(my_claims, token_data.claims); } #[cfg(feature = "use_pem")] #[test] fn ed_x() { let privkey = include_str!("private_ed25519_key.pem"); let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let x = "2-Jj2UvNCvQiUPNYRgSi0cJSPiJI6Rs6D0UTeEpQVj8"; let encrypted = encode( &Header::new(Algorithm::EdDSA), &my_claims, &EncodingKey::from_ed_pem(privkey.as_ref()).unwrap(), ) .unwrap(); let res = decode::( &encrypted, &DecodingKey::from_ed_components(x).unwrap(), &Validation::new(Algorithm::EdDSA), ); assert!(res.is_ok()); } #[cfg(feature = "use_pem")] #[test] fn ed_jwk() { use jsonwebtoken::jwk::Jwk; use serde_json::json; let privkey = include_str!("private_ed25519_key.pem"); let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let jwk: Jwk = serde_json::from_value(json!({ "kty": "OKP", "use": "sig", "crv": "Ed25519", "x": "2-Jj2UvNCvQiUPNYRgSi0cJSPiJI6Rs6D0UTeEpQVj8", "kid": "ed01", "alg": "EdDSA" })) .unwrap(); let encrypted = encode( &Header::new(Algorithm::EdDSA), &my_claims, &EncodingKey::from_ed_pem(privkey.as_ref()).unwrap(), ) .unwrap(); let res = decode::( &encrypted, &DecodingKey::from_jwk(&jwk).unwrap(), &Validation::new(Algorithm::EdDSA), ); assert!(res.is_ok()); } jsonwebtoken-8.3.0/tests/eddsa/private_ed25519_key.pem000064400000000000000000000001671046102023000206750ustar 00000000000000-----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIGrD/e7uKYqSY4twDEsRfMMuLSrODf14dpTiTK6K1YI0 -----END PRIVATE KEY----- jsonwebtoken-8.3.0/tests/eddsa/private_ed25519_key.pk8000064400000000000000000000000601046102023000206060ustar 000000000000000.0+ep" j)cp K|.-* xvLՂ4jsonwebtoken-8.3.0/tests/eddsa/public_ed25519_key.pem000064400000000000000000000001611046102023000204730ustar 00000000000000-----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEA2+Jj2UvNCvQiUPNYRgSi0cJSPiJI6Rs6D0UTeEpQVj8= -----END PUBLIC KEY----- jsonwebtoken-8.3.0/tests/eddsa/public_ed25519_key.pk8000064400000000000000000000000401046102023000204100ustar 00000000000000cK "PXFR>"H:ExJPV?jsonwebtoken-8.3.0/tests/header/cert_chain.json000064400000000000000000000104371046102023000177350ustar 00000000000000["MIIE3jCCA8agAwIBAgICAwEwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMTYwMTU0MzdaFw0yNjExMTYwMTU0MzdaMIHKMQswCQYDVQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xMzAxBgNVBAsTKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeTEwMC4GA1UEAxMnR28gRGFkZHkgU2VjdXJlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MREwDwYDVQQFEwgwNzk2OTI4NzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQt1RWMnCZM7DI161+4WQFapmGBWTtwY6vj3D3HKrjJM9N55DrtPDAjhI6zMBS2sofDPZVUBJ7fmd0LJR4h3mUpfjWoqVTr9vcyOdQmVZWt7/v+WIbXnvQAjYwqDL1CBM6nPwT27oDyqu9SoWlm2r4arV3aLGbqGmu75RpRSgAvSMeYddi5Kcju+GZtCpyz8/x4fKL4o/K1w/O5epHBp+YlLpyo7RJlbmr2EkRTcDCVw5wrWCs9CHRK8r5RsL+H0EwnWGu1NcWdrxcx+AuP7q2BNgWJCJjPOq8lh8BJ6qf9Z/dFjpfMFDniNoW1fho3/Rb2cRGadDAW/hOUoz+EDU8CAwEAAaOCATIwggEuMB0GA1UdDgQWBBT9rGEyk2xF1uLuhV+auud2mWjM5zAfBgNVHSMEGDAWgBTSxLDSkdRMEXGzYcs9of7dqGrU4zASBgNVHRMBAf8ECDAGAQH/AgEAMDMGCCsGAQUFBwEBBCcwJTAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZ29kYWRkeS5jb20wRgYDVR0fBD8wPTA7oDmgN4Y1aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5L2dkcm9vdC5jcmwwSwYDVR0gBEQwQjBABgRVHSAAMDgwNgYIKwYBBQUHAgEWKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeTAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBANKGwOy9+aG2Z+5mC6IGOgRQjhVyrEp0lVPLN8tESe8HkGsz2ZbwlFalEzAFPIUyIXvJxwqoJKSQ3kbTJSMUA2fCENZvD117esyfxVgqwcSeIaha86ykRvOe5GPLL5CkKSkB2XIsKd83ASe8T+5o0yGPwLPk9Qnt0hCqU7S+8MxZC9Y7lhyVJEnfzuz9p0iRFEUOOjZv2kWzRaJBydTXRE4+uXR21aITVSzGh6O1mawGhId/dQb8vxRMDsxuxN89txJx9OjxUUAiKEngHUuHqDTMBqLdElrRhjZkAzVvb3du6/KFUJheqwNTrZEjYx8WnM25sgVjOuH0aBsXBTWVU+4=","MIIE+zCCBGSgAwIBAgICAQ0wDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTA0MDYyOTE3MDYyMFoXDTI0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjggHhMIIB3TAdBgNVHQ4EFgQU0sSw0pHUTBFxs2HLPaH+3ahq1OMwgdIGA1UdIwSByjCBx6GBwaSBvjCBuzEkMCIGA1UEBxMbVmFsaUNlcnQgVmFsaWRhdGlvbiBOZXR3b3JrMRcwFQYDVQQKEw5WYWxpQ2VydCwgSW5jLjE1MDMGA1UECxMsVmFsaUNlcnQgQ2xhc3MgMiBQb2xpY3kgVmFsaWRhdGlvbiBBdXRob3JpdHkxITAfBgNVBAMTGGh0dHA6Ly93d3cudmFsaWNlcnQuY29tLzEgMB4GCSqGSIb3DQEJARYRaW5mb0B2YWxpY2VydC5jb22CAQEwDwYDVR0TAQH/BAUwAwEB/zAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmdvZGFkZHkuY29tMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeS9yb290LmNybDBLBgNVHSAERDBCMEAGBFUdIAAwODA2BggrBgEFBQcCARYqaHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOBgQC1QPmnHfbq/qQaQlpE9xXUhUaJwL6e4+PrxeNYiY+Sn1eocSxI0YGyeR+sBjUZsE4OWBsUs5iB0QQeyAfJg594RAoYC5jcdnplDQ1tgMQLARzLrUc+cb53S8wGd9D0VmsfSxOaFIqII6hR8INMqzW/Rn453HWkrugp++85j09VZw==","MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd"] jsonwebtoken-8.3.0/tests/header/cert_chain.pem000064400000000000000000000107751046102023000175520ustar 00000000000000-----BEGIN CERTIFICATE----- MIIE3jCCA8agAwIBAgICAwEwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMCVVM xITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR2 8gRGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExM TYwMTU0MzdaFw0yNjExMTYwMTU0MzdaMIHKMQswCQYDVQQGEwJVUzEQMA4GA1UE CBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEaMBgGA1UEChMRR29EYWR keS5jb20sIEluYy4xMzAxBgNVBAsTKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYW RkeS5jb20vcmVwb3NpdG9yeTEwMC4GA1UEAxMnR28gRGFkZHkgU2VjdXJlIENlc nRpZmljYXRpb24gQXV0aG9yaXR5MREwDwYDVQQFEwgwNzk2OTI4NzCCASIwDQYJ KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMQt1RWMnCZM7DI161+4WQFapmGBWTt wY6vj3D3HKrjJM9N55DrtPDAjhI6zMBS2sofDPZVUBJ7fmd0LJR4h3mUpfjWoqV Tr9vcyOdQmVZWt7/v+WIbXnvQAjYwqDL1CBM6nPwT27oDyqu9SoWlm2r4arV3aL GbqGmu75RpRSgAvSMeYddi5Kcju+GZtCpyz8/x4fKL4o/K1w/O5epHBp+YlLpyo 7RJlbmr2EkRTcDCVw5wrWCs9CHRK8r5RsL+H0EwnWGu1NcWdrxcx+AuP7q2BNgW JCJjPOq8lh8BJ6qf9Z/dFjpfMFDniNoW1fho3/Rb2cRGadDAW/hOUoz+EDU8CAw EAAaOCATIwggEuMB0GA1UdDgQWBBT9rGEyk2xF1uLuhV+auud2mWjM5zAfBgNVH SMEGDAWgBTSxLDSkdRMEXGzYcs9of7dqGrU4zASBgNVHRMBAf8ECDAGAQH/AgEA MDMGCCsGAQUFBwEBBCcwJTAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZ29kYWR keS5jb20wRgYDVR0fBD8wPTA7oDmgN4Y1aHR0cDovL2NlcnRpZmljYXRlcy5nb2 RhZGR5LmNvbS9yZXBvc2l0b3J5L2dkcm9vdC5jcmwwSwYDVR0gBEQwQjBABgRVH SAAMDgwNgYIKwYBBQUHAgEWKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5j b20vcmVwb3NpdG9yeTAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggE BANKGwOy9+aG2Z+5mC6IGOgRQjhVyrEp0lVPLN8tESe8HkGsz2ZbwlFalEzAFPI UyIXvJxwqoJKSQ3kbTJSMUA2fCENZvD117esyfxVgqwcSeIaha86ykRvOe5GPLL 5CkKSkB2XIsKd83ASe8T+5o0yGPwLPk9Qnt0hCqU7S+8MxZC9Y7lhyVJEnfzuz9 p0iRFEUOOjZv2kWzRaJBydTXRE4+uXR21aITVSzGh6O1mawGhId/dQb8vxRMDsx uxN89txJx9OjxUUAiKEngHUuHqDTMBqLdElrRhjZkAzVvb3du6/KFUJheqwNTrZ EjYx8WnM25sgVjOuH0aBsXBTWVU+4= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIE+zCCBGSgAwIBAgICAQ0wDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1Z hbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIE luYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb 24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8x IDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTA0MDYyOTE3MDY yMFoXDTI0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRoZS BHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3MgM iBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XC APVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux 6wwdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLO tXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWo riMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZ Eewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjggHhMIIB3TAdBgNVHQ 4EFgQU0sSw0pHUTBFxs2HLPaH+3ahq1OMwgdIGA1UdIwSByjCBx6GBwaSBvjCBu zEkMCIGA1UEBxMbVmFsaUNlcnQgVmFsaWRhdGlvbiBOZXR3b3JrMRcwFQYDVQQK Ew5WYWxpQ2VydCwgSW5jLjE1MDMGA1UECxMsVmFsaUNlcnQgQ2xhc3MgMiBQb2x pY3kgVmFsaWRhdGlvbiBBdXRob3JpdHkxITAfBgNVBAMTGGh0dHA6Ly93d3cudm FsaWNlcnQuY29tLzEgMB4GCSqGSIb3DQEJARYRaW5mb0B2YWxpY2VydC5jb22CA QEwDwYDVR0TAQH/BAUwAwEB/zAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGG F2h0dHA6Ly9vY3NwLmdvZGFkZHkuY29tMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA 6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeS9yb290LmNybD BLBgNVHSAERDBCMEAGBFUdIAAwODA2BggrBgEFBQcCARYqaHR0cDovL2NlcnRpZ mljYXRlcy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5MA4GA1UdDwEB/wQEAwIBBjAN BgkqhkiG9w0BAQUFAAOBgQC1QPmnHfbq/qQaQlpE9xXUhUaJwL6e4+PrxeNYiY+ Sn1eocSxI0YGyeR+sBjUZsE4OWBsUs5iB0QQeyAfJg594RAoYC5jcdnplDQ1tgM QLARzLrUc+cb53S8wGd9D0VmsfSxOaFIqII6hR8INMqzW/Rn453HWkrugp++85j 09VZw== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ 0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNT AzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0a G9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkq hkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE 5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTm V0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZ XJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQD ExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9 AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5a vIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zf N1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwb P7RfZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQU AA4GBADt/UG9vUJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQ C1u+mNr0HZDzTuIYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMM j4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd -----END CERTIFICATE----- jsonwebtoken-8.3.0/tests/header/mod.rs000064400000000000000000000020571046102023000160670ustar 00000000000000use base64::{engine::general_purpose::STANDARD, Engine}; use jsonwebtoken::Header; static CERT_CHAIN: [&str; 3] = include!("cert_chain.json"); #[test] fn x5c_der_empty_chain() { let header = Header { x5c: None, ..Default::default() }; assert_eq!(header.x5c_der().unwrap(), None); let header = Header { x5c: Some(Vec::new()), ..Default::default() }; assert_eq!(header.x5c_der().unwrap(), Some(Vec::new())); } #[test] fn x5c_der_valid_chain() { let der_chain: Vec> = CERT_CHAIN.iter().map(|x| STANDARD.decode(x)).collect::>().unwrap(); let x5c = Some(CERT_CHAIN.iter().map(ToString::to_string).collect()); let header = Header { x5c, ..Default::default() }; assert_eq!(header.x5c_der().unwrap(), Some(der_chain)); } #[test] fn x5c_der_invalid_chain() { let mut x5c: Vec<_> = CERT_CHAIN.iter().map(ToString::to_string).collect(); x5c.push("invalid base64 data".to_string()); let x5c = Some(x5c); let header = Header { x5c, ..Default::default() }; assert!(header.x5c_der().is_err()); } jsonwebtoken-8.3.0/tests/hmac.rs000064400000000000000000000147451046102023000147770ustar 00000000000000use jsonwebtoken::errors::ErrorKind; use jsonwebtoken::{ crypto::{sign, verify}, decode, decode_header, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation, }; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Claims { sub: String, company: String, exp: i64, } #[test] fn sign_hs256() { let result = sign(b"hello world", &EncodingKey::from_secret(b"secret"), Algorithm::HS256).unwrap(); let expected = "c0zGLzKEFWj0VxWuufTXiRMk5tlI5MbGDAYhzaxIYjo"; assert_eq!(result, expected); } #[test] fn verify_hs256() { let sig = "c0zGLzKEFWj0VxWuufTXiRMk5tlI5MbGDAYhzaxIYjo"; let valid = verify(sig, b"hello world", &DecodingKey::from_secret(b"secret"), Algorithm::HS256) .unwrap(); assert!(valid); } #[test] fn encode_with_custom_header() { let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let header = Header { kid: Some("kid".to_string()), ..Default::default() }; let token = encode(&header, &my_claims, &EncodingKey::from_secret(b"secret")).unwrap(); let token_data = decode::( &token, &DecodingKey::from_secret(b"secret"), &Validation::new(Algorithm::HS256), ) .unwrap(); assert_eq!(my_claims, token_data.claims); assert_eq!("kid", token_data.header.kid.unwrap()); } #[test] fn round_trip_claim() { let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let token = encode(&Header::default(), &my_claims, &EncodingKey::from_secret(b"secret")).unwrap(); let token_data = decode::( &token, &DecodingKey::from_secret(b"secret"), &Validation::new(Algorithm::HS256), ) .unwrap(); assert_eq!(my_claims, token_data.claims); assert!(token_data.header.kid.is_none()); } #[test] fn decode_token() { let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.9r56oF7ZliOBlOAyiOFperTGxBtPykRQiWNFxhDCW98"; let claims = decode::( token, &DecodingKey::from_secret(b"secret"), &Validation::new(Algorithm::HS256), ); println!("{:?}", claims); claims.unwrap(); } #[test] #[should_panic(expected = "InvalidToken")] fn decode_token_missing_parts() { let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; let claims = decode::( token, &DecodingKey::from_secret(b"secret"), &Validation::new(Algorithm::HS256), ); claims.unwrap(); } #[test] #[should_panic(expected = "InvalidSignature")] fn decode_token_invalid_signature() { let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUifQ.wrong"; let claims = decode::( token, &DecodingKey::from_secret(b"secret"), &Validation::new(Algorithm::HS256), ); claims.unwrap(); } #[test] #[should_panic(expected = "InvalidAlgorithm")] fn decode_token_wrong_algorithm() { let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUifQ.I1BvFoHe94AFf09O6tDbcSB8-jp8w6xZqmyHIwPeSdY"; let claims = decode::( token, &DecodingKey::from_secret(b"secret"), &Validation::new(Algorithm::RS512), ); claims.unwrap(); } #[test] #[should_panic(expected = "InvalidAlgorithm")] fn encode_wrong_alg_family() { let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let claims = encode(&Header::default(), &my_claims, &EncodingKey::from_rsa_der(b"secret")); claims.unwrap(); } #[test] fn decode_token_with_bytes_secret() { let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.Hm0yvKH25TavFPz7J_coST9lZFYH1hQo0tvhvImmaks"; let claims = decode::( token, &DecodingKey::from_secret(b"\x01\x02\x03"), &Validation::new(Algorithm::HS256), ); assert!(claims.is_ok()); } #[test] fn decode_header_only() { let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjb21wYW55IjoiMTIzNDU2Nzg5MCIsInN1YiI6IkpvaG4gRG9lIn0.S"; let header = decode_header(token).unwrap(); assert_eq!(header.alg, Algorithm::HS256); assert_eq!(header.typ, Some("JWT".to_string())); } #[test] fn dangerous_insecure_decode_valid_token() { let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.9r56oF7ZliOBlOAyiOFperTGxBtPykRQiWNFxhDCW98"; let mut validation = Validation::new(Algorithm::HS256); validation.insecure_disable_signature_validation(); let claims = decode::(token, &DecodingKey::from_secret(&[]), &validation); claims.unwrap(); } #[test] fn dangerous_insecure_decode_token_invalid_signature() { let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.wrong"; let mut validation = Validation::new(Algorithm::HS256); validation.insecure_disable_signature_validation(); let claims = decode::(token, &DecodingKey::from_secret(&[]), &validation); claims.unwrap(); } #[test] fn dangerous_insecure_decode_token_wrong_algorithm() { let token = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.fLxey-hxAKX5rNHHIx1_Ch0KmrbiuoakDVbsJjLWrx8fbjKjrPuWMYEJzTU3SBnYgnZokC-wqSdqckXUOunC-g"; let mut validation = Validation::new(Algorithm::HS256); validation.insecure_disable_signature_validation(); let claims = decode::(token, &DecodingKey::from_secret(&[]), &validation); claims.unwrap(); } #[test] fn dangerous_insecure_decode_token_with_validation_wrong_algorithm() { let token = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjk1MzI1MjQ4OX0.ONtEUTtP1QmyksYH9ijtPCaXoHjZVHcHKZGX1DuJyPiSyKlT93Y-oKgrp_OSkHSu4huxCcVObLzwsdwF-xwiAQ"; let mut validation = Validation::new(Algorithm::HS256); validation.insecure_disable_signature_validation(); let claims = decode::(token, &DecodingKey::from_secret(&[]), &validation); let err = claims.unwrap_err(); assert_eq!(err.kind(), &ErrorKind::ExpiredSignature); } jsonwebtoken-8.3.0/tests/lib.rs000064400000000000000000000000531046102023000146200ustar 00000000000000mod ecdsa; mod eddsa; mod header; mod rsa; jsonwebtoken-8.3.0/tests/rsa/README.md000064400000000000000000000016101046102023000155500ustar 00000000000000# RSA Tests ## How to generate keys and certificates ### RSA PKCS1 ``` openssl genrsa -out private_rsa_pkcs1.pem openssl rsa -in private_rsa_pkcs1.pem -RSAPublicKey_out -out public_rsa_pkcs1.pem openssl req -new -key private_rsa_pkcs1.pem -out certificate_rsa_pkcs1.csr openssl x509 -req -sha256 -days 358000 -in certificate_rsa_pkcs1.csr -signkey private_rsa_pkcs1.pem -out certificate_rsa_pkcs1.crt ``` ### RSA PKCS8 ``` openssl genpkey -algorithm RSA -out private_rsa_pkcs8.pem -pkeyopt rsa_keygen_bits:2048` openssl rsa -pubout -in private_rsa_pkcs8.pem -out public_rsa_pkcs.pem openssl req -new -key private_rsa_pkcs8.key -out certificate_rsa_pkcs8.csr openssl x509 -req -sha256 -days 358000 -in certificate_rsa_pkcs8.csr -signkey private_rsa_pkcs8.key -out certificate_rsa_pkcs8.crt ``` ### Convert to DER format ``` openssl rsa -inform PEM -in private.pem -outform DER -out private.der ``` jsonwebtoken-8.3.0/tests/rsa/certificate_jwtio.crt000064400000000000000000000021471046102023000205070ustar 00000000000000-----BEGIN CERTIFICATE----- MIIDEzCCAfsCFC36imR60P89zge2Gw14moyfJE+hMA0GCSqGSIb3DQEBCwUAMEUx CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl cm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMjIxMjAxMDgzNzM2WhgPMzAwMzAyMDIw ODM3MzZaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUA A4IBDwAwggEKAoIBAQCfPKKzVmN80HRsGAoUxK++RO3CW8GxomrtLnAD6TN5U5Wl VbCRZ1WFrizfxcz+lr/Kvjtq/v7PdVOa8NHIAdxpP3bCFEQWku/1yPmVN4lKJvKv 8yub9i2MJlVaBo5giHCtfAouo+v/XWKdawCR8jK28dZPFlgRxcuABcW5S5pLe4X2 ASI1DDMZNTW/QWqSpMGvgHydbccI3jtdS7S3xjR76V/izg7FBrBYPv0n3/l3dHLS 9tXcCbUW0YmIm87BGwh9UKEOlhK1NwdMIyq29ZtXovXUFaSnMZdJbge/jepr4ZJg 4PZBTrwxvn2hKTY4H4G04ukmh+ZsYQaC+bDIIj0zAgMBAAEwDQYJKoZIhvcNAQEL BQADggEBACPyDJXCWluYWIZZGeCandW9Sp51kPVPEAcDWJjz6wlSsoR38ZyQk4so PcOrBc8tNxG7BYbzaSC31df4qvOeUM6j8A+WemtCUbB99TPBa26VyUNhkQGlqF0G 3dd6h0mCIu0aftD2aci4mDBll5D4Sl99eLq1w83yfYh2KTb8lgwsmckdU1XnWHjZ Vg/N8FaqhIN1mqFY5LozPJUyX9G2vIZFA2D8jxc9S9lfosXG7j1VRJ0UIm60A7Kb 6y25m81IF1QVhDub7AVg7svyoV6tFBefuTdZzJG/gOx75eMdkI3BEBSEaw0I87Bx Z5Qp99Qv3cs6tjUr8K0kwCbqeme5UxY= -----END CERTIFICATE----- jsonwebtoken-8.3.0/tests/rsa/certificate_jwtio.csr000064400000000000000000000016741046102023000205120ustar 00000000000000-----BEGIN CERTIFICATE REQUEST----- MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAJ88orNWY3zQdGwYChTEr75E7cJbwbGiau0ucAPp M3lTlaVVsJFnVYWuLN/FzP6Wv8q+O2r+/s91U5rw0cgB3Gk/dsIURBaS7/XI+ZU3 iUom8q/zK5v2LYwmVVoGjmCIcK18Ci6j6/9dYp1rAJHyMrbx1k8WWBHFy4AFxblL mkt7hfYBIjUMMxk1Nb9BapKkwa+AfJ1txwjeO11LtLfGNHvpX+LODsUGsFg+/Sff +Xd0ctL21dwJtRbRiYibzsEbCH1QoQ6WErU3B0wjKrb1m1ei9dQVpKcxl0luB7+N 6mvhkmDg9kFOvDG+faEpNjgfgbTi6SaH5mxhBoL5sMgiPTMCAwEAAaAAMA0GCSqG SIb3DQEBCwUAA4IBAQCBHiYk5HH/5G/Nw49242mb4YAS7B3gBiNvop0/Jw62u/Ys QFgaiKqPXg5un/dCvU3eoaBDKaQUhslzvya3ziKceEClJfk2w6kXSh8OU9l/39Cj NEtNcpjlj427QWI7dQA83TrTNMokk5gUkgqnY6JDDzGfctlxN9J4iQC1E5hoTpos rEjzBsVHg1h4iX7QoKO36O7S1jE/hCeFnWUPC9svqx22fyterA1Jl5W3LO7kQ8pf SYZhjZqjfM7Bm+HcVotYNFcWkoqMQ60dOCA9QB38oPXHFlweD7oWRQX1P3y8W9pP kXAuYCmWDze2K1ezWImKi9Atga+ByBf/JNBVVn/G -----END CERTIFICATE REQUEST----- jsonwebtoken-8.3.0/tests/rsa/certificate_rsa_key_pkcs1.crt000064400000000000000000000021471046102023000221110ustar 00000000000000-----BEGIN CERTIFICATE----- MIIDEzCCAfsCFGRy3pRvxgdoKPiWJfvmDcJhcaYOMA0GCSqGSIb3DQEBCwUAMEUx CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl cm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMjIxMjAxMDgzOTA3WhgPMzAwMzAyMDIw ODM5MDdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUA A4IBDwAwggEKAoIBAQDJETqse41HRBsc7cfcq3ak4oZWFCoZlcic525A3FfO4qW9 BMtRO/iXiyCCHn8JhiL9y8j5JdVP2Q9ZIpfElcFd3/guS9w+5RqQGgCR+H56IVUy HZWtTJbKPcwWXQdNUX0rBFcsBzCRESJLeelOEdHIjG7LRkx5l/FUvlqsyHDVJEQs HwegZ8b8C0fz0EgT2MMEdn10t6Ur1rXzjMB/wvCg8vG8lvciXmedyo9xJ8oMOh0w UEgxziVDMMovmC+aJctcHUAYubwoGN8TyzcvnGqL7JSh36Pwy28iPzXZ2RLhAyJF U39vLaHdljwthUaupldlNyCfa6Ofy4qNctlUPlN1AgMBAAEwDQYJKoZIhvcNAQEL BQADggEBAE4/4DeDd+xNH5zdyBAve4w7x5YQ1NiSKtiV5KRUG1+mndr5MubTI1SC rezrtE9D4zEqz0u2Glg9wZK9YvbshNSo0pXUEpgnJ30wKYbB9y7JlU8/G/+17da/ KIX5pytMUw5JIcj0GGrkPoVDdyU6AYd9j+dOiMyoExcYyvOstgslEDn0MXtxi58j IykQuuwe1LDWgm58mns1yCPuVr23/r2bj1iTodS2RFeSDk7oK3Kvy+b6nsxJjdyg nQ5Jwb4Seg3+7Sa/pybG5/vNX5OYHA6laEV+dMyiEBJ+XSX55KQYc4FHu2Xd0O0A OBZ2fovx2HMaiFs8tJroFU6IAYLiQCg= -----END CERTIFICATE----- jsonwebtoken-8.3.0/tests/rsa/certificate_rsa_key_pkcs1.csr000064400000000000000000000016741046102023000221140ustar 00000000000000-----BEGIN CERTIFICATE REQUEST----- MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAMkROqx7jUdEGxztx9yrdqTihlYUKhmVyJznbkDc V87ipb0Ey1E7+JeLIIIefwmGIv3LyPkl1U/ZD1kil8SVwV3f+C5L3D7lGpAaAJH4 fnohVTIdla1Mlso9zBZdB01RfSsEVywHMJERIkt56U4R0ciMbstGTHmX8VS+WqzI cNUkRCwfB6BnxvwLR/PQSBPYwwR2fXS3pSvWtfOMwH/C8KDy8byW9yJeZ53Kj3En ygw6HTBQSDHOJUMwyi+YL5oly1wdQBi5vCgY3xPLNy+caovslKHfo/DLbyI/NdnZ EuEDIkVTf28tod2WPC2FRq6mV2U3IJ9ro5/Lio1y2VQ+U3UCAwEAAaAAMA0GCSqG SIb3DQEBCwUAA4IBAQAhhe1K0osJIZLOalQYWU2fmStHrTC6Hkakf6E5rjIAkQqZ G38LnrC/wrPurSKceS8Rb9jmpqODmVdeMnqzag0/t428pgesiO2mdpSg62b/9tEc xQmnhGkXce54AAnA27VcLpIrJPe/adX1yEIT3EZce26RMIjB1wN/Y1bSjMSvFjHo HDIDQi7ZSC/U4xXfjn+TSfBKkKg8Am9hxZNGUCt7PhMJ13i+nXdF2JzhjCisTCTu C2R+ccN3BtAKIs2BdYvYze55LBCbhb1Zo2jYDPi86CRPcozp6wzOxvKzzqxZRbcA HnyTfnHMPkQzAWMJXoDawPg5imwhM3kN2Qo5CzSA -----END CERTIFICATE REQUEST----- jsonwebtoken-8.3.0/tests/rsa/certificate_rsa_key_pkcs8.crt000064400000000000000000000021471046102023000221200ustar 00000000000000-----BEGIN CERTIFICATE----- MIIDEzCCAfsCFFkk9C6Hjy5OekqYnYg4D54caVHwMA0GCSqGSIb3DQEBCwUAMEUx CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl cm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMjIxMjAxMDgzOTAxWhgPMzAwMzAyMDIw ODM5MDFaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUA A4IBDwAwggEKAoIBAQDJETqse41HRBsc7cfcq3ak4oZWFCoZlcic525A3FfO4qW9 BMtRO/iXiyCCHn8JhiL9y8j5JdVP2Q9ZIpfElcFd3/guS9w+5RqQGgCR+H56IVUy HZWtTJbKPcwWXQdNUX0rBFcsBzCRESJLeelOEdHIjG7LRkx5l/FUvlqsyHDVJEQs HwegZ8b8C0fz0EgT2MMEdn10t6Ur1rXzjMB/wvCg8vG8lvciXmedyo9xJ8oMOh0w UEgxziVDMMovmC+aJctcHUAYubwoGN8TyzcvnGqL7JSh36Pwy28iPzXZ2RLhAyJF U39vLaHdljwthUaupldlNyCfa6Ofy4qNctlUPlN1AgMBAAEwDQYJKoZIhvcNAQEL BQADggEBAIOPwsrHPCl2F4VYXXUWjBGHlupJ9GbDk+UgjR+87eEhwuXzxiE/Yv1S 3SvWkkzUQ0zu2o5Z4B6zf5V4HnpFiy4JikWdhY5jmS30pToGQ9mhaMHa3WMqLh39 vCLqvccK+PfOmS13iaMKjZQRrn6yX1fRE7zc3bIBQou1rkP0utt3kdp0lyvnK24k 6F2I7MGMLWtjdTxIHuJks7kZ2JQyrkHXBtP0PBKhINhaBY3PQovrF5RM2fCzT87x F7ZHKJu5HVRJUKcK8nRgC2J2iOHUqYSeyGid0XZZN1g8ux1ZoCaYyqZQhalwSZmD 0D2NJhnz1Eo+gAWAbjsNPBGOGsMeyHI= -----END CERTIFICATE----- jsonwebtoken-8.3.0/tests/rsa/certificate_rsa_key_pkcs8.csr000064400000000000000000000016741046102023000221230ustar 00000000000000-----BEGIN CERTIFICATE REQUEST----- MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAMkROqx7jUdEGxztx9yrdqTihlYUKhmVyJznbkDc V87ipb0Ey1E7+JeLIIIefwmGIv3LyPkl1U/ZD1kil8SVwV3f+C5L3D7lGpAaAJH4 fnohVTIdla1Mlso9zBZdB01RfSsEVywHMJERIkt56U4R0ciMbstGTHmX8VS+WqzI cNUkRCwfB6BnxvwLR/PQSBPYwwR2fXS3pSvWtfOMwH/C8KDy8byW9yJeZ53Kj3En ygw6HTBQSDHOJUMwyi+YL5oly1wdQBi5vCgY3xPLNy+caovslKHfo/DLbyI/NdnZ EuEDIkVTf28tod2WPC2FRq6mV2U3IJ9ro5/Lio1y2VQ+U3UCAwEAAaAAMA0GCSqG SIb3DQEBCwUAA4IBAQAhhe1K0osJIZLOalQYWU2fmStHrTC6Hkakf6E5rjIAkQqZ G38LnrC/wrPurSKceS8Rb9jmpqODmVdeMnqzag0/t428pgesiO2mdpSg62b/9tEc xQmnhGkXce54AAnA27VcLpIrJPe/adX1yEIT3EZce26RMIjB1wN/Y1bSjMSvFjHo HDIDQi7ZSC/U4xXfjn+TSfBKkKg8Am9hxZNGUCt7PhMJ13i+nXdF2JzhjCisTCTu C2R+ccN3BtAKIs2BdYvYze55LBCbhb1Zo2jYDPi86CRPcozp6wzOxvKzzqxZRbcA HnyTfnHMPkQzAWMJXoDawPg5imwhM3kN2Qo5CzSA -----END CERTIFICATE REQUEST----- jsonwebtoken-8.3.0/tests/rsa/mod.rs000064400000000000000000000166431046102023000154320ustar 00000000000000use jsonwebtoken::{ crypto::{sign, verify}, Algorithm, DecodingKey, EncodingKey, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "use_pem")] use jsonwebtoken::{decode, encode, Header, Validation}; #[cfg(feature = "use_pem")] use time::OffsetDateTime; const RSA_ALGORITHMS: &[Algorithm] = &[ Algorithm::RS256, Algorithm::RS384, Algorithm::RS512, Algorithm::PS256, Algorithm::PS384, Algorithm::PS512, ]; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Claims { sub: String, company: String, exp: i64, } #[cfg(feature = "use_pem")] #[test] fn round_trip_sign_verification_pem_pkcs1() { let privkey_pem = include_bytes!("private_rsa_key_pkcs1.pem"); let pubkey_pem = include_bytes!("public_rsa_key_pkcs1.pem"); let certificate_pem = include_bytes!("certificate_rsa_key_pkcs1.crt"); for &alg in RSA_ALGORITHMS { let encrypted = sign(b"hello world", &EncodingKey::from_rsa_pem(privkey_pem).unwrap(), alg).unwrap(); let is_valid = verify( &encrypted, b"hello world", &DecodingKey::from_rsa_pem(pubkey_pem).unwrap(), alg, ) .unwrap(); assert!(is_valid); let cert_is_valid = verify( &encrypted, b"hello world", &DecodingKey::from_rsa_pem(certificate_pem).unwrap(), alg, ) .unwrap(); assert!(cert_is_valid); } } #[cfg(feature = "use_pem")] #[test] fn round_trip_sign_verification_pem_pkcs8() { let privkey_pem = include_bytes!("private_rsa_key_pkcs8.pem"); let pubkey_pem = include_bytes!("public_rsa_key_pkcs8.pem"); let certificate_pem = include_bytes!("certificate_rsa_key_pkcs8.crt"); for &alg in RSA_ALGORITHMS { let encrypted = sign(b"hello world", &EncodingKey::from_rsa_pem(privkey_pem).unwrap(), alg).unwrap(); let is_valid = verify( &encrypted, b"hello world", &DecodingKey::from_rsa_pem(pubkey_pem).unwrap(), alg, ) .unwrap(); assert!(is_valid); let cert_is_valid = verify( &encrypted, b"hello world", &DecodingKey::from_rsa_pem(certificate_pem).unwrap(), alg, ) .unwrap(); assert!(cert_is_valid); } } #[test] fn round_trip_sign_verification_der() { let privkey_der = include_bytes!("private_rsa_key.der"); let pubkey_der = include_bytes!("public_rsa_key.der"); for &alg in RSA_ALGORITHMS { let encrypted = sign(b"hello world", &EncodingKey::from_rsa_der(privkey_der), alg).unwrap(); let is_valid = verify(&encrypted, b"hello world", &DecodingKey::from_rsa_der(pubkey_der), alg) .unwrap(); assert!(is_valid); } } #[cfg(feature = "use_pem")] #[test] fn round_trip_claim() { let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let privkey_pem = include_bytes!("private_rsa_key_pkcs1.pem"); let pubkey_pem = include_bytes!("public_rsa_key_pkcs1.pem"); let certificate_pem = include_bytes!("certificate_rsa_key_pkcs1.crt"); for &alg in RSA_ALGORITHMS { let token = encode(&Header::new(alg), &my_claims, &EncodingKey::from_rsa_pem(privkey_pem).unwrap()) .unwrap(); let token_data = decode::( &token, &DecodingKey::from_rsa_pem(pubkey_pem).unwrap(), &Validation::new(alg), ) .unwrap(); assert_eq!(my_claims, token_data.claims); assert!(token_data.header.kid.is_none()); let cert_token_data = decode::( &token, &DecodingKey::from_rsa_pem(certificate_pem).unwrap(), &Validation::new(alg), ) .unwrap(); assert_eq!(my_claims, cert_token_data.claims); assert!(cert_token_data.header.kid.is_none()); } } #[cfg(feature = "use_pem")] #[test] fn rsa_modulus_exponent() { let privkey = include_str!("private_rsa_key_pkcs1.pem"); let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let n = "yRE6rHuNR0QbHO3H3Kt2pOKGVhQqGZXInOduQNxXzuKlvQTLUTv4l4sggh5_CYYi_cvI-SXVT9kPWSKXxJXBXd_4LkvcPuUakBoAkfh-eiFVMh2VrUyWyj3MFl0HTVF9KwRXLAcwkREiS3npThHRyIxuy0ZMeZfxVL5arMhw1SRELB8HoGfG_AtH89BIE9jDBHZ9dLelK9a184zAf8LwoPLxvJb3Il5nncqPcSfKDDodMFBIMc4lQzDKL5gvmiXLXB1AGLm8KBjfE8s3L5xqi-yUod-j8MtvIj812dkS4QMiRVN_by2h3ZY8LYVGrqZXZTcgn2ujn8uKjXLZVD5TdQ"; let e = "AQAB"; let encrypted = encode( &Header::new(Algorithm::RS256), &my_claims, &EncodingKey::from_rsa_pem(privkey.as_ref()).unwrap(), ) .unwrap(); let res = decode::( &encrypted, &DecodingKey::from_rsa_components(n, e).unwrap(), &Validation::new(Algorithm::RS256), ); assert!(res.is_ok()); } #[cfg(feature = "use_pem")] #[test] fn rsa_jwk() { use jsonwebtoken::jwk::Jwk; use serde_json::json; let privkey = include_str!("private_rsa_key_pkcs8.pem"); let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let jwk:Jwk = serde_json::from_value(json!({ "kty": "RSA", "n": "yRE6rHuNR0QbHO3H3Kt2pOKGVhQqGZXInOduQNxXzuKlvQTLUTv4l4sggh5_CYYi_cvI-SXVT9kPWSKXxJXBXd_4LkvcPuUakBoAkfh-eiFVMh2VrUyWyj3MFl0HTVF9KwRXLAcwkREiS3npThHRyIxuy0ZMeZfxVL5arMhw1SRELB8HoGfG_AtH89BIE9jDBHZ9dLelK9a184zAf8LwoPLxvJb3Il5nncqPcSfKDDodMFBIMc4lQzDKL5gvmiXLXB1AGLm8KBjfE8s3L5xqi-yUod-j8MtvIj812dkS4QMiRVN_by2h3ZY8LYVGrqZXZTcgn2ujn8uKjXLZVD5TdQ", "e": "AQAB", "kid": "rsa01", "alg": "RS256", "use": "sig" })).unwrap(); let encrypted = encode( &Header::new(Algorithm::RS256), &my_claims, &EncodingKey::from_rsa_pem(privkey.as_ref()).unwrap(), ) .unwrap(); let res = decode::( &encrypted, &DecodingKey::from_jwk(&jwk).unwrap(), &Validation::new(Algorithm::RS256), ); assert!(res.is_ok()); } // https://jwt.io/ is often used for examples so ensure their example works with jsonwebtoken #[cfg(feature = "use_pem")] #[test] fn roundtrip_with_jwtio_example_jey() { let privkey_pem = include_bytes!("private_jwtio.pem"); let pubkey_pem = include_bytes!("public_jwtio.pem"); let certificate_pem = include_bytes!("certificate_jwtio.crt"); let my_claims = Claims { sub: "b@b.com".to_string(), company: "ACME".to_string(), exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; for &alg in RSA_ALGORITHMS { let token = encode(&Header::new(alg), &my_claims, &EncodingKey::from_rsa_pem(privkey_pem).unwrap()) .unwrap(); let token_data = decode::( &token, &DecodingKey::from_rsa_pem(pubkey_pem).unwrap(), &Validation::new(alg), ) .unwrap(); assert_eq!(my_claims, token_data.claims); let cert_token_data = decode::( &token, &DecodingKey::from_rsa_pem(certificate_pem).unwrap(), &Validation::new(alg), ) .unwrap(); assert_eq!(my_claims, cert_token_data.claims); } } jsonwebtoken-8.3.0/tests/rsa/private_jwtio.pem000064400000000000000000000032131046102023000176630ustar 00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWw kWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mr m/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEi NQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV 3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2 QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQABAoIBACiARq2wkltjtcjs kFvZ7w1JAORHbEufEO1Eu27zOIlqbgyAcAl7q+/1bip4Z/x1IVES84/yTaM8p0go amMhvgry/mS8vNi1BN2SAZEnb/7xSxbflb70bX9RHLJqKnp5GZe2jexw+wyXlwaM +bclUCrh9e1ltH7IvUrRrQnFJfh+is1fRon9Co9Li0GwoN0x0byrrngU8Ak3Y6D9 D8GjQA4Elm94ST3izJv8iCOLSDBmzsPsXfcCUZfmTfZ5DbUDMbMxRnSo3nQeoKGC 0Lj9FkWcfmLcpGlSXTO+Ww1L7EGq+PT3NtRae1FZPwjddQ1/4V905kyQFLamAA5Y lSpE2wkCgYEAy1OPLQcZt4NQnQzPz2SBJqQN2P5u3vXl+zNVKP8w4eBv0vWuJJF+ hkGNnSxXQrTkvDOIUddSKOzHHgSg4nY6K02ecyT0PPm/UZvtRpWrnBjcEVtHEJNp bU9pLD5iZ0J9sbzPU/LxPmuAP2Bs8JmTn6aFRspFrP7W0s1Nmk2jsm0CgYEAyH0X +jpoqxj4efZfkUrg5GbSEhf+dZglf0tTOA5bVg8IYwtmNk/pniLG/zI7c+GlTc9B BwfMr59EzBq/eFMI7+LgXaVUsM/sS4Ry+yeK6SJx/otIMWtDfqxsLD8CPMCRvecC 2Pip4uSgrl0MOebl9XKp57GoaUWRWRHqwV4Y6h8CgYAZhI4mh4qZtnhKjY4TKDjx QYufXSdLAi9v3FxmvchDwOgn4L+PRVdMwDNms2bsL0m5uPn104EzM6w1vzz1zwKz 5pTpPI0OjgWN13Tq8+PKvm/4Ga2MjgOgPWQkslulO/oMcXbPwWC3hcRdr9tcQtn9 Imf9n2spL/6EDFId+Hp/7QKBgAqlWdiXsWckdE1Fn91/NGHsc8syKvjjk1onDcw0 NvVi5vcba9oGdElJX3e9mxqUKMrw7msJJv1MX8LWyMQC5L6YNYHDfbPF1q5L4i8j 8mRex97UVokJQRRA452V2vCO6S5ETgpnad36de3MUxHgCOX3qL382Qx9/THVmbma 3YfRAoGAUxL/Eu5yvMK8SAt/dJK6FedngcM3JEFNplmtLYVLWhkIlNRGDwkg3I5K y18Ae9n7dHVueyslrb6weq7dTkYDi3iOYRW8HRkIQh06wEdbxt0shTzAJvvCQfrB jg/3747WSsf/zBTcHihTRBdAv6OmdhV4/dD5YBfLAkLrd+mX7iE= -----END RSA PRIVATE KEY----- jsonwebtoken-8.3.0/tests/rsa/private_rsa_key.der000064400000000000000000000022501046102023000201550ustar 000000000000000:{GDܫvV*Ȝn@W⥽Q;  "%OY"ĕ].K>~z!U2L=]MQ}+W,0"KyNȌnFLyTZp$D,g GHv}t+ֵ"^gʏq' :0PH1%C0//%\@(7/j씡ߣo"?5"ESo-ݖ<-FWe7 kˊrT>SutDB4;"YJ@]cP8aQPd"7w<f;mY 0)R)xJ¥R3@@8ₛr8 =G4 ^GBĥpXܣA98FlǮ IhHg~v(Ng.|홅Oukķ?sm1@QF%"w .mlm/>: w,ִ'GTePU24LV-̟ yIK`9 ’?tg-6m1RlKXte'FF EH%&M~}Y&e*!FМʗS*umZ'(Fή#"reCM qFSaTnc*: b[$5>Υ|0A5+Y'L'.bE oAcpl1B3˜ 'Ho2ц{#eiC%[ ]{H8ɯT.qWܟ}Pd*V*u8`4@J1B a!fiߏ0/H#o;oW^!@!J[e`aT|PyHO2h$2?Z(XJ6.Id ]:hiF1¥{R8_B%,X/'=G= (&/J!Vt6?M5٨@e1NyGRKŤdCo@)0ƥړAP Ȉ3m ||YcC?\jr!qaS=Xݓ7Ajsonwebtoken-8.3.0/tests/rsa/private_rsa_key_pkcs1.pem000064400000000000000000000032171046102023000212710ustar 00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAyRE6rHuNR0QbHO3H3Kt2pOKGVhQqGZXInOduQNxXzuKlvQTL UTv4l4sggh5/CYYi/cvI+SXVT9kPWSKXxJXBXd/4LkvcPuUakBoAkfh+eiFVMh2V rUyWyj3MFl0HTVF9KwRXLAcwkREiS3npThHRyIxuy0ZMeZfxVL5arMhw1SRELB8H oGfG/AtH89BIE9jDBHZ9dLelK9a184zAf8LwoPLxvJb3Il5nncqPcSfKDDodMFBI Mc4lQzDKL5gvmiXLXB1AGLm8KBjfE8s3L5xqi+yUod+j8MtvIj812dkS4QMiRVN/ by2h3ZY8LYVGrqZXZTcgn2ujn8uKjXLZVD5TdQIDAQABAoIBAHREk0I0O9DvECKd WUpAmF3mY7oY9PNQiu44Yaf+AoSuyRpRUGTMIgc3u3eivOE8ALX0BmYUO5JtuRNZ Dpvt4SAwqCnVUinIf6C+eH/wSurCpapSM0BAHp4aOA7igptyOMgMPYBHNA1e9A7j E0dCxKWMl3DSWNyjQTk4zeRGEAEfbNjHrq6YCtjHSZSLmWiG80hnfnYos9hOr5Jn LnyS7ZmFE/5P3XVrxLc/tQ5zum0R4cbrgzHiQP5RgfxGJaEi7XcgherCCOgurJSS bYH29Gz8u5fFbS+Yg8s+OiCss3cs1rSgJ9/eHZuzGEdUZVARH6hVMjSuwvqVTFaE 8AgtleECgYEA+uLMn4kNqHlJS2A5uAnCkj90ZxEtNm3E8hAxUrhssktY5XSOAPBl xyf5RuRGIImGtUVIr4HuJSa5TX48n3Vdt9MYCprO/iYl6moNRSPt5qowIIOJmIjY 2mqPDfDt/zw+fcDD3lmCJrFlzcnh0uea1CohxEbQnL3cypeLt+WbU6kCgYEAzSp1 9m1ajieFkqgoB0YTpt/OroDx38vvI5unInJlEeOjQ+oIAQdN2wpxBvTrRorMU6P0 7mFUbt1j+Co6CbNiw+X8HcCaqYLR5clbJOOWNR36PuzOpQLkfK8woupBxzW9B8gZ mY8rB1mbJ+/WTPrEJy6YGmIEBkWylQ2VpW8O4O0CgYEApdbvvfFBlwD9YxbrcGz7 MeNCFbMz+MucqQntIKoKJ91ImPxvtc0y6e/Rhnv0oyNlaUOwJVu0yNgNG117w0g4 t/+Q38mvVC5xV7/cn7x9UMFk6MkqVir3dYGEqIl/OP1grY2Tq9HtB5iyG9L8NIam QOLMyUqqMUILxdthHyFmiGkCgYEAn9+PjpjGMPHxL0gj8Q8VbzsFtou6b1deIRRA 2CHmSltltR1gYVTMwXxQeUhPMmgkMqUXzs4/WijgpthY44hK1TaZEKIuoxrS70nJ 4WQLf5a9k1065fDsFZD6yGjdGxvwEmlGMZgTwqV7t1I4X0Ilqhav5hcs5apYL7gn PYPeRz0CgYALHCj/Ji8XSsDoF/MhVhnGdIs2P99NNdmo3R2Pv0CuZbDKMU559LJH UvrKS8WkuWRDuKrz1W/EQKApFjDGpdqToZqriUFQzwy7mR3ayIiogzNtHcvbDHx8 oFnGY0OFksX/ye0/XGpy2SFxYRwGU98HPYeBvAQQrVjdkzfy7BmXQQ== -----END RSA PRIVATE KEY----- jsonwebtoken-8.3.0/tests/rsa/private_rsa_key_pkcs8.pem000064400000000000000000000032501046102023000212750ustar 00000000000000-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJETqse41HRBsc 7cfcq3ak4oZWFCoZlcic525A3FfO4qW9BMtRO/iXiyCCHn8JhiL9y8j5JdVP2Q9Z IpfElcFd3/guS9w+5RqQGgCR+H56IVUyHZWtTJbKPcwWXQdNUX0rBFcsBzCRESJL eelOEdHIjG7LRkx5l/FUvlqsyHDVJEQsHwegZ8b8C0fz0EgT2MMEdn10t6Ur1rXz jMB/wvCg8vG8lvciXmedyo9xJ8oMOh0wUEgxziVDMMovmC+aJctcHUAYubwoGN8T yzcvnGqL7JSh36Pwy28iPzXZ2RLhAyJFU39vLaHdljwthUaupldlNyCfa6Ofy4qN ctlUPlN1AgMBAAECggEAdESTQjQ70O8QIp1ZSkCYXeZjuhj081CK7jhhp/4ChK7J GlFQZMwiBze7d6K84TwAtfQGZhQ7km25E1kOm+3hIDCoKdVSKch/oL54f/BK6sKl qlIzQEAenho4DuKCm3I4yAw9gEc0DV70DuMTR0LEpYyXcNJY3KNBOTjN5EYQAR9s 2MeurpgK2MdJlIuZaIbzSGd+diiz2E6vkmcufJLtmYUT/k/ddWvEtz+1DnO6bRHh xuuDMeJA/lGB/EYloSLtdyCF6sII6C6slJJtgfb0bPy7l8VtL5iDyz46IKyzdyzW tKAn394dm7MYR1RlUBEfqFUyNK7C+pVMVoTwCC2V4QKBgQD64syfiQ2oeUlLYDm4 CcKSP3RnES02bcTyEDFSuGyyS1jldI4A8GXHJ/lG5EYgiYa1RUivge4lJrlNfjyf dV230xgKms7+JiXqag1FI+3mqjAgg4mYiNjaao8N8O3/PD59wMPeWYImsWXNyeHS 55rUKiHERtCcvdzKl4u35ZtTqQKBgQDNKnX2bVqOJ4WSqCgHRhOm386ugPHfy+8j m6cicmUR46ND6ggBB03bCnEG9OtGisxTo/TuYVRu3WP4KjoJs2LD5fwdwJqpgtHl yVsk45Y1Hfo+7M6lAuR8rzCi6kHHNb0HyBmZjysHWZsn79ZM+sQnLpgaYgQGRbKV DZWlbw7g7QKBgQCl1u+98UGXAP1jFutwbPsx40IVszP4y5ypCe0gqgon3UiY/G+1 zTLp79GGe/SjI2VpQ7AlW7TI2A0bXXvDSDi3/5Dfya9ULnFXv9yfvH1QwWToySpW Kvd1gYSoiX84/WCtjZOr0e0HmLIb0vw0hqZA4szJSqoxQgvF22EfIWaIaQKBgQCf 34+OmMYw8fEvSCPxDxVvOwW2i7pvV14hFEDYIeZKW2W1HWBhVMzBfFB5SE8yaCQy pRfOzj9aKOCm2FjjiErVNpkQoi6jGtLvScnhZAt/lr2TXTrl8OwVkPrIaN0bG/AS aUYxmBPCpXu3UjhfQiWqFq/mFyzlqlgvuCc9g95HPQKBgAscKP8mLxdKwOgX8yFW GcZ0izY/30012ajdHY+/QK5lsMoxTnn0skdS+spLxaS5ZEO4qvPVb8RAoCkWMMal 2pOhmquJQVDPDLuZHdrIiKiDM20dy9sMfHygWcZjQ4WSxf/J7T9canLZIXFhHAZT 3wc9h4G8BBCtWN2TN/LsGZdB -----END PRIVATE KEY----- jsonwebtoken-8.3.0/tests/rsa/public_jwtio.pem000064400000000000000000000007031046102023000174700ustar 00000000000000-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv vkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc aT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy tvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0 e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb V6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9 MwIDAQAB -----END PUBLIC KEY----- jsonwebtoken-8.3.0/tests/rsa/public_rsa_key.der000064400000000000000000000004161046102023000177630ustar 000000000000000 :{GDܫvV*Ȝn@W⥽Q;  "%OY"ĕ].K>~z!U2L=]MQ}+W,0"KyNȌnFLyTZp$D,g GHv}t+ֵ"^gʏq' :0PH1%C0//%\@(7/j씡ߣo"?5"ESo-ݖ<-FWe7 kˊrT>Sujsonwebtoken-8.3.0/tests/rsa/public_rsa_key_pkcs1.pem000064400000000000000000000006521046102023000210750ustar 00000000000000-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAyRE6rHuNR0QbHO3H3Kt2pOKGVhQqGZXInOduQNxXzuKlvQTLUTv4 l4sggh5/CYYi/cvI+SXVT9kPWSKXxJXBXd/4LkvcPuUakBoAkfh+eiFVMh2VrUyW yj3MFl0HTVF9KwRXLAcwkREiS3npThHRyIxuy0ZMeZfxVL5arMhw1SRELB8HoGfG /AtH89BIE9jDBHZ9dLelK9a184zAf8LwoPLxvJb3Il5nncqPcSfKDDodMFBIMc4l QzDKL5gvmiXLXB1AGLm8KBjfE8s3L5xqi+yUod+j8MtvIj812dkS4QMiRVN/by2h 3ZY8LYVGrqZXZTcgn2ujn8uKjXLZVD5TdQIDAQAB -----END RSA PUBLIC KEY----- jsonwebtoken-8.3.0/tests/rsa/public_rsa_key_pkcs8.pem000064400000000000000000000007031046102023000211010ustar 00000000000000-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyRE6rHuNR0QbHO3H3Kt2 pOKGVhQqGZXInOduQNxXzuKlvQTLUTv4l4sggh5/CYYi/cvI+SXVT9kPWSKXxJXB Xd/4LkvcPuUakBoAkfh+eiFVMh2VrUyWyj3MFl0HTVF9KwRXLAcwkREiS3npThHR yIxuy0ZMeZfxVL5arMhw1SRELB8HoGfG/AtH89BIE9jDBHZ9dLelK9a184zAf8Lw oPLxvJb3Il5nncqPcSfKDDodMFBIMc4lQzDKL5gvmiXLXB1AGLm8KBjfE8s3L5xq i+yUod+j8MtvIj812dkS4QMiRVN/by2h3ZY8LYVGrqZXZTcgn2ujn8uKjXLZVD5T dQIDAQAB -----END PUBLIC KEY-----