diff --git a/Cargo.lock b/Cargo.lock index 0626a7b..c6d9f7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,12 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +[[package]] +name = "anyhow" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" + [[package]] name = "apdu" version = "0.4.0" @@ -105,7 +111,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", "synstructure", ] @@ -117,7 +123,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -157,9 +163,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.3" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", @@ -239,7 +245,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -306,7 +312,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -323,7 +329,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -380,7 +386,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -391,7 +397,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -402,9 +408,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "blake2" @@ -467,7 +473,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84ae4213cc2a8dc663acecac67bbdad05142be4d8ef372b6903abf878b0c690a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "bluez-generated", "dbus", "dbus-tokio", @@ -497,7 +503,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9a11621cb2c8c024e444734292482b1ad86fb50ded066cf46252e46643c8748" dependencies = [ "async-trait", - "bitflags 2.10.0", + "bitflags 2.11.0", "bluez-async", "dashmap 6.1.0", "dbus", @@ -520,15 +526,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5c6f81257d10a0f602a294ae4182251151ff97dbb504ef9afcdda4a64b24d9b4" [[package]] name = "bytemuck" -version = "1.24.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" [[package]] name = "byteorder" @@ -544,9 +550,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cairo-rs" @@ -554,7 +560,7 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b01fe135c0bd16afe262b6dea349bd5ea30e6de50708cec639aae7c5c14cc7e4" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cairo-sys-rs", "glib", "libc", @@ -594,9 +600,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.53" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "shlex", @@ -619,9 +625,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.20.5" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21be0e1ce6cdb2ee7fff840f922fb04ead349e5cfb1e750b769132d44ce04720" +checksum = "78cef5b5a1a6827c7322ae2a636368a573006b27cfa76c7ebd53e834daeaab6a" dependencies = [ "smallvec", "target-lexicon", @@ -879,7 +885,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -975,9 +981,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "cc3dc5ad92c2e2d1c193bbbbdf2ea477cb81331de4f3103f267ca18368b988c4" dependencies = [ "powerfmt", ] @@ -1002,7 +1008,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -1078,7 +1084,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -1158,9 +1164,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "find-winsdk" @@ -1173,6 +1179,12 @@ dependencies = [ "winreg", ] +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "fragile" version = "2.0.1" @@ -1181,9 +1193,9 @@ checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -1196,9 +1208,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -1206,15 +1218,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -1223,9 +1235,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -1242,32 +1254,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -1277,7 +1289,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -1396,6 +1407,19 @@ dependencies = [ "wasip2", ] +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + [[package]] name = "gettext-rs" version = "0.7.7" @@ -1462,7 +1486,7 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16de123c2e6c90ce3b573b7330de19be649080ec612033d397d72da265f1bd8b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "futures-channel", "futures-core", "futures-executor", @@ -1487,7 +1511,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -1624,7 +1648,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -1672,6 +1696,15 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + [[package]] name = "hashbrown" version = "0.16.1" @@ -1768,6 +1801,114 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "image" version = "0.25.9" @@ -1788,6 +1929,8 @@ checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", + "serde", + "serde_core", ] [[package]] @@ -1893,11 +2036,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.180" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libdbus-sys" @@ -1920,15 +2069,16 @@ dependencies = [ [[package]] name = "libwebauthn" -version = "0.2.2" -source = "git+https://github.com/linux-credentials/libwebauthn.git?rev=80545bff16c4e89a930221e90d3141a76303b84b#80545bff16c4e89a930221e90d3141a76303b84b" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f74ddf06fefa81809987cb138dc265bcd50131bdce8f9d4ddc7e409a5c7167" dependencies = [ "aes", "apdu", "apdu-core", "async-trait", "base64-url", - "bitflags 2.10.0", + "bitflags 2.11.0", "btleplug", "byteorder", "cbc", @@ -1942,6 +2092,7 @@ dependencies = [ "hidapi", "hkdf", "hmac", + "idna", "maplit", "mockall", "nfc1", @@ -1958,6 +2109,7 @@ dependencies = [ "serde_bytes", "serde_cbor_2", "serde_derive", + "serde_json", "serde_repr", "sha2", "snow", @@ -1979,6 +2131,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + [[package]] name = "locale_config" version = "0.3.0" @@ -2027,9 +2185,9 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memoffset" @@ -2080,7 +2238,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -2146,9 +2304,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-derive" @@ -2158,7 +2316,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -2198,7 +2356,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -2243,7 +2401,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a644b62ffb826a5277f536cf0f701493de420b13d40e700c452c36567771111" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "objc2", "objc2-foundation", ] @@ -2260,7 +2418,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "block2", "libc", "objc2", @@ -2298,9 +2456,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl-probe" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "ordered-stream" @@ -2384,7 +2542,7 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd833ecf8967e65934c49d3521a175929839bf6d0e497f3bd0d3a2ca08943da" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "pcsc-sys", ] @@ -2482,6 +2640,15 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2499,9 +2666,9 @@ dependencies = [ [[package]] name = "predicates" -version = "3.1.3" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe" dependencies = [ "anstyle", "predicates-core", @@ -2509,15 +2676,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" +checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" [[package]] name = "predicates-tree" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" dependencies = [ "predicates-core", "termtree", @@ -2530,7 +2697,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -2554,9 +2721,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -2581,9 +2748,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -2659,14 +2826,14 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -2676,9 +2843,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -2687,9 +2854,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" [[package]] name = "rfc6979" @@ -2745,7 +2912,7 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys", @@ -2845,11 +3012,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.5.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +checksum = "d17b898a6d6948c3a8ee4372c17cb384f90d2e6e912ef00895b14fd7ab54ec38" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "core-foundation", "core-foundation-sys", "libc", @@ -2858,9 +3025,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "321c8673b092a9a42605034a9879d73cb79101ed5fd117bc9a597b89b4e9e61a" dependencies = [ "core-foundation-sys", "libc", @@ -2890,7 +3057,7 @@ checksum = "fca2da10b1f1623f47130256065e05e94fd7a98dbd26a780a4c5de831b21e5c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -2901,7 +3068,7 @@ checksum = "8f68cf7478db8b81abcf71b6d195a34a4891bd3d39868731c4d73194d74ec7a3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -2953,7 +3120,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -2977,7 +3144,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -3058,9 +3225,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -3088,9 +3255,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ "libc", "windows-sys 0.60.2", @@ -3146,9 +3313,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.114" +version = "2.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" dependencies = [ "proc-macro2", "quote", @@ -3163,7 +3330,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -3193,12 +3360,12 @@ checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964" [[package]] name = "tempfile" -version = "3.24.0" +version = "3.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.1", "once_cell", "rustix", "windows-sys 0.61.2", @@ -3242,7 +3409,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -3253,7 +3420,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -3267,9 +3434,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.45" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", @@ -3282,20 +3449,30 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.25" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", ] +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tokio" version = "1.49.0" @@ -3322,7 +3499,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -3378,9 +3555,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.11+spec-1.1.0" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "indexmap", "serde_core", @@ -3414,9 +3591,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" dependencies = [ "winnow", ] @@ -3446,7 +3623,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -3522,9 +3699,15 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "universal-hash" @@ -3548,13 +3731,19 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" -version = "1.19.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.1", "js-sys", "serde_core", "wasm-bindgen", @@ -3615,6 +3804,15 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.108" @@ -3661,7 +3859,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", "wasm-bindgen-shared", ] @@ -3674,6 +3872,40 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "web-sys" version = "0.3.85" @@ -3769,7 +4001,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -3780,7 +4012,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", ] [[package]] @@ -4087,6 +4319,94 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 2.0.116", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.116", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "x509-parser" @@ -4111,11 +4431,34 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8aa498d22c9bbaf482329839bc5620c46be275a19a812e9a22a2b07529a642a" +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", + "synstructure", +] + [[package]] name = "zbus" -version = "5.13.1" +version = "5.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f79257df967b6779afa536788657777a0001f5b42524fcaf5038d4344df40b" +checksum = "1bfeff997a0aaa3eb20c4652baf788d2dfa6d2839a0ead0b3ff69ce2f9c4bdd1" dependencies = [ "async-broadcast", "async-executor", @@ -4149,14 +4492,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.13.1" +version = "5.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aad23e2d2f91cae771c7af7a630a49e755f1eb74f8a46e9f6d5f7a146edf5a37" +checksum = "0bbd5a90dbe8feee5b13def448427ae314ccd26a49cac47905cafefb9ff846f1" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", "zbus_names", "zvariant", "zvariant_utils", @@ -4175,22 +4518,43 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.33" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.33" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", + "synstructure", ] [[package]] @@ -4199,11 +4563,44 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "zmij" -version = "1.0.15" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f63c051f4fe3c1509da62131a678643c5b6fbdc9273b2b79d4378ebda003d2" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zvariant" @@ -4228,7 +4625,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.116", "zvariant_utils", ] @@ -4241,6 +4638,6 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.114", + "syn 2.0.116", "winnow", ] diff --git a/credentialsd/Cargo.toml b/credentialsd/Cargo.toml index 07b3d04..dcb5302 100644 --- a/credentialsd/Cargo.toml +++ b/credentialsd/Cargo.toml @@ -11,7 +11,7 @@ async-trait = "0.1.89" base64 = "0.22.1" credentialsd-common = { path = "../credentialsd-common" } futures-lite.workspace = true -libwebauthn = { git = "https://github.com/linux-credentials/libwebauthn.git", rev="80545bff16c4e89a930221e90d3141a76303b84b", features = ["libnfc","pcsc"] } +libwebauthn = { version = "0.3.0", features = ["libnfc","pcsc"] } # TODO: split nfc and pcsc into separate features # Also, 0.6.1 fails to build with non-vendored library. # https://github.com/alexrsagen/rs-nfc1/issues/15 diff --git a/credentialsd/src/credential_service/hybrid.rs b/credentialsd/src/credential_service/hybrid.rs index fba0c2d..dde5756 100644 --- a/credentialsd/src/credential_service/hybrid.rs +++ b/credentialsd/src/credential_service/hybrid.rs @@ -51,7 +51,13 @@ impl HybridHandler for InternalHybridHandler { QrCodeOperationHint::GetAssertionRequest } }; - let mut device = CableQrCodeDevice::new_transient(hint); + let mut device = match CableQrCodeDevice::new_transient(hint) { + Ok(device) => device, + Err(err) => { + tracing::error!("Failed to create caBLE QR code device: {:?}", err); + return; + } + }; let qr_code = device.qr_code.to_string(); if let Err(err) = tx.send(HybridStateInternal::Init(qr_code)).await { tracing::error!("Failed to send caBLE update: {:?}", err); diff --git a/credentialsd/src/credential_service/mod.rs b/credentialsd/src/credential_service/mod.rs index 1ab7979..58104b2 100644 --- a/credentialsd/src/credential_service/mod.rs +++ b/credentialsd/src/credential_service/mod.rs @@ -381,6 +381,7 @@ impl From for AuthenticatorResponse { mod test { use std::{sync::Arc, time::Duration}; + use base64::Engine as _; use libwebauthn::{ ops::webauthn::{ MakeCredentialRequest, ResidentKeyRequirement, UserVerificationRequirement, @@ -396,9 +397,7 @@ mod test { credential_service::usb::InProcessUsbHandler, dbus::test::{DummyFlowServer, DummyUiServer}, model::CredentialRequest, - webauthn::{self, NavigationContext}, }; - use credentialsd_common::model::Operation; use super::{ hybrid::{test::DummyHybridHandler, HybridStateInternal}, @@ -456,13 +455,15 @@ mod test { fn create_credential_request() -> CredentialRequest { let challenge = "Ox0AXQz7WUER7BGQFzvVrQbReTkS3sepVGj26qfUhhrWSarkDbGF4T4NuCY1aAwHYzOzKMJJ2YRSatetl0D9bQ"; - let origin = NavigationContext::SameOrigin("https://webauthn.io".parse().unwrap()); - let client_data_json = - webauthn::format_client_data_json(Operation::Create, challenge, &origin); - let client_data_hash = webauthn::create_client_data_hash(&client_data_json); + let origin = "webauthn.io".to_string(); + let is_cross_origin = false; + let challenge_bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD + .decode(challenge) + .expect("valid base64url challenge"); let make_request = MakeCredentialRequest { - hash: client_data_hash, - origin: "https://webauthn.io".to_string(), + challenge: challenge_bytes, + origin: origin.clone(), + cross_origin: Some(is_cross_origin), relying_party: Ctap2PublicKeyCredentialRpEntity { id: "webauthn.io".to_string(), name: Some("webauthn.io".to_string()), diff --git a/credentialsd/src/credential_service/nfc.rs b/credentialsd/src/credential_service/nfc.rs index 9291985..fad55a9 100644 --- a/credentialsd/src/credential_service/nfc.rs +++ b/credentialsd/src/credential_service/nfc.rs @@ -39,8 +39,11 @@ impl InProcessNfcHandler { prev_nfc_state: &NfcStateInternal, ) -> Result { match libwebauthn::transport::nfc::get_nfc_device().await { - Ok(None) => Ok(NfcStateInternal::Waiting), - Ok(Some(hid_device)) => Ok(NfcStateInternal::Connected(hid_device)), + Ok(Some(nfc_device)) => Ok(NfcStateInternal::Connected(nfc_device)), + Ok(None) => { + let state = NfcStateInternal::Waiting; + Ok(state) + } Err(err) => { *failures += 1; if *failures == 5 { @@ -527,6 +530,9 @@ async fn handle_nfc_updates( UvUpdate::PresenceRequired => { tracing::debug!("Authenticator requested user presence, but that makes no sense for NFC. Skipping"); } + UvUpdate::PinNotSet(_) => { + tracing::error!("Authenticator requested PIN setup, which is not yet supported."); + } } } debug!("NFC update channel closed."); diff --git a/credentialsd/src/credential_service/usb.rs b/credentialsd/src/credential_service/usb.rs index 8fc8dc9..35771b8 100644 --- a/credentialsd/src/credential_service/usb.rs +++ b/credentialsd/src/credential_service/usb.rs @@ -641,6 +641,9 @@ async fn handle_usb_updates( tracing::error!("Authenticator requested user presence, but we cannot relay the message to the credential service: {:?}", err); } } + UvUpdate::PinNotSet(_) => { + tracing::error!("Authenticator requested PIN setup, which is not yet supported."); + } } } debug!("USB update channel closed."); diff --git a/credentialsd/src/gateway/util.rs b/credentialsd/src/gateway/util.rs index 57e1524..75c0945 100644 --- a/credentialsd/src/gateway/util.rs +++ b/credentialsd/src/gateway/util.rs @@ -2,207 +2,108 @@ //! //! Types shared between components within this service belong in credentialsd_common::model. -use std::{collections::HashMap, time::Duration}; - -use base64::{self, engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; - use credentialsd_common::{ - model::{Operation, WebAuthnError}, + model::WebAuthnError, server::{ CreateCredentialRequest, CreatePublicKeyCredentialResponse, GetCredentialRequest, GetPublicKeyCredentialResponse, }, }; -use crate::{ - cose::CoseKeyAlgorithmIdentifier, - model::{GetAssertionResponseInternal, MakeCredentialResponseInternal}, - webauthn::{ - self, CredentialProtectionExtension, Ctap2PublicKeyCredentialDescriptor, - Ctap2PublicKeyCredentialRpEntity, Ctap2PublicKeyCredentialUserEntity, - GetAssertionHmacOrPrfInput, GetAssertionLargeBlobExtension, GetAssertionRequest, - GetAssertionRequestExtensions, GetPublicKeyCredentialUnsignedExtensionsResponse, - MakeCredentialHmacOrPrfInput, MakeCredentialRequest, MakeCredentialsRequestExtensions, - NavigationContext, Origin, PublicKeyCredentialParameters, ResidentKeyRequirement, - UserVerificationRequirement, - }, +use crate::model::{GetAssertionResponseInternal, MakeCredentialResponseInternal}; +use crate::webauthn::{ + self, GetAssertionRequest, GetPublicKeyCredentialUnsignedExtensionsResponse, + MakeCredentialRequest, NavigationContext, Origin, RelyingPartyId, WebAuthnIDL, }; -// Helper functions for translating D-Bus types into internal types -pub(super) fn create_credential_request_try_into_ctap2( - request: &CreateCredentialRequest, - origin: &NavigationContext, -) -> std::result::Result<(MakeCredentialRequest, String), WebAuthnError> { - if request.public_key.is_none() { - return Err(WebAuthnError::NotSupportedError); - } - let options = request.public_key.as_ref().ok_or_else(|| { - tracing::info!("Invalid request: missing public_key"); +/// Reads the rpId from a create-credential request JSON (`rp.id`). +/// +/// Used as a fallback when the origin is an AppId and the effective domain +/// cannot be derived from the origin alone. +// TODO(libwebauthn#185) +fn peek_make_credential_rp_id(request_json: &str) -> Result { + let value = serde_json::from_str::(request_json).map_err(|err| { + tracing::info!("Invalid request JSON: {err}"); WebAuthnError::TypeError })?; - - let request_value = - serde_json::from_str::(&options.request_json).map_err(|err| { - tracing::info!("Invalid request JSON: {err}"); - WebAuthnError::TypeError + let rp_id_str = value + .get("rp") + .and_then(|rp| rp.get("id")) + .and_then(|id| id.as_str()) + .ok_or_else(|| { + tracing::info!("RP ID required if using app ID as origin"); + WebAuthnError::SecurityError })?; - let json = request_value.as_object().ok_or_else(|| { - tracing::info!("Invalid request JSON: not an object"); + RelyingPartyId::try_from(rp_id_str).map_err(|_| { + tracing::info!("Invalid relying party ID"); + WebAuthnError::TypeError + }) +} + +/// Reads the rpId from a get-credential request JSON (`rpId`). +/// +/// Used as a fallback when the origin is an AppId and the effective domain +/// cannot be derived from the origin alone. +// TODO(libwebauthn#185) +fn peek_get_assertion_rp_id(request_json: &str) -> Result { + let value = serde_json::from_str::(request_json).map_err(|err| { + tracing::info!("Invalid request JSON: {err}"); WebAuthnError::TypeError })?; - let challenge = json - .get("challenge") - .and_then(|c| c.as_str()) + let rp_id_str = value + .get("rpId") + .and_then(|id| id.as_str()) .ok_or_else(|| { - tracing::info!("JSON missing `challenge` field."); - WebAuthnError::TypeError - })? - .to_owned(); - let rp = json - .get("rp") - .and_then(|val| { - serde_json::from_str::(&val.to_string()).ok() - }) - .ok_or_else(|| { - tracing::info!("JSON missing `rp` field"); - WebAuthnError::TypeError + tracing::info!("RP ID required if using app ID as origin"); + WebAuthnError::SecurityError })?; - let mut user = - json.get("user") - .ok_or_else(|| { - tracing::info!("JSON missing `user` field."); - WebAuthnError::TypeError - }) - .and_then(|val| { - serde_json::from_str::(&val.to_string()) - .map_err(|e| { - tracing::info!("JSON missing `user` field: {e}"); - WebAuthnError::TypeError - }) - })?; - user.id = URL_SAFE_NO_PAD - .decode(user.id) - .map_err(|_| { - tracing::info!("user ID is not a valid base64url string"); - WebAuthnError::TypeError - })? - .into(); - let other_options = - serde_json::from_str::(&request_value.to_string()) - .map_err(|e| { - tracing::info!("Received invalid request JSON: {e}"); - WebAuthnError::TypeError - })?; - let (resident_key, user_verification) = - if let Some(authenticator_selection) = other_options.authenticator_selection { - let resident_key = match authenticator_selection.resident_key.as_deref() { - Some("required") => Some(ResidentKeyRequirement::Required), - Some("preferred") => Some(ResidentKeyRequirement::Preferred), - Some("discouraged") => Some(ResidentKeyRequirement::Discouraged), - Some(_) => None, - // legacy webauthn-1 member - None if authenticator_selection.require_resident_key == Some(true) => { - Some(ResidentKeyRequirement::Required) - } - None => None, - }; + RelyingPartyId::try_from(rp_id_str).map_err(|_| { + tracing::info!("Invalid relying party ID"); + WebAuthnError::TypeError + }) +} - let user_verification = authenticator_selection - .user_verification - .map(|uv| match uv.as_ref() { - "required" => UserVerificationRequirement::Required, - "preferred" => UserVerificationRequirement::Preferred, - "discouraged" => UserVerificationRequirement::Discouraged, - _ => todo!("This should be fixed in the future"), - }) - .unwrap_or(UserVerificationRequirement::Preferred); +/// Parses a WebAuthn create credential request from D-Bus into a CTAP2 MakeCredentialRequest. +/// +/// Uses libwebauthn's `WebAuthnIDL::from_json()` for parsing. The relying party ID is derived +/// from the request's origin; libwebauthn validates that any rpId in the JSON matches it. +pub(super) fn create_credential_request_try_into_ctap2( + request: &CreateCredentialRequest, + request_environment: &NavigationContext, +) -> std::result::Result<(MakeCredentialRequest, String), WebAuthnError> { + let options = request.public_key.as_ref().ok_or_else(|| { + tracing::info!("Invalid request: missing public_key"); + WebAuthnError::NotSupportedError + })?; - (resident_key, user_verification) - } else { - (None, UserVerificationRequirement::Preferred) - }; - let extensions = if let Some(incoming_extensions) = other_options.extensions { - let extensions = MakeCredentialsRequestExtensions { - cred_props: incoming_extensions.cred_props, - cred_blob: incoming_extensions - .cred_blob - .and_then(|x| URL_SAFE_NO_PAD.decode(x).ok()), - min_pin_length: incoming_extensions.min_pin_length, - cred_protect: match incoming_extensions.credential_protection_policy { - Some(cred_prot_policy) => Some(CredentialProtectionExtension { - policy: cred_prot_policy, - enforce_policy: incoming_extensions - .enforce_credential_protection_policy - .unwrap_or_default(), - }), - None => None, - }, - large_blob: incoming_extensions - .large_blob - .map(|x| x.support.unwrap_or_default()) - .unwrap_or_default(), - hmac_or_prf: if incoming_extensions.prf.is_some() { - // CTAP currently doesn't support PRF queries at credentials.create() - // So we ignore any potential value set in the request and only mark this - // credential to activate HMAC for future PRF queries using credentials.get() - MakeCredentialHmacOrPrfInput::Prf - } else { - // MakeCredentialHmacOrPrfInput::Hmac is not used directly by webauthn - MakeCredentialHmacOrPrfInput::None - }, - }; - Some(extensions) - } else { - None + let origin = request_environment.origin(); + let rp_id = match origin { + Origin::Https { .. } => RelyingPartyId::try_from(origin).map_err(|err| { + tracing::info!("Cannot derive relying party ID from origin: {err}"); + WebAuthnError::SecurityError + })?, + Origin::AppId(_) => peek_make_credential_rp_id(&options.request_json)?, }; - let credential_parameters = match request_value.clone().get("pubKeyCredParams") { - // https://www.w3.org/TR/webauthn-3/#sctn-createCredential Section 5.1.3.10 - // Default to ES256 and RS256 if no params are given. - None => Ok(vec![ - PublicKeyCredentialParameters { - alg: CoseKeyAlgorithmIdentifier::ES256.into(), - }, - PublicKeyCredentialParameters { - alg: CoseKeyAlgorithmIdentifier::RS256.into(), - }, - ]), - Some(val) => serde_json::from_str::>(&val.to_string()) - .map_err(|e| { - tracing::info!("Request JSON missing or invalid `pubKeyCredParams` key: {e}."); - WebAuthnError::TypeError - }), - }?; - let algorithms = credential_parameters - .iter() - .filter_map(|p| p.try_into().ok()) - .collect(); - let exclude = other_options.excluded_credentials.map(|v| { - v.iter() - .map(|e| e.try_into()) - .filter_map(|e| e.ok()) - .collect() - }); - let client_data_json = webauthn::format_client_data_json(Operation::Create, &challenge, origin); - let client_data_hash = webauthn::create_client_data_hash(&client_data_json); - Ok(( - MakeCredentialRequest { - hash: client_data_hash, - origin: origin.origin().to_string(), + let mut make_cred_request = MakeCredentialRequest::from_json(&rp_id, &options.request_json) + .map_err(|err| { + tracing::info!("Failed to parse MakeCredential request JSON: {err}"); + WebAuthnError::TypeError + })?; - relying_party: rp, - user, - resident_key, - user_verification, - algorithms, - exclude, - extensions, - timeout: other_options.timeout.unwrap_or(Duration::from_secs(300)), - }, - client_data_json, - )) + // TODO(libwebauthn#185) + make_cred_request.origin = origin.to_string(); + make_cred_request.cross_origin = Some(matches!( + request_environment, + NavigationContext::CrossOrigin(_) + )); + + let client_data_json = make_cred_request.client_data_json(); + + Ok((make_cred_request, client_data_json)) } +/// Serializes a CTAP2 MakeCredentialResponse to WebAuthn JSON format. pub(super) fn create_credential_response_try_from_ctap2( response: &MakeCredentialResponseInternal, client_data_json: String, @@ -228,7 +129,7 @@ pub(super) fn create_credential_response_try_from_ctap2( response.ctap.enterprise_attestation.unwrap_or(false), ) .map_err(|_| "Failed to create attestation object".to_string())?; - // TODO: do we need to check that the client_data_hash is the same? + let registration_response_json = webauthn::CreatePublicKeyCredentialResponse::new( attested_credential.credential_id.clone(), attestation_object, @@ -244,116 +145,47 @@ pub(super) fn create_credential_response_try_from_ctap2( Ok(response) } +/// Parses a WebAuthn get credential request from D-Bus into a CTAP2 GetAssertionRequest. +/// +/// Uses libwebauthn's `WebAuthnIDL::from_json()` for parsing. The relying party ID is derived +/// from the request's origin; libwebauthn validates that any rpId in the JSON matches it. pub(super) fn get_credential_request_try_into_ctap2( request: &GetCredentialRequest, - request_env: &NavigationContext, + request_environment: &NavigationContext, ) -> std::result::Result<(GetAssertionRequest, String), WebAuthnError> { - if request.public_key.is_none() { - return Err(WebAuthnError::NotSupportedError); - } - let options: webauthn::GetCredentialOptions = request - .public_key - .as_ref() - .ok_or_else(|| { - tracing::info!("Invalid request: no \"publicKey\" options specified."); - WebAuthnError::TypeError - }) - .and_then(|o| { - serde_json::from_str(&o.request_json).map_err(|e| { - tracing::info!("Received invalid request JSON: {:?}", e); - WebAuthnError::TypeError - }) - })?; - let mut allow: Vec = options - .allow_credentials - .iter() - .filter_map(|cred| { - if cred.cred_type == "public-key" { - cred.try_into().ok() - } else { - None - } - }) - .collect(); - // TODO: The allow is returning an empty list instead of either None or a list of transports. - // This should be investigated, but this is just a UI hint and isn't necessary to pass to the authenticator. - // Just removing it for now. - for c in allow.iter_mut() { - c.transports = None; - } + let options = request.public_key.as_ref().ok_or_else(|| { + tracing::info!("Invalid request: no \"publicKey\" options specified."); + WebAuthnError::NotSupportedError + })?; - let client_data_json = - webauthn::format_client_data_json(Operation::Get, &options.challenge, request_env); - let client_data_hash = webauthn::create_client_data_hash(&client_data_json); - // TODO: actually calculate correct effective domain, and use fallback to related origin requests to fill this in. For now, just default to origin. - let user_verification = match options - .user_verification - .unwrap_or_else(|| String::from("preferred")) - .as_ref() - { - "required" => UserVerificationRequirement::Required, - "preferred" => UserVerificationRequirement::Preferred, - "discouraged" => UserVerificationRequirement::Discouraged, - _ => { - tracing::info!("Invalid user verification requirement specified by client."); - return Err(WebAuthnError::TypeError); - } - }; - let relying_party_id = match (options.rp_id, request_env.origin()) { - (Some(rp_id), _) => rp_id, - (None, Origin::Https { host, .. }) => host.to_string(), - (None, Origin::AppId(_)) => { - tracing::info!("RP ID required if using app ID as origin"); - return Err(WebAuthnError::SecurityError); - } + let origin = request_environment.origin(); + let rp_id = match origin { + Origin::Https { .. } => RelyingPartyId::try_from(origin).map_err(|err| { + tracing::info!("Cannot derive relying party ID from origin: {err}"); + WebAuthnError::SecurityError + })?, + Origin::AppId(_) => peek_get_assertion_rp_id(&options.request_json)?, }; - let extensions = if let Some(incoming_extensions) = options.extensions { - let extensions = GetAssertionRequestExtensions { - cred_blob: incoming_extensions.get_cred_blob, - hmac_or_prf: incoming_extensions - .prf - .and_then(|x| { - x.eval.map(|eval| { - let eval = Some(eval.decode()); - let mut eval_by_credential = HashMap::new(); - if let Some(incoming_eval) = x.eval_by_credential { - for (key, val) in incoming_eval.iter() { - eval_by_credential.insert(key.clone(), val.decode()); - } - } - GetAssertionHmacOrPrfInput::Prf { - eval, - eval_by_credential, - } - }) - }) - .unwrap_or_default(), - large_blob: incoming_extensions - .large_blob - // TODO: Implement GetAssertionLargeBlobExtension::Write, once libwebauthn supports it - .filter(|x| x.read == Some(true)) - .map(|_| GetAssertionLargeBlobExtension::Read) - .unwrap_or(GetAssertionLargeBlobExtension::None), - }; - Some(extensions) - } else { - None - }; + let mut get_assertion_request = GetAssertionRequest::from_json(&rp_id, &options.request_json) + .map_err(|err| { + tracing::info!("Failed to parse GetAssertion request JSON: {err}"); + WebAuthnError::TypeError + })?; - Ok(( - GetAssertionRequest { - hash: client_data_hash, - relying_party_id, - user_verification, - allow, - extensions, - timeout: options.timeout.unwrap_or(Duration::from_secs(300)), - }, - client_data_json, - )) + // TODO(libwebauthn#185) + get_assertion_request.origin = origin.to_string(); + get_assertion_request.cross_origin = Some(matches!( + request_environment, + NavigationContext::CrossOrigin(_) + )); + + let client_data_json = get_assertion_request.client_data_json(); + + Ok((get_assertion_request, client_data_json)) } +/// Serializes a CTAP2 GetAssertionResponse to WebAuthn JSON format. pub(super) fn get_credential_response_try_from_ctap2( response: &GetAssertionResponseInternal, client_data_json: String, @@ -364,12 +196,6 @@ pub(super) fn get_credential_response_try_from_ctap2( .to_response_bytes() .map_err(|err| format!("Failed to parse authenticator data: {err}"))?; - // We can't just do this here, because we need encode all byte arrays for the JS-communication: - // let unsigned_extensions = response - // .ctap - // .unsigned_extensions_output - // .as_ref() - // .map(|extensions| serde_json::to_string(&extensions).unwrap()); let unsigned_extensions = response .ctap .unsigned_extensions_output diff --git a/credentialsd/src/main.rs b/credentialsd/src/main.rs index ea920e8..ef088c2 100644 --- a/credentialsd/src/main.rs +++ b/credentialsd/src/main.rs @@ -4,7 +4,6 @@ mod credential_service; mod dbus; mod gateway; mod model; -mod serde; mod webauthn; use std::{error::Error, sync::Arc}; diff --git a/credentialsd/src/serde/mod.rs b/credentialsd/src/serde/mod.rs deleted file mode 100644 index 353b0f2..0000000 --- a/credentialsd/src/serde/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -pub(crate) mod b64 { - use base64::{self, engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; - - use serde::{de, Deserialize, Deserializer}; - - pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - URL_SAFE_NO_PAD.decode(s).map_err(de::Error::custom) - } -} - -pub(crate) mod duration { - use std::time::Duration; - - use serde::{Deserialize, Deserializer}; - - pub(crate) fn from_opt_ms<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - Option::::deserialize(deserializer) - .map(|ms_opt| ms_opt.map(|ms| Duration::from_millis(ms as u64))) - } -} diff --git a/credentialsd/src/webauthn.rs b/credentialsd/src/webauthn.rs index a539e82..bb9a23a 100644 --- a/credentialsd/src/webauthn.rs +++ b/credentialsd/src/webauthn.rs @@ -1,41 +1,23 @@ -use std::{collections::HashMap, fmt::Display, str::FromStr, time::Duration}; +use std::{fmt::Display, str::FromStr}; use base64::{self, engine::general_purpose::URL_SAFE_NO_PAD, Engine}; -use libwebauthn::{ - ops::webauthn::{CredentialProtectionPolicy, MakeCredentialLargeBlobExtension}, - proto::ctap2::{ - Ctap2AttestationStatement, Ctap2CredentialType, Ctap2PublicKeyCredentialType, - Ctap2Transport, - }, -}; +use libwebauthn::proto::ctap2::Ctap2AttestationStatement; use ring::digest; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use serde_json::json; use tracing::debug; use credentialsd_common::model::Operation; -use crate::cose::{CoseKeyAlgorithmIdentifier, CoseKeyType}; +use crate::cose::CoseKeyAlgorithmIdentifier; pub use libwebauthn::ops::webauthn::{ - CredentialProtectionExtension, GetAssertionHmacOrPrfInput, GetAssertionLargeBlobExtension, - GetAssertionRequest, GetAssertionRequestExtensions, MakeCredentialHmacOrPrfInput, - MakeCredentialRequest, MakeCredentialsRequestExtensions, ResidentKeyRequirement, - UserVerificationRequirement, -}; -pub use libwebauthn::proto::ctap2::{ - Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialRpEntity, - Ctap2PublicKeyCredentialUserEntity, + GetAssertionRequest, MakeCredentialRequest, RelyingPartyId, WebAuthnIDL, }; #[derive(Debug)] pub enum Error { - Unknown, NotSupported, - InvalidState, - NotAllowed, - Constraint, - Internal(String), } pub(crate) fn create_attestation_object( @@ -71,14 +53,16 @@ pub(crate) fn create_attestation_object( } AttestationStatement::U2F { signature, - certificate, + certificates, } => { cbor_writer.write_text("fido-u2f").unwrap(); cbor_writer.write_text("attStmt").unwrap(); cbor_writer.write_map_start(2).unwrap(); cbor_writer.write_text("x5c").unwrap(); - cbor_writer.write_array_start(1).unwrap(); - cbor_writer.write_bytes(certificate).unwrap(); + cbor_writer.write_array_start(certificates.len()).unwrap(); + for cert in certificates.iter() { + cbor_writer.write_bytes(cert).unwrap(); + } cbor_writer.write_text("sig").unwrap(); cbor_writer.write_bytes(signature).unwrap(); } @@ -95,257 +79,12 @@ pub(crate) fn create_attestation_object( Ok(attestation_object) } -#[derive(Debug, Deserialize)] -pub(crate) struct MakeCredentialOptions { - /// Timeout in milliseconds - #[serde(deserialize_with = "crate::serde::duration::from_opt_ms")] - #[serde(default)] - pub timeout: Option, - #[serde(rename = "excludeCredentials")] - pub excluded_credentials: Option>, - #[serde(rename = "authenticatorSelection")] - pub authenticator_selection: Option, - /// https://www.w3.org/TR/webauthn-3/#enum-attestation-convey - #[allow(dead_code)] - pub attestation: Option, - /// extensions input as a JSON object - pub extensions: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub(crate) struct MakeCredentialExtensions { - #[serde(skip_serializing_if = "Option::is_none")] - pub cred_blob: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub cred_props: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub min_pin_length: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub credential_protection_policy: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub enforce_credential_protection_policy: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub large_blob: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub prf: Option, -} - -#[derive(Debug, Default, Deserialize)] -pub(crate) struct LargeBlobExtension { - #[serde(skip_serializing_if = "Option::is_none")] - pub support: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub read: Option, - #[allow(dead_code)] // TODO: Not currently used, but we should eventually implement - #[serde(skip_serializing_if = "Option::is_none")] - pub write: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub(crate) struct Prf { - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) eval: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) eval_by_credential: Option>, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct PRFValue { - // base64 encoded data - pub first: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub second: Option, -} - -impl PRFValue { - pub(crate) fn decode(&self) -> libwebauthn::ops::webauthn::PRFValue { - let mut res = libwebauthn::ops::webauthn::PRFValue::default(); - let first = URL_SAFE_NO_PAD.decode(&self.first).unwrap(); - let len_to_copy = std::cmp::min(first.len(), 32); // Determine how many bytes to copy - res.first[..len_to_copy].copy_from_slice(&first[..len_to_copy]); - if let Some(second) = self - .second - .as_ref() - .map(|second| URL_SAFE_NO_PAD.decode(second).unwrap()) - { - let len_to_copy = std::cmp::min(second.len(), 32); // Determine how many bytes to copy - let mut res_second = [0u8; 32]; - res_second[..len_to_copy].copy_from_slice(&second[..len_to_copy]); - res.second = Some(res_second); - } - res - } -} - -#[derive(Debug, Deserialize)] -pub(crate) struct GetCredentialOptions { - /// Challenge bytes in base64url-encoding with no padding. - pub(crate) challenge: String, - - #[serde(deserialize_with = "crate::serde::duration::from_opt_ms")] - #[serde(default)] - pub(crate) timeout: Option, - - /// Relying Party ID. - /// If not set, the request origin's effective domain will be used instead. - #[serde(rename = "rpId")] - pub(crate) rp_id: Option, - - /// An list of allowed credentials, in descending order of RP preference. - /// If empty, then any credential that can fulfill the request is allowed. - #[serde(rename = "allowCredentials")] - #[serde(default)] - pub(crate) allow_credentials: Vec, - - /// Defaults to `preferred` - #[serde(rename = "userVerification")] - pub(crate) user_verification: Option, - - /// Contextual information from the RP to help the client guide the user - /// through the authentication ceremony. - #[allow(dead_code)] // TODO: Not currently used, but we should eventually implement support for hints. - #[serde(default)] - pub(crate) hints: Vec, - - pub(crate) extensions: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub(crate) struct GetCredentialExtensions { - // TODO: appid - #[serde(skip_serializing_if = "Option::is_none")] - pub get_cred_blob: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub large_blob: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub prf: Option, -} - -#[derive(Debug, Deserialize)] -/// https://www.w3.org/TR/webauthn-3/#dictionary-credential-descriptor -pub(crate) struct CredentialDescriptor { - /// Type of the public key credential the caller is referring to. - /// - /// The value SHOULD be a member of PublicKeyCredentialType but client - /// platforms MUST ignore any PublicKeyCredentialDescriptor with an unknown - /// type. - #[serde(rename = "type")] - pub(crate) cred_type: String, - /// Credential ID of the public key credential the caller is referring to. - #[serde(with = "crate::serde::b64")] - pub(crate) id: Vec, - pub(crate) transports: Option>, -} - -impl TryFrom<&CredentialDescriptor> for Ctap2PublicKeyCredentialDescriptor { - type Error = Error; - fn try_from(value: &CredentialDescriptor) -> Result { - let transports = value.transports.as_ref().filter(|t| !t.is_empty()); - let transports = match transports { - Some(transports) => { - let transport_list = transports.iter().map(|t| match t.as_ref() { - "ble" => Some(Ctap2Transport::Ble), - "hybrid" => Some(Ctap2Transport::Hybrid), - "internal" => Some(Ctap2Transport::Internal), - "nfc" => Some(Ctap2Transport::Nfc), - "usb" => Some(Ctap2Transport::Usb), - _ => None, - }); - transport_list.collect() - } - None => None, - }; - Ok(Self { - r#type: Ctap2PublicKeyCredentialType::PublicKey, - id: value.id.clone().into(), - transports, - }) - } -} -impl TryFrom for Ctap2PublicKeyCredentialDescriptor { - type Error = Error; - fn try_from(value: CredentialDescriptor) -> Result { - Ctap2PublicKeyCredentialDescriptor::try_from(&value) - } -} - -#[derive(Debug, Deserialize)] -/// https://www.w3.org/TR/webauthn-3/#dictionary-authenticatorSelection -pub(crate) struct AuthenticatorSelectionCriteria { - // /// https://www.w3.org/TR/webauthn-3/#enum-attachment - // #[zvariant(rename = "authenticatorAttachment")] - // pub authenticator_attachment: Option, - // - /// https://www.w3.org/TR/webauthn-3/#enum-residentKeyRequirement - #[serde(rename = "residentKey")] - pub resident_key: Option, - - // Implied by resident_key == "required", deprecated in webauthn - // https://www.w3.org/TR/webauthn-3/#enum-residentKeyRequirement - #[serde(rename = "requireResidentKey")] - pub require_resident_key: Option, - - /// https://www.w3.org/TR/webauthn-3/#enumdef-userverificationrequirement - #[serde(rename = "userVerification")] - pub user_verification: Option, -} - -#[derive(Clone, Deserialize)] -/// https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialparameters -pub(crate) struct PublicKeyCredentialParameters { - pub alg: i64, -} - -impl TryFrom<&PublicKeyCredentialParameters> for Ctap2CredentialType { - type Error = Error; - - fn try_from(value: &PublicKeyCredentialParameters) -> Result { - let algorithm = match value.alg { - -7 => libwebauthn::proto::ctap2::Ctap2COSEAlgorithmIdentifier::ES256, - -8 => libwebauthn::proto::ctap2::Ctap2COSEAlgorithmIdentifier::EDDSA, - // TODO: we should still pass on the raw value to the authenticator and let it decide whether it's supported. - _ => { - return Err(Error::Internal( - "Invalid algorithm passed for new credential".to_owned(), - )) - } - }; - Ok(Self { - public_key_type: Ctap2PublicKeyCredentialType::PublicKey, - algorithm, - }) - } -} - -impl TryFrom<&PublicKeyCredentialParameters> for CoseKeyType { - type Error = String; - fn try_from(value: &PublicKeyCredentialParameters) -> Result { - match value.alg { - -7 => Ok(CoseKeyType::Es256P256), - -8 => Ok(CoseKeyType::EddsaEd25519), - -257 => Ok(CoseKeyType::RS256), - _ => Err("Invalid or unsupported algorithm specified".to_owned()), - } - } -} - -impl TryFrom for CoseKeyType { - type Error = String; - fn try_from(value: PublicKeyCredentialParameters) -> Result { - CoseKeyType::try_from(&value) - } -} - #[derive(Debug, PartialEq)] pub(crate) enum AttestationStatement { None, U2F { signature: Vec, - certificate: Vec, + certificates: Vec>, }, Packed { algorithm: CoseKeyAlgorithmIdentifier, @@ -377,7 +116,11 @@ impl TryFrom<&Ctap2AttestationStatement> for AttestationStatement { } Ctap2AttestationStatement::FidoU2F(att_stmt) => Ok(Self::U2F { signature: att_stmt.signature.as_ref().to_vec(), - certificate: att_stmt.certificate.to_vec(), + certificates: att_stmt + .certificates + .iter() + .map(|c| c.as_ref().to_vec()) + .collect(), }), _ => { debug!("Unsupported attestation type: {:?}", value); @@ -767,6 +510,23 @@ impl Display for Origin { } } +impl TryFrom<&Origin> for RelyingPartyId { + type Error = OriginParseError; + + /// Derives the relying party ID (effective domain) from an origin. + /// + /// AppId origins have no effective domain and must be mapped to an rpId + /// out-of-band, so this conversion fails for them. + fn try_from(origin: &Origin) -> Result { + match origin { + Origin::Https { host, .. } => { + RelyingPartyId::try_from(host.as_str()).map_err(|_| OriginParseError::InvalidHost) + } + Origin::AppId(_) => Err(OriginParseError::InvalidScheme), + } + } +} + impl FromStr for Origin { type Err = OriginParseError;