diff --git a/Cargo.lock b/Cargo.lock index b1cf16ce..97335ce7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,7 +35,7 @@ checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ "getrandom", "once_cell", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -152,7 +152,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", ] [[package]] @@ -168,15 +168,18 @@ dependencies = [ [[package]] name = "autocfg" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.1.0", +] [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "aws-config" @@ -383,7 +386,7 @@ dependencies = [ "percent-encoding", "pin-project", "tokio", - "tokio-util", + "tokio-util 0.6.9", "tracing", ] @@ -464,7 +467,7 @@ checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" dependencies = [ "addr2line", "cc", - "cfg-if 1.0.0", + "cfg-if", "libc", "miniz_oxide", "object", @@ -477,15 +480,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -dependencies = [ - "byteorder", -] - [[package]] name = "base64" version = "0.13.0" @@ -515,11 +509,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "blake2" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94ba84325db59637ffc528bbe8c7f86c02c57cff5c0e2b9b00f9a851f42f309" +checksum = "f08f9f6871a8eacbb960d18db3d077ae6db1f0bc0df3272a78ca09eef8c5a931" dependencies = [ - "digest 0.10.1", + "digest 0.10.3", ] [[package]] @@ -545,9 +539,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" dependencies = [ "generic-array 0.14.5", ] @@ -653,15 +647,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.72" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[package]] name = "cfg-if" @@ -675,7 +663,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01b72a433d0cf2aef113ba70f62634c56fddb0f244e6377185c56a7cadbd8f91" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cipher", "cpufeatures", "zeroize", @@ -741,9 +729,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.0.14" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63edc3f163b3c71ec8aa23f9bd6070f77edbf3d1d198b164afa90ff00e4ec62" +checksum = "e5f1fea81f183005ced9e59cdb01737ef2423956dac5a6d731b06b2ecfaa3467" dependencies = [ "atty", "bitflags", @@ -758,9 +746,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.0.14" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1132dc3944b31c20dd8b906b3a9f0a5d0243e092d59171414969657ac6aa85" +checksum = "5fd1122e63869df2cb309f449da1ad54a7c6dfeb7c7e6ccd8e0825d9eb93bb72" dependencies = [ "heck 0.4.0", "proc-macro-error", @@ -788,14 +776,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" dependencies = [ "time 0.3.7", - "version_check 0.9.4", + "version_check", ] [[package]] name = "core-foundation" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys", "libc", @@ -833,20 +821,11 @@ checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" [[package]] name = "crc32fast" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "crossbeam-channel" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" -dependencies = [ - "crossbeam-utils 0.6.6", + "cfg-if", ] [[package]] @@ -855,37 +834,27 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils 0.8.6", + "cfg-if", + "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b979d76c9fcb84dffc80a73f7290da0f83e4c95773494674cb44b76d13a7a110" +checksum = "4dd435b205a4842da59efd07628f921c096bc1cc0a156835b4fa0bcb9a19bcce" dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils 0.8.6", + "cfg-if", + "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.6.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" dependencies = [ - "cfg-if 0.1.10", - "lazy_static", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" -dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "lazy_static", ] @@ -903,11 +872,12 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d6b536309245c849479fba3da410962a43ed8e51c26b729208ec0ac2798d0" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" dependencies = [ "generic-array 0.14.5", + "typenum", ] [[package]] @@ -992,7 +962,7 @@ version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "num_cpus", ] @@ -1039,13 +1009,12 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ - "block-buffer 0.10.0", + "block-buffer 0.10.2", "crypto-common", - "generic-array 0.14.5", "subtle", ] @@ -1127,7 +1096,7 @@ version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1162,13 +1131,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790b4292c72618abbab50f787a477014fe15634f96291de45672ce46afe122df" dependencies = [ "atomic", - "parking_lot", + "parking_lot 0.11.2", "pear", "serde", "serde_yaml", "tempfile", "uncased", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -1183,7 +1152,7 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crc32fast", "libc", "miniz_oxide", @@ -1213,9 +1182,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.19" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4" +checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" dependencies = [ "futures-channel", "futures-core", @@ -1244,9 +1213,9 @@ checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" [[package]] name = "futures-executor" -version = "0.3.19" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a" +checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" dependencies = [ "futures-core", "futures-task", @@ -1261,7 +1230,7 @@ checksum = "62007592ac46aa7c2b6416f7deb9a8a8f63a01e0f1d6e1787d5630170db2b63e" dependencies = [ "futures-core", "lock_api", - "parking_lot", + "parking_lot 0.11.2", ] [[package]] @@ -1328,7 +1297,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" dependencies = [ "typenum", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -1337,7 +1306,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi", ] @@ -1398,7 +1367,7 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util", + "tokio-util 0.6.9", "tracing", ] @@ -1422,32 +1391,32 @@ dependencies = [ [[package]] name = "hdrhistogram" -version = "6.3.4" +version = "7.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d331ebcdbca4acbefe5da8c3299b2e246f198a8294cc5163354e743398b89d" +checksum = "31672b7011be2c4f7456c4ddbcb40e7e9a4a9fad8efe49a6ebaf5f307d0109c0" dependencies = [ - "base64 0.10.1", + "base64", "byteorder", - "crossbeam-channel 0.3.9", + "crossbeam-channel", "flate2", - "nom 4.2.3", + "nom", "num-traits", ] [[package]] name = "headers" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84c647447a07ca16f5fbd05b633e535cc41a08d2d74ab1e08648df53be9cb89" +checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" dependencies = [ - "base64 0.13.0", + "base64", "bitflags", "bytes 1.1.0", "headers-core", "http", "httpdate", "mime", - "sha-1 0.9.8", + "sha-1 0.10.0", ] [[package]] @@ -1501,11 +1470,11 @@ dependencies = [ [[package]] name = "hmac" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddca131f3e7f2ce2df364b57949a9d47915cfbd35e46cfee355ccebbf794d6a2" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.1", + "digest 0.10.3", ] [[package]] @@ -1615,7 +1584,7 @@ dependencies = [ "http", "hyper", "log", - "rustls 0.20.2", + "rustls 0.20.3", "rustls-native-certs 0.6.1", "tokio", "tokio-rustls 0.23.2", @@ -1656,7 +1625,7 @@ version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" dependencies = [ - "crossbeam-utils 0.8.6", + "crossbeam-utils", "globset", "lazy_static", "log", @@ -1674,7 +1643,7 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "hashbrown", ] @@ -1699,7 +1668,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1729,7 +1698,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f0f7638c1e223529f1bfdc48c8b133b9e0b434094d1d28473161ee48b235f78" dependencies = [ - "nom 7.1.0", + "nom", ] [[package]] @@ -1787,7 +1756,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d8da8f34d086b081c9cc3b57d3bb3b51d16fc06b5c848a188e2f14d58ac2a5" dependencies = [ "async-trait", - "base64 0.13.0", + "base64", "fastrand", "futures-io", "futures-util", @@ -1795,11 +1764,11 @@ dependencies = [ "httpdate", "idna", "mime", - "nom 7.1.0", + "nom", "once_cell", "quoted_printable", "regex", - "rustls 0.20.2", + "rustls 0.20.3", "rustls-pemfile", "serde", "tokio", @@ -1810,15 +1779,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.117" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" +checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94" [[package]] name = "libm" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" +checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" [[package]] name = "linked-hash-map" @@ -1841,7 +1810,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1859,7 +1828,7 @@ dependencies = [ "atty", "clap", "dotenv", - "futures 0.3.19", + "futures 0.3.21", "hyper", "indoc", "mas-config", @@ -1900,8 +1869,12 @@ dependencies = [ "chrono", "elliptic-curve", "figment", + "futures-util", + "http", + "http-body", "indoc", "lettre", + "mas-http", "mas-jose", "p256", "pem-rfc7468", @@ -1915,6 +1888,7 @@ dependencies = [ "sqlx", "thiserror", "tokio", + "tower", "tracing", "url", ] @@ -1980,10 +1954,11 @@ dependencies = [ "serde_json", "serde_urlencoded", "serde_with", - "sha2 0.10.1", + "sha2 0.10.2", "sqlx", "thiserror", "tokio", + "tower", "tracing", "url", "warp", @@ -2001,7 +1976,7 @@ dependencies = [ "hyper-rustls 0.23.0", "opentelemetry", "opentelemetry-http", - "rustls 0.20.2", + "rustls 0.20.3", "serde", "serde_json", "thiserror", @@ -2046,10 +2021,13 @@ dependencies = [ "base64ct", "chrono", "crypto-mac", - "digest 0.10.1", + "digest 0.10.3", "ecdsa", "elliptic-curve", - "hmac 0.12.0", + "futures-util", + "hmac 0.12.1", + "http", + "mas-http", "mas-iana", "p256", "pkcs1", @@ -2061,10 +2039,11 @@ dependencies = [ "serde", "serde_json", "serde_with", - "sha2 0.10.1", + "sha2 0.10.2", "signature", "thiserror", "tokio", + "tower", "url", "zeroize", ] @@ -2162,6 +2141,7 @@ dependencies = [ "sqlx", "thiserror", "tokio", + "tower", "tracing", "url", "warp", @@ -2213,9 +2193,9 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "mime_guess" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" dependencies = [ "mime", "unicase", @@ -2234,14 +2214,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", - "autocfg 1.0.1", + "autocfg 1.1.0", ] [[package]] name = "mio" -version = "0.7.14" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2" dependencies = [ "libc", "log", @@ -2283,16 +2263,6 @@ dependencies = [ "twoway", ] -[[package]] -name = "nom" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -dependencies = [ - "memchr", - "version_check 0.1.5", -] - [[package]] name = "nom" version = "7.1.0" @@ -2301,14 +2271,14 @@ checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" dependencies = [ "memchr", "minimal-lexical", - "version_check 0.9.4", + "version_check", ] [[package]] name = "ntapi" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" dependencies = [ "winapi", ] @@ -2319,7 +2289,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4547ee5541c18742396ae2c895d0717d0f886d8823b8399cdaf7b07d63ad0480" dependencies = [ - "autocfg 0.1.7", + "autocfg 0.1.8", "byteorder", "lazy_static", "libm", @@ -2337,7 +2307,7 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "num-traits", ] @@ -2347,7 +2317,7 @@ version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "num-integer", "num-traits", ] @@ -2358,7 +2328,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "libm", ] @@ -2396,7 +2366,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "sha2 0.10.1", + "sha2 0.10.2", "thiserror", "url", ] @@ -2441,7 +2411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6105e89802af13fdf48c49d7646d3b533a70e536d818aae7e78ba0433d01acb8" dependencies = [ "async-trait", - "crossbeam-channel 0.5.2", + "crossbeam-channel", "dashmap", "fnv", "futures-channel", @@ -2496,7 +2466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1a6ca9de4c8b00aa7f1a153bd76cb263287155cec642680d79d98706f3d28a" dependencies = [ "async-trait", - "futures 0.3.19", + "futures 0.3.21", "futures-util", "http", "opentelemetry", @@ -2573,7 +2543,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core", + "parking_lot_core 0.8.5", +] + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.1", ] [[package]] @@ -2582,7 +2562,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "instant", "libc", "redox_syscall", @@ -2591,10 +2571,23 @@ dependencies = [ ] [[package]] -name = "parse-display" -version = "0.5.3" +name = "parking_lot_core" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "898bf4c2a569dedbfd4e6c3f0bbd0ae825e5b6b0b69bae3e3c1000158689334a" +checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "parse-display" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb10034e2db1d7ec22c3da90743505c07ab1e7483e93df8222c41dadb5937e8" dependencies = [ "once_cell", "parse-display-derive", @@ -2603,9 +2596,9 @@ dependencies = [ [[package]] name = "parse-display-derive" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1779d1e28ab04568223744c2af4aa4e642e67b92c76bdce0929a6d2c36267199" +checksum = "4546f47b5e547d7f9a5cc60d798798aebb013bb930df9297ada0048fd4901558" dependencies = [ "once_cell", "proc-macro2", @@ -2847,7 +2840,7 @@ dependencies = [ "proc-macro2", "quote", "syn", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -2858,7 +2851,7 @@ checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -2879,7 +2872,7 @@ dependencies = [ "proc-macro2", "quote", "syn", - "version_check 0.9.4", + "version_check", "yansi", ] @@ -2959,14 +2952,13 @@ checksum = "3fee2dce59f7a43418e3382c766554c614e06a552d53a8f07ef499ea4b332c0f" [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", - "rand_hc", ] [[package]] @@ -2988,15 +2980,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - [[package]] name = "redox_syscall" version = "0.2.10" @@ -3057,7 +3040,7 @@ version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525" dependencies = [ - "base64 0.13.0", + "base64", "bytes 1.1.0", "encoding_rs", "futures-core", @@ -3074,7 +3057,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rustls 0.20.2", + "rustls 0.20.3", "rustls-native-certs 0.6.1", "rustls-pemfile", "serde", @@ -3122,7 +3105,7 @@ version = "0.5.0" source = "git+https://github.com/RustCrypto/RSA.git#7395997c40b0f2c0bdb362b2998cbf9250408276" dependencies = [ "byteorder", - "digest 0.10.1", + "digest 0.10.3", "num-bigint-dig", "num-integer", "num-iter", @@ -3189,7 +3172,7 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ - "base64 0.13.0", + "base64", "log", "ring", "sct 0.6.1", @@ -3198,9 +3181,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37e5e2290f3e040b594b1a9e04377c2c671f1a1cfd9bfdef82106ac1c113f84" +checksum = "b323592e3164322f5b193dc4302e4e36cd8d37158a712d664efae1a5c2791700" dependencies = [ "log", "ring", @@ -3238,7 +3221,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" dependencies = [ - "base64 0.13.0", + "base64", ] [[package]] @@ -3351,9 +3334,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fed7948b6c68acbb6e20c334f55ad635dc0f75506963de4464289fbd3b051ac" +checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" dependencies = [ "bitflags", "core-foundation", @@ -3364,9 +3347,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a57321bf8bc2362081b2599912d2961fe899c0efadf1b4b2f8d48b3e253bb96c" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" dependencies = [ "core-foundation-sys", "libc", @@ -3374,9 +3357,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" +checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7" [[package]] name = "serde" @@ -3424,9 +3407,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" dependencies = [ "indexmap", "itoa 1.0.1", @@ -3448,11 +3431,11 @@ dependencies = [ [[package]] name = "serde_with" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad6056b4cb69b6e43e3a0f055def223380baecc99da683884f205bf347f7c4b3" +checksum = "ec1e6ec4d8950e5b1e894eac0d360742f3b1407a6078a604a731c4b3f49cefbc" dependencies = [ - "base64 0.13.0", + "base64", "chrono", "hex", "rustversion", @@ -3503,12 +3486,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.9.0", "opaque-debug 0.3.0", ] +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.3", +] + [[package]] name = "sha2" version = "0.9.9" @@ -3516,7 +3510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.9.0", "opaque-debug 0.3.0", @@ -3524,13 +3518,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", - "digest 0.10.1", + "digest 0.10.3", ] [[package]] @@ -3621,7 +3615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4" dependencies = [ "itertools", - "nom 7.1.0", + "nom", "unicode_categories", ] @@ -3643,15 +3637,15 @@ checksum = "518be6f6fff5ca76f985d434f9c37f3662af279642acf730388f271dff7b9016" dependencies = [ "ahash", "atoi", - "base64 0.13.0", + "base64", "bitflags", "byteorder", "bytes 1.1.0", "chrono", "crc", - "crossbeam-channel 0.5.2", + "crossbeam-channel", "crossbeam-queue", - "crossbeam-utils 0.8.6", + "crossbeam-utils", "dirs", "either", "futures-channel", @@ -3668,7 +3662,7 @@ dependencies = [ "md-5", "memchr", "once_cell", - "parking_lot", + "parking_lot 0.11.2", "percent-encoding", "rand", "rustls 0.19.1", @@ -3795,7 +3789,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "fastrand", "libc", "redox_syscall", @@ -3937,9 +3931,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.16.1" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c27a64b625de6d309e8c57716ba93021dccf1b3b5c97edd6d3dd2d2135afc0a" +checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" dependencies = [ "bytes 1.1.0", "libc", @@ -3947,9 +3941,10 @@ dependencies = [ "mio", "num_cpus", "once_cell", - "parking_lot", + "parking_lot 0.12.0", "pin-project-lite", "signal-hook-registry", + "socket2", "tokio-macros", "winapi", ] @@ -3992,7 +3987,7 @@ version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" dependencies = [ - "rustls 0.20.2", + "rustls 0.20.3", "tokio", "webpki 0.22.0", ] @@ -4037,6 +4032,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-util" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64910e1b9c1901aaf5375561e35b9c057d95ff41a44ede043a03e09279eabaf1" +dependencies = [ + "bytes 1.1.0", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + [[package]] name = "tonic" version = "0.6.2" @@ -4045,7 +4054,7 @@ checksum = "ff08f4649d10a70ffa3522ca559031285d8e421d727ac85c60825761818f5d0a" dependencies = [ "async-stream", "async-trait", - "base64 0.13.0", + "base64", "bytes 1.1.0", "futures-core", "futures-util", @@ -4060,7 +4069,7 @@ dependencies = [ "prost-derive", "tokio", "tokio-stream", - "tokio-util", + "tokio-util 0.6.9", "tower", "tower-layer", "tower-service", @@ -4082,9 +4091,9 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5651b5f6860a99bd1adb59dbfe1db8beb433e73709d9032b413a77e2fb7c066a" +checksum = "9a89fd63ad6adf737582df5db40d286574513c69a11dac5214dc3b5603d6713e" dependencies = [ "futures-core", "futures-util", @@ -4095,8 +4104,7 @@ dependencies = [ "rand", "slab", "tokio", - "tokio-stream", - "tokio-util", + "tokio-util 0.7.0", "tower-layer", "tower-service", "tracing", @@ -4104,9 +4112,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03650267ad175b51c47d02ed9547fc7d4ba2c7e5cb76df0bed67edd1825ae297" +checksum = "33ed53e1fd082268841975cb39587fa9fca9a7a30da84f97818ef667c97f2872" dependencies = [ "async-compression", "bitflags", @@ -4119,7 +4127,7 @@ dependencies = [ "iri-string", "pin-project-lite", "tokio", - "tokio-util", + "tokio-util 0.6.9", "tower", "tower-layer", "tower-service", @@ -4144,7 +4152,7 @@ version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -4157,7 +4165,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94571df2eae3ed4353815ea5a90974a594a1792d8782ff2cbcc9392d1101f366" dependencies = [ - "crossbeam-channel 0.5.2", + "crossbeam-channel", "time 0.3.7", "tracing-subscriber", ] @@ -4206,9 +4214,9 @@ dependencies = [ [[package]] name = "tracing-opentelemetry" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff3ebfa73490b6d574ca747819b6bde3e8d006b58861f34cafa89898d91eb9c" +checksum = "a73d0f08e0d227c4c63e6d003804946767f4c4f01f52098b56821ae22e336bfc" dependencies = [ "opentelemetry", "tracing", @@ -4219,9 +4227,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5312f325fe3588e277415f5a6cca1f4ccad0f248c4cd5a4bd33032d7286abc22" +checksum = "74786ce43333fcf51efe947aed9718fbe46d5c7328ec3f1029e818083966d9aa" dependencies = [ "ansi_term", "lazy_static", @@ -4247,7 +4255,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5" dependencies = [ - "base64 0.13.0", + "base64", "byteorder", "bytes 1.1.0", "http", @@ -4298,7 +4306,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0" dependencies = [ - "version_check 0.9.4", + "version_check", ] [[package]] @@ -4357,7 +4365,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ - "version_check 0.9.4", + "version_check", ] [[package]] @@ -4377,9 +4385,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" [[package]] name = "unicode-xid" @@ -4446,12 +4454,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" - [[package]] name = "version_check" version = "0.9.4" @@ -4504,7 +4506,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-tungstenite", - "tokio-util", + "tokio-util 0.6.9", "tower-service", "tracing", ] @@ -4521,7 +4523,7 @@ version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] @@ -4546,7 +4548,7 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "wasm-bindgen", "web-sys", @@ -4589,13 +4591,13 @@ checksum = "1afbab1186833c9b34f64132b80ed4b373ed4eab6f9efa1f55430835200f0a28" dependencies = [ "anyhow", "bytes 1.1.0", - "futures 0.3.19", + "futures 0.3.21", "maplit", "serde", "serde_bser", "thiserror", "tokio", - "tokio-util", + "tokio-util 0.6.9", "winapi", ] @@ -4699,6 +4701,49 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" + +[[package]] +name = "windows_i686_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" + +[[package]] +name = "windows_i686_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" + [[package]] name = "winreg" version = "0.7.0" diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index ea318003..d4be159a 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -35,3 +35,8 @@ pem-rfc7468 = "0.3.1" indoc = "1.0.3" mas-jose = { path = "../jose" } +mas-http = { path = "../http" } +tower = { version = "0.4.11", features = ["util"] } +http = "0.2.6" +http-body = "0.4.4" +futures-util = "0.3.21" diff --git a/crates/config/src/sections/clients.rs b/crates/config/src/sections/clients.rs index 2ac393dd..48326921 100644 --- a/crates/config/src/sections/clients.rs +++ b/crates/config/src/sections/clients.rs @@ -15,11 +15,15 @@ use std::ops::{Deref, DerefMut}; use async_trait::async_trait; -use mas_jose::{JsonWebKeySet, StaticJwksStore}; +use futures_util::future::Either; +use http::Request; +use mas_http::HttpServiceExt; +use mas_jose::{DynamicJwksStore, JsonWebKeySet, StaticJwksStore, VerifyingKeystore}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use thiserror::Error; +use tower::{BoxError, ServiceExt}; use url::Url; use super::ConfigurationSection; @@ -32,13 +36,37 @@ pub enum JwksOrJwksUri { } impl JwksOrJwksUri { - pub fn key_store(&self) -> StaticJwksStore { - let jwks = match self { - Self::Jwks(jwks) => jwks.clone(), - Self::JwksUri(_) => unimplemented!("jwks_uri are not implemented yet"), + pub fn key_store(&self) -> Either { + // Assert that the output is both a VerifyingKeystore and Send + fn assert(t: T) -> T { + t + } + + let inner = match self { + Self::Jwks(jwks) => Either::Left(StaticJwksStore::new(jwks.clone())), + Self::JwksUri(uri) => { + let uri = uri.clone(); + + // TODO: get the client from somewhere else? + let exporter = mas_http::client("fetch-jwks") + .json::() + .map_request(move |_: ()| { + Request::builder() + .method("GET") + // TODO: change the Uri type in config to avoid reparsing here + .uri(uri.to_string()) + .body(http_body::Empty::new()) + .unwrap() + }) + .map_response(http::Response::into_body) + .map_err(BoxError::from) + .boxed_clone(); + + Either::Right(DynamicJwksStore::new(exporter)) + } }; - StaticJwksStore::new(jwks) + assert(inner) } } diff --git a/crates/handlers/Cargo.toml b/crates/handlers/Cargo.toml index d27663fa..bf15f092 100644 --- a/crates/handlers/Cargo.toml +++ b/crates/handlers/Cargo.toml @@ -63,6 +63,7 @@ mas-static-files = { path = "../static-files" } mas-storage = { path = "../storage" } mas-templates = { path = "../templates" } mas-warp-utils = { path = "../warp-utils" } +tower = "0.4.11" [dev-dependencies] indoc = "1.0.3" diff --git a/crates/handlers/src/oauth2/discovery.rs b/crates/handlers/src/oauth2/discovery.rs index 300472f1..132afc0d 100644 --- a/crates/handlers/src/oauth2/discovery.rs +++ b/crates/handlers/src/oauth2/discovery.rs @@ -32,7 +32,7 @@ use warp::{filters::BoxedFilter, Filter, Reply}; #[allow(clippy::too_many_lines)] pub(super) fn filter( - key_store: impl SigningKeystore, + key_store: &impl SigningKeystore, http_config: &HttpConfig, ) -> BoxedFilter<(Box,)> { let builder = UrlBuilder::from(http_config); diff --git a/crates/handlers/src/oauth2/keys.rs b/crates/handlers/src/oauth2/keys.rs index c9dca6b6..044541f0 100644 --- a/crates/handlers/src/oauth2/keys.rs +++ b/crates/handlers/src/oauth2/keys.rs @@ -14,8 +14,9 @@ use std::sync::Arc; -use mas_jose::{ExportJwks, StaticKeystore}; -use mas_warp_utils::{errors::WrapError, filters}; +use mas_jose::StaticKeystore; +use mas_warp_utils::filters; +use tower::{Service, ServiceExt}; use warp::{filters::BoxedFilter, Filter, Rejection, Reply}; pub(super) fn filter(key_store: &Arc) -> BoxedFilter<(Box,)> { @@ -27,7 +28,7 @@ pub(super) fn filter(key_store: &Arc) -> BoxedFilter<(Box) -> Result, Rejection> { - let jwks = key_store.export_jwks().await.wrap_error()?; - + let mut key_store: &StaticKeystore = key_store.as_ref(); + let jwks = key_store.ready().await?.call(()).await?; Ok(Box::new(warp::reply::json(&jwks))) } diff --git a/crates/http/src/ext.rs b/crates/http/src/ext.rs index 348ce47f..d0efe4a9 100644 --- a/crates/http/src/ext.rs +++ b/crates/http/src/ext.rs @@ -12,20 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::layers::{get::Get, json::Json}; +use crate::layers::json::Json; pub trait ServiceExt: Sized { fn json(self) -> Json; - - fn get(self) -> Get; } impl ServiceExt for S { fn json(self) -> Json { Json::new(self) } - - fn get(self) -> Get { - Get::new(self) - } } diff --git a/crates/http/src/layers/client.rs b/crates/http/src/layers/client.rs index 8f66d749..8fac89c4 100644 --- a/crates/http/src/layers/client.rs +++ b/crates/http/src/layers/client.rs @@ -54,14 +54,14 @@ pub type ClientResponse = Response< DecompressionBody::Data, ::Error>>, >; -impl Layer for ClientLayer +impl Layer for ClientLayer where - S: Service, Response = Response> + Clone + Send + 'static, + S: Service, Response = Response, Error = E> + Clone + Send + 'static, ReqBody: http_body::Body + Default + Send + 'static, ResBody: http_body::Body + Sync + Send + 'static, ResBody::Error: std::fmt::Display + 'static, S::Future: Send + 'static, - S::Error: Into, + E: Into, { type Service = BoxCloneService, ClientResponse, BoxError>; diff --git a/crates/http/src/layers/get.rs b/crates/http/src/layers/get.rs deleted file mode 100644 index 67638fa3..00000000 --- a/crates/http/src/layers/get.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2022 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use http::{Request, Uri}; -use tower::{Layer, Service}; - -pub struct Get { - inner: S, -} - -impl Get { - pub const fn new(inner: S) -> Self { - Self { inner } - } -} - -impl Service for Get -where - S: Service>>, -{ - type Error = S::Error; - type Response = S::Response; - type Future = S::Future; - - fn poll_ready( - &mut self, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - self.inner.poll_ready(cx) - } - - fn call(&mut self, req: Uri) -> Self::Future { - let body = http_body::Empty::new(); - let req = Request::builder() - .method("GET") - .uri(req) - .body(body) - .unwrap(); - self.inner.call(req) - } -} - -#[derive(Default, Clone, Copy)] -pub struct GetLayer; - -impl Layer for GetLayer -where - S: Service>>, -{ - type Service = Get; - - fn layer(&self, inner: S) -> Self::Service { - Get::new(inner) - } -} diff --git a/crates/http/src/layers/json.rs b/crates/http/src/layers/json.rs index 47ceccca..db620f86 100644 --- a/crates/http/src/layers/json.rs +++ b/crates/http/src/layers/json.rs @@ -53,6 +53,7 @@ impl Error { } } +#[derive(Clone)] pub struct Json { inner: S, _t: PhantomData, diff --git a/crates/http/src/layers/mod.rs b/crates/http/src/layers/mod.rs index 23856b5c..2fd44508 100644 --- a/crates/http/src/layers/mod.rs +++ b/crates/http/src/layers/mod.rs @@ -13,7 +13,6 @@ // limitations under the License. pub(crate) mod client; -pub(crate) mod get; pub(crate) mod json; pub(crate) mod server; pub(crate) mod trace; diff --git a/crates/http/src/layers/server.rs b/crates/http/src/layers/server.rs index 1528564b..1bebfec0 100644 --- a/crates/http/src/layers/server.rs +++ b/crates/http/src/layers/server.rs @@ -29,15 +29,16 @@ pub struct ServerLayer { _t: PhantomData, } -impl Layer for ServerLayer +impl Layer for ServerLayer where - S: Service, Response = Response> + Clone + Send + 'static, + S: Service, Response = Response, Error = E> + Clone + Send + 'static, ReqBody: http_body::Body + 'static, ResBody: http_body::Body + Sync + Send + 'static, ResBody::Error: std::fmt::Display + 'static, S::Future: Send + 'static, - S::Error: Into, + E: Into, { + #[allow(clippy::type_complexity)] type Service = BoxCloneService< Request, Response>>, diff --git a/crates/jose/Cargo.toml b/crates/jose/Cargo.toml index 1214cc3f..5e17f87b 100644 --- a/crates/jose/Cargo.toml +++ b/crates/jose/Cargo.toml @@ -14,7 +14,9 @@ crypto-mac = { version = "0.11.1", features = ["std"] } digest = "0.10.1" ecdsa = { version = "0.13.4", features = ["sign", "verify", "pem", "pkcs8"] } elliptic-curve = { version = "0.11.12", features = ["ecdh", "pem"] } +futures-util = "0.3.21" hmac = "0.12.0" +http = "0.2.6" p256 = { version = "0.10.1", features = ["ecdsa", "pem", "pkcs8"] } pkcs1 = { version = "0.3.3", features = ["pem", "pkcs8"] } pkcs8 = { version = "0.8.0", features = ["pem"] } @@ -29,7 +31,11 @@ sha2 = "0.10.1" signature = "1.4.0" thiserror = "1.0.30" tokio = { version = "1.16.1", features = ["macros", "rt", "sync"] } +tower = "0.4.11" url = { version = "2.2.2", features = ["serde"] } zeroize = "1.4.3" mas-iana = { path = "../iana" } + +[dev-dependencies] +mas-http = { path = "../http" } diff --git a/crates/jose/src/jwt.rs b/crates/jose/src/jwt.rs index 0b3fdd5f..ad21a822 100644 --- a/crates/jose/src/jwt.rs +++ b/crates/jose/src/jwt.rs @@ -30,7 +30,7 @@ use crate::{jwk::JsonWebKey, SigningKeystore, VerifyingKeystore}; #[serde_as] #[skip_serializing_none] -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct JwtHeader { alg: JsonWebSignatureAlg, @@ -163,7 +163,7 @@ where Ok(format!("{}.{}", header, payload)) } - pub async fn sign(&self, store: S) -> anyhow::Result { + pub async fn sign(&self, store: &S) -> anyhow::Result { let payload = self.serialize()?; let signature = store.sign(&self.header, payload.as_bytes()).await?; Ok(JsonWebTokenParts { payload, signature }) @@ -205,22 +205,24 @@ impl JsonWebTokenParts { Ok(decoded) } - pub async fn verify( + pub fn verify( &self, decoded: &DecodedJsonWebToken, - store: S, - ) -> anyhow::Result<()> { - store - .verify(&decoded.header, self.payload.as_bytes(), &self.signature) - .await?; - - Ok(()) + store: &S, + ) -> S::Future + where + S::Error: std::error::Error + Send + Sync + 'static, + { + store.verify(&decoded.header, self.payload.as_bytes(), &self.signature) } pub async fn decode_and_verify( &self, - store: S, - ) -> anyhow::Result> { + store: &S, + ) -> anyhow::Result> + where + S::Error: std::error::Error + Send + Sync + 'static, + { let decoded = self.decode()?; self.verify(&decoded, store).await?; Ok(decoded) diff --git a/crates/jose/src/keystore/jwks.rs b/crates/jose/src/keystore/jwks.rs deleted file mode 100644 index 10ba1546..00000000 --- a/crates/jose/src/keystore/jwks.rs +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2022 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::collections::HashMap; - -use anyhow::bail; -use async_trait::async_trait; -use chrono::{DateTime, Duration, Utc}; -use digest::Digest; -use mas_iana::jose::{JsonWebKeyType, JsonWebSignatureAlg}; -use rsa::{PublicKey, RsaPublicKey}; -use sha2::{Sha256, Sha384, Sha512}; -use signature::{Signature, Verifier}; -use tokio::sync::RwLock; - -use crate::{ExportJwks, JsonWebKeySet, JwtHeader, VerifyingKeystore}; - -pub struct StaticJwksStore { - key_set: JsonWebKeySet, - index: HashMap<(JsonWebKeyType, String), usize>, -} - -impl StaticJwksStore { - #[must_use] - pub fn new(key_set: JsonWebKeySet) -> Self { - let index = key_set - .iter() - .enumerate() - .filter_map(|(index, key)| { - let kid = key.kid()?.to_string(); - let kty = key.kty(); - - Some(((kty, kid), index)) - }) - .collect(); - - Self { key_set, index } - } - - fn find_rsa_key(&self, kid: String) -> anyhow::Result { - let index = *self - .index - .get(&(JsonWebKeyType::Rsa, kid)) - .ok_or_else(|| anyhow::anyhow!("key not found"))?; - - let key = self - .key_set - .get(index) - .ok_or_else(|| anyhow::anyhow!("invalid index"))?; - - let key = key.params().clone().try_into()?; - - Ok(key) - } - - fn find_ecdsa_key(&self, kid: String) -> anyhow::Result> { - let index = *self - .index - .get(&(JsonWebKeyType::Ec, kid)) - .ok_or_else(|| anyhow::anyhow!("key not found"))?; - - let key = self - .key_set - .get(index) - .ok_or_else(|| anyhow::anyhow!("invalid index"))?; - - let key = key.params().clone().try_into()?; - - Ok(key) - } -} - -#[async_trait] -impl VerifyingKeystore for &StaticJwksStore { - async fn verify( - self, - header: &JwtHeader, - payload: &[u8], - signature: &[u8], - ) -> anyhow::Result<()> { - let kid = header - .kid() - .ok_or_else(|| anyhow::anyhow!("missing kid"))? - .to_string(); - match header.alg() { - JsonWebSignatureAlg::Rs256 => { - let key = self.find_rsa_key(kid)?; - - let digest = { - let mut digest = Sha256::new(); - digest.update(&payload); - digest.finalize() - }; - - key.verify( - rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_256)), - &digest, - signature, - )?; - } - - JsonWebSignatureAlg::Rs384 => { - let key = self.find_rsa_key(kid)?; - - let digest = { - let mut digest = Sha384::new(); - digest.update(&payload); - digest.finalize() - }; - - key.verify( - rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_384)), - &digest, - signature, - )?; - } - - JsonWebSignatureAlg::Rs512 => { - let key = self.find_rsa_key(kid)?; - - let digest = { - let mut digest = Sha512::new(); - digest.update(&payload); - digest.finalize() - }; - - key.verify( - rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_512)), - &digest, - signature, - )?; - } - - JsonWebSignatureAlg::Es256 => { - let key = self.find_ecdsa_key(kid)?; - - let signature = ecdsa::Signature::from_bytes(signature)?; - - key.verify(payload, &signature)?; - } - - _ => bail!("unsupported algorithm"), - }; - - Ok(()) - } -} - -enum RemoteKeySet { - Pending, - Errored { - at: DateTime, - error: anyhow::Error, - }, - Fulfilled { - at: DateTime, - store: StaticJwksStore, - }, -} - -impl Default for RemoteKeySet { - fn default() -> Self { - Self::Pending - } -} - -impl RemoteKeySet { - fn fullfill(&mut self, key_set: JsonWebKeySet) { - *self = Self::Fulfilled { - at: Utc::now(), - store: StaticJwksStore::new(key_set), - } - } - - fn error(&mut self, error: anyhow::Error) { - *self = Self::Errored { - at: Utc::now(), - error, - } - } - - fn should_refresh(&self) -> bool { - let now = Utc::now(); - match self { - Self::Pending => true, - Self::Errored { at, .. } if *at - now > Duration::minutes(5) => true, - Self::Fulfilled { at, .. } if *at - now > Duration::hours(1) => true, - _ => false, - } - } - - fn should_force_refresh(&self) -> bool { - let now = Utc::now(); - match self { - Self::Pending => true, - Self::Errored { at, .. } | Self::Fulfilled { at, .. } - if *at - now > Duration::minutes(5) => - { - true - } - _ => false, - } - } -} - -pub struct JwksStore -where - T: ExportJwks, -{ - exporter: T, - cache: RwLock, -} - -impl JwksStore { - pub fn new(exporter: T) -> Self { - Self { - exporter, - cache: RwLock::default(), - } - } - - async fn should_refresh(&self) -> bool { - let cache = self.cache.read().await; - cache.should_refresh() - } - - async fn refresh(&self) { - let mut cache = self.cache.write().await; - - if cache.should_force_refresh() { - let jwks = self.exporter.export_jwks().await; - - match jwks { - Ok(jwks) => cache.fullfill(jwks), - Err(err) => cache.error(err), - } - } - } -} - -#[async_trait] -impl VerifyingKeystore for &JwksStore { - async fn verify( - self, - header: &JwtHeader, - payload: &[u8], - signature: &[u8], - ) -> anyhow::Result<()> { - if self.should_refresh().await { - self.refresh().await; - } - - let cache = self.cache.read().await; - // TODO: we could bubble up the underlying error here - let store = match &*cache { - RemoteKeySet::Pending => bail!("inconsistent cache state"), - RemoteKeySet::Errored { error, .. } => bail!("cache in error state {}", error), - RemoteKeySet::Fulfilled { store, .. } => store, - }; - - store.verify(header, payload, signature).await?; - - Ok(()) - } -} diff --git a/crates/jose/src/keystore/jwks/dynamic_store.rs b/crates/jose/src/keystore/jwks/dynamic_store.rs new file mode 100644 index 00000000..c613be0f --- /dev/null +++ b/crates/jose/src/keystore/jwks/dynamic_store.rs @@ -0,0 +1,160 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::Arc; + +use chrono::{DateTime, Duration, Utc}; +use futures_util::future::BoxFuture; +use thiserror::Error; +use tokio::sync::RwLock; +use tower::{ + util::{BoxCloneService, ServiceExt}, + BoxError, Service, +}; + +use super::StaticJwksStore; +use crate::{JsonWebKeySet, JwtHeader, VerifyingKeystore}; + +#[derive(Debug, Error)] +pub enum Error { + #[error("cache in inconsistent state")] + InconsistentCache, + + #[error(transparent)] + Cached(Arc), + + #[error("todo")] + Todo, + + #[error(transparent)] + Verification(#[from] super::static_store::Error), +} + +enum State { + Pending, + Errored { + at: DateTime, + error: E, + }, + Fulfilled { + at: DateTime, + store: StaticJwksStore, + }, +} + +impl Default for State { + fn default() -> Self { + Self::Pending + } +} + +impl State { + fn fullfill(&mut self, key_set: JsonWebKeySet) { + *self = Self::Fulfilled { + at: Utc::now(), + store: StaticJwksStore::new(key_set), + } + } + + fn error(&mut self, error: E) { + *self = Self::Errored { + at: Utc::now(), + error, + } + } + + fn should_refresh(&self) -> bool { + let now = Utc::now(); + match self { + Self::Pending => true, + Self::Errored { at, .. } if *at - now > Duration::minutes(5) => true, + Self::Fulfilled { at, .. } if *at - now > Duration::hours(1) => true, + _ => false, + } + } + + fn should_force_refresh(&self) -> bool { + let now = Utc::now(); + match self { + Self::Pending => true, + Self::Errored { at, .. } | Self::Fulfilled { at, .. } + if *at - now > Duration::minutes(5) => + { + true + } + _ => false, + } + } +} + +#[derive(Clone)] +pub struct DynamicJwksStore { + exporter: BoxCloneService<(), JsonWebKeySet, BoxError>, + cache: Arc>>>, +} + +impl DynamicJwksStore { + pub fn new(exporter: T) -> Self + where + T: Service<(), Response = JsonWebKeySet, Error = BoxError> + Send + Clone + 'static, + T::Future: Send, + { + Self { + exporter: exporter.boxed_clone(), + cache: Arc::default(), + } + } +} + +impl VerifyingKeystore for DynamicJwksStore { + type Error = Error; + type Future = BoxFuture<'static, Result<(), Self::Error>>; + + fn verify(&self, header: &JwtHeader, payload: &[u8], signature: &[u8]) -> Self::Future { + let cache = self.cache.clone(); + let exporter = self.exporter.clone(); + let header = header.clone(); + let payload = payload.to_owned(); + let signature = signature.to_owned(); + + let fut = async move { + if cache.read().await.should_refresh() { + let mut cache = cache.write().await; + + if cache.should_force_refresh() { + let jwks = async move { exporter.ready_oneshot().await?.call(()).await }.await; + + match jwks { + Ok(jwks) => cache.fullfill(jwks), + Err(err) => cache.error(Arc::new(err)), + } + } + } + + let cache = cache.read().await; + // TODO: we could bubble up the underlying error here + let store = match &*cache { + State::Pending => return Err(Error::InconsistentCache), + State::Errored { error, .. } => return Err(Error::Cached(error.clone())), + State::Fulfilled { store, .. } => store, + }; + + store.verify(&header, &payload, &signature).await?; + + Ok(()) + }; + + Box::pin(fut) + } +} diff --git a/crates/jose/src/keystore/jwks/mod.rs b/crates/jose/src/keystore/jwks/mod.rs new file mode 100644 index 00000000..93849d86 --- /dev/null +++ b/crates/jose/src/keystore/jwks/mod.rs @@ -0,0 +1,18 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod dynamic_store; +mod static_store; + +pub use self::{dynamic_store::DynamicJwksStore, static_store::StaticJwksStore}; diff --git a/crates/jose/src/keystore/jwks/static_store.rs b/crates/jose/src/keystore/jwks/static_store.rs new file mode 100644 index 00000000..d6adfcf0 --- /dev/null +++ b/crates/jose/src/keystore/jwks/static_store.rs @@ -0,0 +1,196 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{collections::HashMap, future::Ready}; + +use digest::Digest; +use mas_iana::jose::{JsonWebKeyType, JsonWebSignatureAlg}; +use rsa::{PublicKey, RsaPublicKey}; +use sha2::{Sha256, Sha384, Sha512}; +use signature::{Signature, Verifier}; +use thiserror::Error; + +use crate::{JsonWebKeySet, JwtHeader, VerifyingKeystore}; + +#[derive(Debug, Error)] +pub enum Error { + #[error("key not found")] + KeyNotFound, + + #[error("invalid index")] + InvalidIndex, + + #[error(r#"missing "kid" field in header"#)] + MissingKid, + + #[error(transparent)] + Rsa(#[from] rsa::errors::Error), + + #[error("unsupported algorithm {alg}")] + UnsupportedAlgorithm { alg: JsonWebSignatureAlg }, + + #[error(transparent)] + Signature(#[from] signature::Error), + + #[error("invalid {kty} key {kid}")] + InvalidKey { + kty: JsonWebKeyType, + kid: String, + source: anyhow::Error, + }, +} + +pub struct StaticJwksStore { + key_set: JsonWebKeySet, + index: HashMap<(JsonWebKeyType, String), usize>, +} + +impl StaticJwksStore { + #[must_use] + pub fn new(key_set: JsonWebKeySet) -> Self { + let index = key_set + .iter() + .enumerate() + .filter_map(|(index, key)| { + let kid = key.kid()?.to_string(); + let kty = key.kty(); + + Some(((kty, kid), index)) + }) + .collect(); + + Self { key_set, index } + } + + fn find_rsa_key(&self, kid: String) -> Result { + let index = *self + .index + .get(&(JsonWebKeyType::Rsa, kid.clone())) + .ok_or(Error::KeyNotFound)?; + + let key = self.key_set.get(index).ok_or(Error::InvalidIndex)?; + + let key = key + .params() + .clone() + .try_into() + .map_err(|source| Error::InvalidKey { + kty: JsonWebKeyType::Rsa, + kid, + source, + })?; + + Ok(key) + } + + fn find_ecdsa_key(&self, kid: String) -> Result, Error> { + let index = *self + .index + .get(&(JsonWebKeyType::Ec, kid.clone())) + .ok_or(Error::KeyNotFound)?; + + let key = self.key_set.get(index).ok_or(Error::InvalidIndex)?; + + let key = key + .params() + .clone() + .try_into() + .map_err(|source| Error::InvalidKey { + kty: JsonWebKeyType::Ec, + kid, + source, + })?; + + Ok(key) + } + + fn verify_sync( + &self, + header: &JwtHeader, + payload: &[u8], + signature: &[u8], + ) -> Result<(), Error> { + let kid = header.kid().ok_or(Error::MissingKid)?.to_string(); + match header.alg() { + JsonWebSignatureAlg::Rs256 => { + let key = self.find_rsa_key(kid)?; + + let digest = { + let mut digest = Sha256::new(); + digest.update(&payload); + digest.finalize() + }; + + key.verify( + rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_256)), + &digest, + signature, + )?; + } + + JsonWebSignatureAlg::Rs384 => { + let key = self.find_rsa_key(kid)?; + + let digest = { + let mut digest = Sha384::new(); + digest.update(&payload); + digest.finalize() + }; + + key.verify( + rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_384)), + &digest, + signature, + )?; + } + + JsonWebSignatureAlg::Rs512 => { + let key = self.find_rsa_key(kid)?; + + let digest = { + let mut digest = Sha512::new(); + digest.update(&payload); + digest.finalize() + }; + + key.verify( + rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_512)), + &digest, + signature, + )?; + } + + JsonWebSignatureAlg::Es256 => { + let key = self.find_ecdsa_key(kid)?; + + let signature = ecdsa::Signature::from_bytes(signature)?; + + key.verify(payload, &signature)?; + } + + alg => return Err(Error::UnsupportedAlgorithm { alg }), + }; + + Ok(()) + } +} + +impl VerifyingKeystore for StaticJwksStore { + type Error = Error; + type Future = Ready>; + + fn verify(&self, header: &JwtHeader, payload: &[u8], signature: &[u8]) -> Self::Future { + std::future::ready(self.verify_sync(header, payload, signature)) + } +} diff --git a/crates/jose/src/keystore/mod.rs b/crates/jose/src/keystore/mod.rs index 350e40a8..d030af50 100644 --- a/crates/jose/src/keystore/mod.rs +++ b/crates/jose/src/keystore/mod.rs @@ -18,8 +18,8 @@ mod static_keystore; mod traits; pub use self::{ - jwks::{JwksStore, StaticJwksStore}, + jwks::{DynamicJwksStore, StaticJwksStore}, shared_secret::SharedSecret, static_keystore::StaticKeystore, - traits::{ExportJwks, SigningKeystore, VerifyingKeystore}, + traits::{SigningKeystore, VerifyingKeystore}, }; diff --git a/crates/jose/src/keystore/shared_secret.rs b/crates/jose/src/keystore/shared_secret.rs index e045d3d3..c24441b2 100644 --- a/crates/jose/src/keystore/shared_secret.rs +++ b/crates/jose/src/keystore/shared_secret.rs @@ -12,17 +12,31 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::HashSet; +use std::{collections::HashSet, future::Ready}; use anyhow::bail; use async_trait::async_trait; +use digest::{InvalidLength, MacError}; use hmac::{Hmac, Mac}; use mas_iana::jose::JsonWebSignatureAlg; use sha2::{Sha256, Sha384, Sha512}; +use thiserror::Error; use super::{SigningKeystore, VerifyingKeystore}; use crate::JwtHeader; +#[derive(Debug, Error)] +pub enum Error { + #[error("invalid key")] + InvalidKey(#[from] InvalidLength), + + #[error("unsupported algorithm {alg}")] + UnsupportedAlgorithm { alg: JsonWebSignatureAlg }, + + #[error("signature verification failed")] + Verification(#[from] MacError), +} + pub struct SharedSecret<'a> { inner: &'a [u8], } @@ -33,11 +47,42 @@ impl<'a> SharedSecret<'a> { inner: source.as_ref(), } } + + fn verify_sync( + &self, + header: &JwtHeader, + payload: &[u8], + signature: &[u8], + ) -> Result<(), Error> { + match header.alg() { + JsonWebSignatureAlg::Hs256 => { + let mut mac = Hmac::::new_from_slice(self.inner)?; + mac.update(payload); + mac.verify(signature.into())?; + } + + JsonWebSignatureAlg::Hs384 => { + let mut mac = Hmac::::new_from_slice(self.inner)?; + mac.update(payload); + mac.verify(signature.into())?; + } + + JsonWebSignatureAlg::Hs512 => { + let mut mac = Hmac::::new_from_slice(self.inner)?; + mac.update(payload); + mac.verify(signature.into())?; + } + + alg => return Err(Error::UnsupportedAlgorithm { alg }), + }; + + Ok(()) + } } #[async_trait] -impl<'a> SigningKeystore for &SharedSecret<'a> { - fn supported_algorithms(self) -> HashSet { +impl<'a> SigningKeystore for SharedSecret<'a> { + fn supported_algorithms(&self) -> HashSet { let mut algorithms = HashSet::with_capacity(3); algorithms.insert(JsonWebSignatureAlg::Hs256); @@ -47,7 +92,7 @@ impl<'a> SigningKeystore for &SharedSecret<'a> { algorithms } - async fn prepare_header(self, alg: JsonWebSignatureAlg) -> anyhow::Result { + async fn prepare_header(&self, alg: JsonWebSignatureAlg) -> anyhow::Result { if !matches!( alg, JsonWebSignatureAlg::Hs256 | JsonWebSignatureAlg::Hs384 | JsonWebSignatureAlg::Hs512, @@ -58,7 +103,7 @@ impl<'a> SigningKeystore for &SharedSecret<'a> { Ok(JwtHeader::new(alg)) } - async fn sign(self, header: &JwtHeader, msg: &[u8]) -> anyhow::Result> { + async fn sign(&self, header: &JwtHeader, msg: &[u8]) -> anyhow::Result> { // TODO: do the signing in a blocking task // TODO: should we bail out if the key is too small? let signature = match header.alg() { @@ -87,38 +132,12 @@ impl<'a> SigningKeystore for &SharedSecret<'a> { } } -#[async_trait] -impl<'a> VerifyingKeystore for &SharedSecret<'a> { - async fn verify( - self, - header: &JwtHeader, - payload: &[u8], - signature: &[u8], - ) -> anyhow::Result<()> { - // TODO: do the verification in a blocking task - match header.alg() { - JsonWebSignatureAlg::Hs256 => { - let mut mac = Hmac::::new_from_slice(self.inner)?; - mac.update(payload); - mac.verify(signature.try_into()?)?; - } +impl<'a> VerifyingKeystore for SharedSecret<'a> { + type Error = Error; + type Future = Ready>; - JsonWebSignatureAlg::Hs384 => { - let mut mac = Hmac::::new_from_slice(self.inner)?; - mac.update(payload); - mac.verify(signature.try_into()?)?; - } - - JsonWebSignatureAlg::Hs512 => { - let mut mac = Hmac::::new_from_slice(self.inner)?; - mac.update(payload); - mac.verify(signature.try_into()?)?; - } - - _ => bail!("unsupported algorithm"), - }; - - Ok(()) + fn verify(&self, header: &JwtHeader, payload: &[u8], signature: &[u8]) -> Self::Future { + std::future::ready(self.verify_sync(header, payload, signature)) } } diff --git a/crates/jose/src/keystore/static_keystore.rs b/crates/jose/src/keystore/static_keystore.rs index ea25b510..ab011cff 100644 --- a/crates/jose/src/keystore/static_keystore.rs +++ b/crates/jose/src/keystore/static_keystore.rs @@ -12,7 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::{HashMap, HashSet}; +use std::{ + collections::{HashMap, HashSet}, + convert::Infallible, + future::Ready, + task::Poll, +}; use anyhow::bail; use async_trait::async_trait; @@ -26,8 +31,9 @@ use pkcs8::{DecodePrivateKey, EncodePublicKey}; use rsa::{PublicKey as _, RsaPrivateKey, RsaPublicKey}; use sha2::{Sha256, Sha384, Sha512}; use signature::{Signature, Signer, Verifier}; +use tower::Service; -use super::{ExportJwks, SigningKeystore, VerifyingKeystore}; +use super::{SigningKeystore, VerifyingKeystore}; use crate::{JsonWebKey, JsonWebKeySet, JwtHeader}; // Generate with @@ -123,135 +129,9 @@ impl StaticKeystore { self.es256_keys.insert(kid, key); Ok(()) } -} -#[async_trait] -impl SigningKeystore for &StaticKeystore { - fn supported_algorithms(self) -> HashSet { - let has_rsa = !self.rsa_keys.is_empty(); - let has_es256 = !self.es256_keys.is_empty(); - - let capacity = (if has_rsa { 3 } else { 0 }) + (if has_es256 { 1 } else { 0 }); - let mut algorithms = HashSet::with_capacity(capacity); - - if has_rsa { - algorithms.insert(JsonWebSignatureAlg::Rs256); - algorithms.insert(JsonWebSignatureAlg::Rs384); - algorithms.insert(JsonWebSignatureAlg::Rs512); - } - - if has_es256 { - algorithms.insert(JsonWebSignatureAlg::Es256); - } - - algorithms - } - - async fn prepare_header(self, alg: JsonWebSignatureAlg) -> anyhow::Result { - let header = JwtHeader::new(alg); - - let kid = match alg { - JsonWebSignatureAlg::Rs256 - | JsonWebSignatureAlg::Rs384 - | JsonWebSignatureAlg::Rs512 => self - .rsa_keys - .keys() - .next() - .ok_or_else(|| anyhow::anyhow!("no RSA keys in keystore"))?, - JsonWebSignatureAlg::Es256 => self - .es256_keys - .keys() - .next() - .ok_or_else(|| anyhow::anyhow!("no ECDSA keys in keystore"))?, - _ => bail!("unsupported algorithm"), - }; - - Ok(header.with_kid(kid)) - } - - async fn sign(self, header: &JwtHeader, msg: &[u8]) -> anyhow::Result> { - let kid = header - .kid() - .ok_or_else(|| anyhow::anyhow!("missing kid from the JWT header"))?; - - // TODO: do the signing in a blocking task - let signature = match header.alg() { - JsonWebSignatureAlg::Rs256 => { - let key = self - .rsa_keys - .get(kid) - .ok_or_else(|| anyhow::anyhow!("RSA key not found in key store"))?; - - let digest = { - let mut digest = Sha256::new(); - digest.update(&msg); - digest.finalize() - }; - - key.sign( - rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_256)), - &digest, - )? - } - - JsonWebSignatureAlg::Rs384 => { - let key = self - .rsa_keys - .get(kid) - .ok_or_else(|| anyhow::anyhow!("RSA key not found in key store"))?; - - let digest = { - let mut digest = Sha384::new(); - digest.update(&msg); - digest.finalize() - }; - - key.sign( - rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_384)), - &digest, - )? - } - - JsonWebSignatureAlg::Rs512 => { - let key = self - .rsa_keys - .get(kid) - .ok_or_else(|| anyhow::anyhow!("RSA key not found in key store"))?; - - let digest = { - let mut digest = Sha512::new(); - digest.update(&msg); - digest.finalize() - }; - - key.sign( - rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_512)), - &digest, - )? - } - - JsonWebSignatureAlg::Es256 => { - let key = self - .es256_keys - .get(kid) - .ok_or_else(|| anyhow::anyhow!("ECDSA key not found in key store"))?; - - let signature = key.try_sign(msg)?; - let signature: &[u8] = signature.as_ref(); - signature.to_vec() - } - - _ => bail!("Unsupported algorithm"), - }; - - Ok(signature) - } -} - -#[async_trait] -impl VerifyingKeystore for &StaticKeystore { - async fn verify( - self, + fn verify_sync( + &self, header: &JwtHeader, payload: &[u8], signature: &[u8], @@ -344,8 +224,147 @@ impl VerifyingKeystore for &StaticKeystore { } #[async_trait] -impl ExportJwks for StaticKeystore { - async fn export_jwks(&self) -> anyhow::Result { +impl SigningKeystore for StaticKeystore { + fn supported_algorithms(&self) -> HashSet { + let has_rsa = !self.rsa_keys.is_empty(); + let has_es256 = !self.es256_keys.is_empty(); + + let capacity = (if has_rsa { 3 } else { 0 }) + (if has_es256 { 1 } else { 0 }); + let mut algorithms = HashSet::with_capacity(capacity); + + if has_rsa { + algorithms.insert(JsonWebSignatureAlg::Rs256); + algorithms.insert(JsonWebSignatureAlg::Rs384); + algorithms.insert(JsonWebSignatureAlg::Rs512); + } + + if has_es256 { + algorithms.insert(JsonWebSignatureAlg::Es256); + } + + algorithms + } + + async fn prepare_header(&self, alg: JsonWebSignatureAlg) -> anyhow::Result { + let header = JwtHeader::new(alg); + + let kid = match alg { + JsonWebSignatureAlg::Rs256 + | JsonWebSignatureAlg::Rs384 + | JsonWebSignatureAlg::Rs512 => self + .rsa_keys + .keys() + .next() + .ok_or_else(|| anyhow::anyhow!("no RSA keys in keystore"))?, + JsonWebSignatureAlg::Es256 => self + .es256_keys + .keys() + .next() + .ok_or_else(|| anyhow::anyhow!("no ECDSA keys in keystore"))?, + _ => bail!("unsupported algorithm"), + }; + + Ok(header.with_kid(kid)) + } + + async fn sign(&self, header: &JwtHeader, msg: &[u8]) -> anyhow::Result> { + let kid = header + .kid() + .ok_or_else(|| anyhow::anyhow!("missing kid from the JWT header"))?; + + // TODO: do the signing in a blocking task + let signature = match header.alg() { + JsonWebSignatureAlg::Rs256 => { + let key = self + .rsa_keys + .get(kid) + .ok_or_else(|| anyhow::anyhow!("RSA key not found in key store"))?; + + let digest = { + let mut digest = Sha256::new(); + digest.update(&msg); + digest.finalize() + }; + + key.sign( + rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_256)), + &digest, + )? + } + + JsonWebSignatureAlg::Rs384 => { + let key = self + .rsa_keys + .get(kid) + .ok_or_else(|| anyhow::anyhow!("RSA key not found in key store"))?; + + let digest = { + let mut digest = Sha384::new(); + digest.update(&msg); + digest.finalize() + }; + + key.sign( + rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_384)), + &digest, + )? + } + + JsonWebSignatureAlg::Rs512 => { + let key = self + .rsa_keys + .get(kid) + .ok_or_else(|| anyhow::anyhow!("RSA key not found in key store"))?; + + let digest = { + let mut digest = Sha512::new(); + digest.update(&msg); + digest.finalize() + }; + + key.sign( + rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_512)), + &digest, + )? + } + + JsonWebSignatureAlg::Es256 => { + let key = self + .es256_keys + .get(kid) + .ok_or_else(|| anyhow::anyhow!("ECDSA key not found in key store"))?; + + let signature = key.try_sign(msg)?; + let signature: &[u8] = signature.as_ref(); + signature.to_vec() + } + + _ => bail!("Unsupported algorithm"), + }; + + Ok(signature) + } +} + +impl VerifyingKeystore for StaticKeystore { + type Error = anyhow::Error; + type Future = Ready>; + + fn verify(&self, header: &JwtHeader, msg: &[u8], signature: &[u8]) -> Self::Future { + std::future::ready(self.verify_sync(header, msg, signature)) + } +} + +impl Service<()> for &StaticKeystore { + type Future = Ready>; + type Response = JsonWebKeySet; + type Error = Infallible; + + fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _req: ()) -> Self::Future { let rsa = self.rsa_keys.iter().map(|(kid, key)| { let pubkey = RsaPublicKey::from(key); JsonWebKey::new(pubkey.into()) @@ -362,7 +381,7 @@ impl ExportJwks for StaticKeystore { }); let keys = rsa.chain(es256).collect(); - Ok(JsonWebKeySet::new(keys)) + std::future::ready(Ok(JsonWebKeySet::new(keys))) } } diff --git a/crates/jose/src/keystore/traits.rs b/crates/jose/src/keystore/traits.rs index fc4b67ef..5e75a392 100644 --- a/crates/jose/src/keystore/traits.rs +++ b/crates/jose/src/keystore/traits.rs @@ -12,28 +12,78 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::HashSet; +use std::{collections::HashSet, future::Future, sync::Arc}; use async_trait::async_trait; +use futures_util::{ + future::{Either, MapErr}, + TryFutureExt, +}; use mas_iana::jose::JsonWebSignatureAlg; +use thiserror::Error; -use crate::{JsonWebKeySet, JwtHeader}; +use crate::JwtHeader; #[async_trait] pub trait SigningKeystore { - fn supported_algorithms(self) -> HashSet; + fn supported_algorithms(&self) -> HashSet; - async fn prepare_header(self, alg: JsonWebSignatureAlg) -> anyhow::Result; + async fn prepare_header(&self, alg: JsonWebSignatureAlg) -> anyhow::Result; - async fn sign(self, header: &JwtHeader, msg: &[u8]) -> anyhow::Result>; + async fn sign(&self, header: &JwtHeader, msg: &[u8]) -> anyhow::Result>; } -#[async_trait] pub trait VerifyingKeystore { - async fn verify(self, header: &JwtHeader, msg: &[u8], signature: &[u8]) -> anyhow::Result<()>; + type Error; + type Future: Future>; + + fn verify(&self, header: &JwtHeader, msg: &[u8], signature: &[u8]) -> Self::Future; } -#[async_trait] -pub trait ExportJwks { - async fn export_jwks(&self) -> anyhow::Result; +#[derive(Debug, Error)] +pub enum EitherError { + #[error(transparent)] + Left(A), + #[error(transparent)] + Right(B), +} + +impl VerifyingKeystore for Either +where + L: VerifyingKeystore, + R: VerifyingKeystore, +{ + type Error = EitherError; + + #[allow(clippy::type_complexity)] + type Future = Either< + MapErr Self::Error>, + MapErr Self::Error>, + >; + + fn verify(&self, header: &JwtHeader, msg: &[u8], signature: &[u8]) -> Self::Future { + match self { + Either::Left(left) => Either::Left( + left.verify(header, msg, signature) + .map_err(EitherError::Left), + ), + Either::Right(right) => Either::Right( + right + .verify(header, msg, signature) + .map_err(EitherError::Right), + ), + } + } +} + +impl VerifyingKeystore for Arc +where + T: VerifyingKeystore, +{ + type Error = T::Error; + type Future = T::Future; + + fn verify(&self, header: &JwtHeader, msg: &[u8], signature: &[u8]) -> Self::Future { + self.as_ref().verify(header, msg, signature) + } } diff --git a/crates/jose/src/lib.rs b/crates/jose/src/lib.rs index f3bab024..2fb9340e 100644 --- a/crates/jose/src/lib.rs +++ b/crates/jose/src/lib.rs @@ -26,7 +26,7 @@ pub use self::{ jwk::{JsonWebKey, JsonWebKeySet}, jwt::{DecodedJsonWebToken, JsonWebTokenParts, JwtHeader}, keystore::{ - ExportJwks, JwksStore, SharedSecret, SigningKeystore, StaticJwksStore, StaticKeystore, + DynamicJwksStore, SharedSecret, SigningKeystore, StaticJwksStore, StaticKeystore, VerifyingKeystore, }, }; diff --git a/crates/warp-utils/Cargo.toml b/crates/warp-utils/Cargo.toml index 2a7d6b9d..8f3165f5 100644 --- a/crates/warp-utils/Cargo.toml +++ b/crates/warp-utils/Cargo.toml @@ -27,6 +27,7 @@ rand = "0.8.4" mime = "0.3.16" bincode = "1.3.3" crc = "2.1.0" +url = "2.2.2" oauth2-types = { path = "../oauth2-types" } mas-config = { path = "../config" } @@ -35,4 +36,6 @@ mas-data-model = { path = "../data-model" } mas-storage = { path = "../storage" } mas-jose = { path = "../jose" } mas-iana = { path = "../iana" } -url = "2.2.2" + +[dev-dependencies] +tower = { version = "0.4.11", features = ["util"] } diff --git a/crates/warp-utils/src/filters/client.rs b/crates/warp-utils/src/filters/client.rs index bae905bf..01ec7c39 100644 --- a/crates/warp-utils/src/filters/client.rs +++ b/crates/warp-utils/src/filters/client.rs @@ -95,6 +95,7 @@ enum ClientAuthenticationError { impl Reject for ClientAuthenticationError {} #[allow(clippy::too_many_lines)] +#[tracing::instrument(skip_all, fields(enduser.id), err(Debug))] async fn authenticate_client( clients_config: ClientsConfig, audience: String, @@ -204,7 +205,8 @@ async fn authenticate_client( let auth_method = match &client.client_auth_method { ClientAuthMethodConfig::PrivateKeyJwt(jwks) => { let store = jwks.key_store(); - token.verify(&decoded, &store).await.wrap_error()?; + let fut = token.verify(&decoded, &store); + fut.await.wrap_error()?; OAuthClientAuthenticationMethod::PrivateKeyJwt } @@ -239,6 +241,8 @@ async fn authenticate_client( } }; + tracing::Span::current().record("enduser.id", &client.client_id.as_str()); + Ok((auth_method, client.clone(), body)) } @@ -291,8 +295,9 @@ struct ClientAuthForm { mod tests { use headers::authorization::Credentials; use mas_config::{ClientAuthMethodConfig, ConfigurationSection}; - use mas_jose::{ExportJwks, SigningKeystore, StaticKeystore}; + use mas_jose::{SigningKeystore, StaticKeystore}; use serde_json::json; + use tower::{Service, ServiceExt}; use super::*; @@ -343,7 +348,8 @@ mod tests { }); let store = client_private_keystore(); - let jwks = store.export_jwks().await.unwrap(); + let jwks = (&store).ready().await.unwrap().call(()).await.unwrap(); + //let jwks = store.export_jwks().await.unwrap(); config.push(ClientConfig { client_id: "private-key-jwt".to_string(), client_auth_method: ClientAuthMethodConfig::PrivateKeyJwt(jwks.clone().into()),