diff --git a/libwebauthn/Cargo.lock b/libwebauthn/Cargo.lock index 111e557..cd07b69 100644 --- a/libwebauthn/Cargo.lock +++ b/libwebauthn/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "aead" version = "0.5.2" @@ -55,18 +40,18 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -79,9 +64,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -94,22 +79,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -162,7 +147,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror 2.0.16", + "thiserror 2.0.17", "time", ] @@ -174,7 +159,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "synstructure 0.13.2", ] @@ -186,7 +171,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -197,7 +182,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -217,9 +202,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.14.0" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b8ff6c09cd57b16da53641caa860168b88c172a5ee163b0288d3d6eea12786" +checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" dependencies = [ "aws-lc-sys", "zeroize", @@ -227,32 +212,16 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.31.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e44d16778acaf6a9ec9899b92cebd65580b83f685446bf2e1f5d3d732f99dcd" +checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" dependencies = [ - "bindgen 0.72.1", "cc", "cmake", "dunce", "fs_extra", ] -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - [[package]] name = "base16ct" version = "0.2.0" @@ -267,18 +236,18 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64-url" -version = "3.0.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2b6c78c06f7288d5e3c3d683bde35a79531127c83b087e5d0d77c974b4b28" +checksum = "f5b428e9fb429c6fda7316e9b006f993e6b4c33005e4659339fb5214479dddec" dependencies = [ "base64", ] [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] name = "bindgen" @@ -297,9 +266,9 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", + "rustc-hash", "shlex", - "syn 2.0.106", + "syn 2.0.111", "which", ] @@ -309,36 +278,16 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cexpr", "clang-sys", "itertools 0.13.0", "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", + "rustc-hash", "shlex", - "syn 2.0.106", -] - -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags 2.9.4", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 2.1.1", - "shlex", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -349,9 +298,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "blake2" @@ -404,7 +353,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84ae4213cc2a8dc663acecac67bbdad05142be4d8ef372b6903abf878b0c690a" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "bluez-generated", "dbus", "dbus-tokio", @@ -413,7 +362,7 @@ dependencies = [ "log", "serde", "serde-xml-rs", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "uuid", ] @@ -434,7 +383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9a11621cb2c8c024e444734292482b1ad86fb50ded066cf46252e46643c8748" dependencies = [ "async-trait", - "bitflags 2.9.4", + "bitflags 2.10.0", "bluez-async", "dashmap 6.1.0", "dbus", @@ -447,7 +396,7 @@ dependencies = [ "objc2-foundation", "once_cell", "static_assertions", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "uuid", @@ -457,15 +406,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytemuck" -version = "1.23.2" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" [[package]] name = "byteorder" @@ -481,9 +430,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "cbc" @@ -496,21 +445,21 @@ dependencies = [ [[package]] name = "cbor-smol" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087b31faa4ad4ba21c9bd0209204eef424dae6424195aafc7242006b69fc8d" +checksum = "0b6dd31f7069836e87169bc5910212571b873cebe389c7c7f2d8b1fb3e55c80d" dependencies = [ "delog", "heapless", "heapless-bytes", - "serde", + "serde_core", ] [[package]] name = "cc" -version = "1.2.38" +version = "1.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" dependencies = [ "find-msvc-tools", "jobserver", @@ -535,9 +484,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chacha20" @@ -587,9 +536,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.54" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" dependencies = [ "cc", ] @@ -696,9 +645,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -817,7 +766,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -855,9 +804,9 @@ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "dbus" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190b6255e8ab55a7b568df5a883e9497edc3e4821c06396612048b430e5ad1e9" +checksum = "21b3aa68d7e7abee336255bd7248ea965cc393f3e70411135a6f6a4b651345d4" dependencies = [ "futures-channel", "futures-util", @@ -935,9 +884,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", ] @@ -980,7 +929,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1076,9 +1025,9 @@ dependencies = [ [[package]] name = "env_filter" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" dependencies = [ "log", ] @@ -1108,7 +1057,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -1169,9 +1118,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.2" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" [[package]] name = "find-winsdk" @@ -1203,16 +1152,10 @@ checksum = "e9985657bc39bae5cb7f4e686b46c67b1ea37390d82fe4bab56d7cd1933429d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "synstructure 0.13.2", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "fragile" version = "2.0.1" @@ -1281,7 +1224,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1346,19 +1289,19 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.7+wasi-0.2.4", + "wasip2", ] [[package]] @@ -1371,12 +1314,6 @@ dependencies = [ "polyval", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "glob" version = "0.3.3" @@ -1407,12 +1344,13 @@ dependencies = [ [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy", ] [[package]] @@ -1432,9 +1370,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heapless" @@ -1475,9 +1413,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hidapi" -version = "2.6.3" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b876ecf37e86b359573c16c8366bc3eba52b689884a0fc42ba3f67203d2a8b" +checksum = "565dd4c730b8f8b2c0fb36df6be12e5470ae10895ddcc4e9dcfbfb495de202b0" dependencies = [ "cc", "cfg-if", @@ -1516,21 +1454,20 @@ dependencies = [ [[package]] name = "home" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -1542,9 +1479,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "image" -version = "0.25.8" +version = "0.25.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" +checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" dependencies = [ "bytemuck", "byteorder-lite", @@ -1554,12 +1491,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.4" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", ] [[package]] @@ -1581,22 +1518,11 @@ dependencies = [ "loom", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", -] - [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "iso7816" @@ -1627,9 +1553,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010" [[package]] name = "jni" @@ -1672,15 +1598,15 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.80" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -1700,27 +1626,27 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.175" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libdbus-sys" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cbe856efeb50e4681f010e9aaa2bf0a644e10139e54cde10fc83a307c23bd9f" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" dependencies = [ "pkg-config", ] [[package]] name = "libloading" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-targets 0.53.3", + "windows-link 0.2.1", ] [[package]] @@ -1732,7 +1658,7 @@ dependencies = [ "apdu-core", "async-trait", "base64-url", - "bitflags 2.9.4", + "bitflags 2.10.0", "btleplug", "byteorder", "cbc", @@ -1769,13 +1695,14 @@ dependencies = [ "serde_bytes", "serde_cbor_2", "serde_derive", + "serde_json", "serde_repr", "sha2 0.10.9", "snow", "tempfile", "test-log", "text_io", - "thiserror 2.0.16", + "thiserror 2.0.17", "time", "tokio", "tokio-stream", @@ -1816,11 +1743,11 @@ dependencies = [ [[package]] name = "littlefs2-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a81a4745d38b288b7583fe8ea3736897628df81f4d0f1d0314fa5a3af570de4" +checksum = "0dd2d1cad5532f5e11b0fd871b8a981885efd3fdaec0427c0d637322ae09efbf" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "heapless-bytes", "serde", ] @@ -1837,19 +1764,18 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "loom" @@ -1881,9 +1807,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "minimal-lexical" @@ -1891,24 +1817,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - [[package]] name = "mio" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "wasi", + "windows-sys 0.61.2", ] [[package]] @@ -1934,14 +1851,14 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "moxcms" -version = "0.7.5" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd32fa8935aeadb8a8a6b6b351e40225570a37c43de67690383d87ef170cd08" +checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" dependencies = [ "num-traits", "pxfm", @@ -1987,11 +1904,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.1" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2018,7 +1935,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -2041,9 +1958,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", "rustversion", @@ -2051,14 +1968,14 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -2083,7 +2000,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a644b62ffb826a5277f536cf0f701493de420b13d40e700c452c36567771111" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "objc2", "objc2-foundation", ] @@ -2100,21 +2017,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "libc", "objc2", ] -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - [[package]] name = "oid-registry" version = "0.8.1" @@ -2132,9 +2040,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "opaque-debug" @@ -2200,9 +2108,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -2210,15 +2118,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] @@ -2227,7 +2135,7 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd833ecf8967e65934c49d3521a175929839bf6d0e497f3bd0d3a2ca08943da" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "pcsc-sys", ] @@ -2371,7 +2279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -2395,18 +2303,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "pxfm" -version = "0.1.23" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55f4fedc84ed39cb7a489322318976425e42a147e2be79d8f878e2884f94e84" +checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" dependencies = [ "num-traits", ] @@ -2422,9 +2330,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -2491,16 +2399,16 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", ] [[package]] @@ -2511,9 +2419,9 @@ checksum = "09c30c54dffee5b40af088d5d50aa3455c91a0127164b51f0215efc4cb28fb3c" [[package]] name = "regex" -version = "1.11.2" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -2523,9 +2431,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -2534,9 +2442,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "rfc6979" @@ -2562,24 +2470,12 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - [[package]] name = "rustc_version" version = "0.4.1" @@ -2604,7 +2500,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -2613,22 +2509,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.31" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "aws-lc-rs", "log", @@ -2642,9 +2538,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -2654,18 +2550,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.6" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "aws-lc-rs", "ring", @@ -2706,7 +2602,7 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -2738,11 +2634,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.4.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b369d18893388b345804dc0007963c99b7d665ae71d275812d828c6f089640" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation", "core-foundation-sys", "libc", @@ -2767,9 +2663,9 @@ checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -2792,7 +2688,7 @@ checksum = "fca2da10b1f1623f47130256065e05e94fd7a98dbd26a780a4c5de831b21e5c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -2803,19 +2699,19 @@ checksum = "8f68cf7478db8b81abcf71b6d195a34a4891bd3d39868731c4d73194d74ec7a3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "serde-xml-rs" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53630160a98edebde0123eb4dfd0fce6adff091b2305db3154a9e920206eb510" +checksum = "cc2215ce3e6a77550b80a1c37251b7d294febaf42e36e21b7b411e0bf54d540d" dependencies = [ "log", "serde", - "thiserror 1.0.69", - "xml-rs", + "thiserror 2.0.17", + "xml", ] [[package]] @@ -2840,22 +2736,35 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", +] + +[[package]] +name = "serde_json" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af14725505314343e673e9ecb7cd7e8a36aa9791eb936235a3567cc31447ae4" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", ] [[package]] @@ -2866,7 +2775,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -2942,10 +2851,11 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] @@ -2991,7 +2901,7 @@ dependencies = [ "blake2", "chacha20poly1305", "curve25519-dalek", - "getrandom 0.3.3", + "getrandom 0.3.4", "p256 0.13.2", "ring", "rustc_version", @@ -3001,12 +2911,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3030,9 +2940,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_assertions" @@ -3059,9 +2969,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -3088,7 +2998,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -3099,15 +3009,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.22.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", - "rustix 1.1.2", - "windows-sys 0.61.0", + "rustix 1.1.3", + "windows-sys 0.61.2", ] [[package]] @@ -3118,9 +3028,9 @@ checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "test-log" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e33b98a582ea0be1168eba097538ee8dd4bbe0f2b01b22ac92ea30054e5be7b" +checksum = "37d53ac171c92a39e4769491c4b4dde7022c60042254b5fc044ae409d34a24d4" dependencies = [ "env_logger", "test-log-macros", @@ -3129,13 +3039,13 @@ dependencies = [ [[package]] name = "test-log-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451b374529930d7601b1eef8d32bc79ae870b6079b069401709c2a8bf9e75f36" +checksum = "be35209fd0781c5401458ab66e4f98accf63553e8fae7425503e92fdd319783b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -3155,11 +3065,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.16", + "thiserror-impl 2.0.17", ] [[package]] @@ -3170,18 +3080,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -3226,40 +3136,37 @@ dependencies = [ [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", "socket2", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "tokio-rustls" -version = "0.26.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f63835928ca123f1bef57abbcd23bb2ba0ac9ae1235f1e65bda0d06e7786bd" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -3295,9 +3202,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -3308,18 +3215,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.2" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.23.6" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap", "toml_datetime", @@ -3329,18 +3236,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.3" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" dependencies = [ "winnow", ] [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -3349,20 +3256,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -3381,9 +3288,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", @@ -3403,7 +3310,7 @@ version = "0.1.0" source = "git+https://github.com/trussed-dev/trussed.git?rev=024e0eca5fb7dbd2457831f7c7bffe4341e08775#024e0eca5fb7dbd2457831f7c7bffe4341e08775" dependencies = [ "aes", - "bitflags 2.9.4", + "bitflags 2.10.0", "cbc", "cbor-smol", "cfg-if", @@ -3516,21 +3423,21 @@ dependencies = [ "rustls", "rustls-pki-types", "sha1", - "thiserror 2.0.16", + "thiserror 2.0.17", "utf-8", ] [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-xid" @@ -3568,13 +3475,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "js-sys", - "serde", + "serde_core", "wasm-bindgen", ] @@ -3612,15 +3519,6 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] - [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" @@ -3632,9 +3530,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.103" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -3643,25 +3541,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-macro" -version = "0.2.103" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3669,22 +3553,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.103" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.106", - "wasm-bindgen-backend", + "syn 2.0.111", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.103" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -3723,7 +3607,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -3789,24 +3673,24 @@ dependencies = [ [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -3817,9 +3701,9 @@ checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-numerics" @@ -3882,16 +3766,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.5", ] [[package]] name = "windows-sys" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -3927,19 +3811,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.1.3", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -3965,9 +3849,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -3983,9 +3867,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -4001,9 +3885,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -4013,9 +3897,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -4031,9 +3915,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -4049,9 +3933,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -4067,9 +3951,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -4085,15 +3969,15 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -4127,41 +4011,41 @@ dependencies = [ "nom", "oid-registry", "rusticata-macros", - "thiserror 2.0.16", + "thiserror 2.0.17", "time", ] [[package]] -name = "xml-rs" -version = "0.8.27" +name = "xml" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" +checksum = "2df5825faced2427b2da74d9100f1e2e93c533fff063506a81ede1cf517b2e7e" [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] @@ -4174,5 +4058,11 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] + +[[package]] +name = "zmij" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0095ecd462946aa3927d9297b63ef82fb9a5316d7a37d134eeb36e58228615a" diff --git a/libwebauthn/Cargo.toml b/libwebauthn/Cargo.toml index e39043a..9f623b8 100644 --- a/libwebauthn/Cargo.toml +++ b/libwebauthn/Cargo.toml @@ -71,6 +71,7 @@ snow = { version = "0.10", features = ["use-p256"] } ctap-types = { version = "0.4.0" } btleplug = "0.11.7" thiserror = "2.0.12" +serde_json = "1.0.141" apdu-core = { version = "0.4.0", optional = true } apdu = { version = "0.4.0", optional = true } pcsc = { version = "2.9.0", optional = true } diff --git a/libwebauthn/examples/prf_test.rs b/libwebauthn/examples/prf_test.rs index 5d0178e..f4e81cd 100644 --- a/libwebauthn/examples/prf_test.rs +++ b/libwebauthn/examples/prf_test.rs @@ -14,7 +14,7 @@ use tracing_subscriber::{self, EnvFilter}; use libwebauthn::ops::webauthn::{ GetAssertionHmacOrPrfInput, GetAssertionRequest, GetAssertionRequestExtensions, PRFValue, - UserVerificationRequirement, + PrfInput, UserVerificationRequirement, }; use libwebauthn::pin::PinRequestReason; use libwebauthn::proto::ctap2::{Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialType}; @@ -126,10 +126,11 @@ pub async fn main() -> Result<(), Box> { }); let eval_by_credential = HashMap::new(); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); + run_success_test( &mut channel, &credential, @@ -155,7 +156,7 @@ async fn run_success_test( allow: vec![credential.clone()], user_verification: UserVerificationRequirement::Preferred, extensions: Some(GetAssertionRequestExtensions { - hmac_or_prf, + hmac_or_prf: Some(hmac_or_prf), ..Default::default() }), timeout: TIMEOUT, diff --git a/libwebauthn/examples/webauthn_cable.rs b/libwebauthn/examples/webauthn_cable.rs index f6ae857..5bcb770 100644 --- a/libwebauthn/examples/webauthn_cable.rs +++ b/libwebauthn/examples/webauthn_cable.rs @@ -19,7 +19,8 @@ use tokio::time::sleep; use tracing_subscriber::{self, EnvFilter}; use libwebauthn::ops::webauthn::{ - GetAssertionRequest, MakeCredentialRequest, ResidentKeyRequirement, UserVerificationRequirement, + GetAssertionRequest, GetAssertionRequestExtensions, MakeCredentialRequest, + ResidentKeyRequirement, UserVerificationRequirement, }; use libwebauthn::proto::ctap2::{ Ctap2CredentialType, Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialRpEntity, @@ -161,7 +162,7 @@ pub async fn main() -> Result<(), Box> { hash: Vec::from(challenge), allow: vec![credential], user_verification: UserVerificationRequirement::Discouraged, - extensions: None, + extensions: Some(GetAssertionRequestExtensions::default()), timeout: TIMEOUT, }; diff --git a/libwebauthn/examples/webauthn_extensions_hid.rs b/libwebauthn/examples/webauthn_extensions_hid.rs index 22f6bdd..1feef5f 100644 --- a/libwebauthn/examples/webauthn_extensions_hid.rs +++ b/libwebauthn/examples/webauthn_extensions_hid.rs @@ -11,8 +11,7 @@ use tracing_subscriber::{self, EnvFilter}; use libwebauthn::ops::webauthn::{ CredentialProtectionExtension, CredentialProtectionPolicy, GetAssertionHmacOrPrfInput, - GetAssertionRequest, GetAssertionRequestExtensions, HMACGetSecretInput, - MakeCredentialHmacOrPrfInput, MakeCredentialLargeBlobExtension, MakeCredentialRequest, + GetAssertionRequest, GetAssertionRequestExtensions, HMACGetSecretInput, MakeCredentialRequest, MakeCredentialsRequestExtensions, ResidentKeyRequirement, UserVerificationRequirement, }; use libwebauthn::pin::PinRequestReason; @@ -88,10 +87,11 @@ pub async fn main() -> Result<(), Box> { policy: CredentialProtectionPolicy::UserVerificationRequired, enforce_policy: true, }), - cred_blob: Some(r"My own little blob".into()), - large_blob: MakeCredentialLargeBlobExtension::None, + cred_blob: Some("My own little blob".as_bytes().into()), + large_blob: None, min_pin_length: Some(true), - hmac_or_prf: MakeCredentialHmacOrPrfInput::HmacGetSecret, + hmac_create_secret: Some(true), + prf: None, cred_props: Some(true), }; @@ -148,11 +148,13 @@ pub async fn main() -> Result<(), Box> { allow: vec![credential], user_verification: UserVerificationRequirement::Discouraged, extensions: Some(GetAssertionRequestExtensions { - cred_blob: Some(true), - hmac_or_prf: GetAssertionHmacOrPrfInput::HmacGetSecret(HMACGetSecretInput { - salt1: [1; 32], - salt2: None, - }), + cred_blob: true, + hmac_or_prf: Some(GetAssertionHmacOrPrfInput::HmacGetSecret( + HMACGetSecretInput { + salt1: [1; 32], + salt2: None, + }, + )), ..Default::default() }), timeout: TIMEOUT, diff --git a/libwebauthn/examples/webauthn_hid.rs b/libwebauthn/examples/webauthn_hid.rs index e1dd6e2..d05fb46 100644 --- a/libwebauthn/examples/webauthn_hid.rs +++ b/libwebauthn/examples/webauthn_hid.rs @@ -10,7 +10,8 @@ use tokio::sync::broadcast::Receiver; use tracing_subscriber::{self, EnvFilter}; use libwebauthn::ops::webauthn::{ - GetAssertionRequest, MakeCredentialRequest, ResidentKeyRequirement, UserVerificationRequirement, + GetAssertionRequest, GetAssertionRequestExtensions, MakeCredentialRequest, + ResidentKeyRequirement, UserVerificationRequirement, }; use libwebauthn::pin::PinRequestReason; use libwebauthn::proto::ctap2::{ @@ -128,7 +129,7 @@ pub async fn main() -> Result<(), Box> { hash: Vec::from(challenge), allow: vec![credential], user_verification: UserVerificationRequirement::Discouraged, - extensions: None, + extensions: Some(GetAssertionRequestExtensions::default()), timeout: TIMEOUT, }; diff --git a/libwebauthn/examples/webauthn_json_hid.rs b/libwebauthn/examples/webauthn_json_hid.rs new file mode 100644 index 0000000..4b08ac2 --- /dev/null +++ b/libwebauthn/examples/webauthn_json_hid.rs @@ -0,0 +1,166 @@ +use std::error::Error; +use std::io::{self, Write}; +use std::time::Duration; + +use libwebauthn::UvUpdate; +use text_io::read; +use tokio::sync::broadcast::Receiver; +use tracing_subscriber::{self, EnvFilter}; + +use libwebauthn::ops::webauthn::{ + GetAssertionRequest, MakeCredentialRequest, RelyingPartyId, WebAuthnIDL as _, +}; +use libwebauthn::pin::PinRequestReason; +use libwebauthn::transport::hid::list_devices; +use libwebauthn::transport::{Channel as _, Device}; +use libwebauthn::webauthn::{Error as WebAuthnError, WebAuthn}; + +const TIMEOUT: Duration = Duration::from_secs(10); + +fn setup_logging() { + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .without_time() + .init(); +} + +async fn handle_updates(mut state_recv: Receiver) { + while let Ok(update) = state_recv.recv().await { + match update { + UvUpdate::PresenceRequired => println!("Please touch your device!"), + UvUpdate::UvRetry { attempts_left } => { + print!("UV failed."); + if let Some(attempts_left) = attempts_left { + print!(" You have {attempts_left} attempts left."); + } + } + UvUpdate::PinRequired(update) => { + let mut attempts_str = String::new(); + if let Some(attempts) = update.attempts_left { + attempts_str = format!(". You have {attempts} attempts left!"); + }; + + match update.reason { + PinRequestReason::RelyingPartyRequest => println!("RP required a PIN."), + PinRequestReason::AuthenticatorPolicy => { + println!("Your device requires a PIN.") + } + PinRequestReason::FallbackFromUV => { + println!("UV failed too often and is blocked. Falling back to PIN.") + } + } + print!("PIN: Please enter the PIN for your authenticator{attempts_str}: "); + io::stdout().flush().unwrap(); + let pin_raw: String = read!("{}\n"); + + if pin_raw.is_empty() { + println!("PIN: No PIN provided, cancelling operation."); + update.cancel(); + } else { + let _ = update.send_pin(&pin_raw); + } + } + } + } +} + +#[tokio::main] +pub async fn main() -> Result<(), Box> { + setup_logging(); + + let devices = list_devices().await.unwrap(); + println!("Devices found: {:?}", devices); + + for mut device in devices { + println!("Selected HID authenticator: {}", &device); + let mut channel = device.channel().await?; + channel.wink(TIMEOUT).await?; + + // Relying + let rpid = RelyingPartyId("example.org".to_owned()); + let request_json = r#" + { + "rp": { + "id": "example.org", + "name": "Example Relying Party" + }, + "user": { + "id": "MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2Nzg", + "name": "Mario Rossi", + "displayName": "Mario Rossi" + }, + "challenge": "MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2Nzg", + "pubKeyCredParams": [ + {"type": "public-key", "alg": -7} + ], + "timeout": 60000, + "excludeCredentials": [], + "authenticatorSelection": { + "residentKey": "discouraged", + "userVerification": "preferred" + }, + "attestation": "none" + } + "#; + let make_credentials_request: MakeCredentialRequest = + MakeCredentialRequest::from_json(&rpid, request_json) + .expect("Failed to parse request JSON"); + println!( + "WebAuthn MakeCredential request: {:?}", + make_credentials_request + ); + + let state_recv = channel.get_ux_update_receiver(); + tokio::spawn(handle_updates(state_recv)); + + let response = loop { + match channel + .webauthn_make_credential(&make_credentials_request) + .await + { + Ok(response) => break Ok(response), + Err(WebAuthnError::Ctap(ctap_error)) => { + if ctap_error.is_retryable_user_error() { + println!("Oops, try again! Error: {}", ctap_error); + continue; + } + break Err(WebAuthnError::Ctap(ctap_error)); + } + Err(err) => break Err(err), + }; + } + .unwrap(); + println!("WebAuthn MakeCredential response: {:?}", response); + + let request_json = r#" + { + "challenge": "Y3JlZGVudGlhbHMtZm9yLWxpbnV4L2xpYndlYmF1dGhu", + "timeout": 30000, + "rpId": "example.org", + "userVerification": "discouraged" + } + "#; + let get_assertion: GetAssertionRequest = + GetAssertionRequest::from_json(&rpid, request_json) + .expect("Failed to parse request JSON"); + println!("WebAuthn GetAssertion request: {:?}", get_assertion); + + let response = loop { + match channel.webauthn_get_assertion(&get_assertion).await { + Ok(response) => break Ok(response), + Err(WebAuthnError::Ctap(ctap_error)) => { + if ctap_error.is_retryable_user_error() { + println!("Oops, try again! Error: {}", ctap_error); + continue; + } + break Err(WebAuthnError::Ctap(ctap_error)); + } + Err(err) => break Err(err), + }; + } + .unwrap(); + println!("WebAuthn GetAssertion response: {:?}", response); + } + + Ok(()) +} diff --git a/libwebauthn/examples/webauthn_preflight_hid.rs b/libwebauthn/examples/webauthn_preflight_hid.rs index 98d7165..97f876d 100644 --- a/libwebauthn/examples/webauthn_preflight_hid.rs +++ b/libwebauthn/examples/webauthn_preflight_hid.rs @@ -11,8 +11,8 @@ use tokio::sync::broadcast::Receiver; use tracing_subscriber::{self, EnvFilter}; use libwebauthn::ops::webauthn::{ - GetAssertionRequest, GetAssertionResponse, MakeCredentialRequest, ResidentKeyRequirement, - UserVerificationRequirement, + GetAssertionRequest, GetAssertionRequestExtensions, GetAssertionResponse, + MakeCredentialRequest, ResidentKeyRequirement, UserVerificationRequirement, }; use libwebauthn::pin::PinRequestReason; use libwebauthn::proto::ctap2::{ @@ -202,7 +202,7 @@ async fn get_assertion_call( hash: Vec::from(challenge), allow: allow_list, user_verification: UserVerificationRequirement::Discouraged, - extensions: None, + extensions: Some(GetAssertionRequestExtensions::default()), timeout: TIMEOUT, }; diff --git a/libwebauthn/examples/webauthn_prf_hid.rs b/libwebauthn/examples/webauthn_prf_hid.rs index 35d34a7..e279815 100644 --- a/libwebauthn/examples/webauthn_prf_hid.rs +++ b/libwebauthn/examples/webauthn_prf_hid.rs @@ -13,8 +13,8 @@ use tracing_subscriber::{self, EnvFilter}; use libwebauthn::ops::webauthn::{ GetAssertionHmacOrPrfInput, GetAssertionRequest, GetAssertionRequestExtensions, - MakeCredentialHmacOrPrfInput, MakeCredentialRequest, MakeCredentialsRequestExtensions, - PRFValue, ResidentKeyRequirement, UserVerificationRequirement, + MakeCredentialPrfInput, MakeCredentialRequest, MakeCredentialsRequestExtensions, PRFValue, + PrfInput, ResidentKeyRequirement, UserVerificationRequirement, }; use libwebauthn::pin::PinRequestReason; use libwebauthn::proto::ctap2::{ @@ -85,7 +85,7 @@ pub async fn main() -> Result<(), Box> { let challenge: [u8; 32] = thread_rng().gen(); let extensions = MakeCredentialsRequestExtensions { - hmac_or_prf: MakeCredentialHmacOrPrfInput::Prf, + prf: Some(MakeCredentialPrfInput { _eval: None }), ..Default::default() }; @@ -148,10 +148,10 @@ pub async fn main() -> Result<(), Box> { second: None, }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf: GetAssertionHmacOrPrfInput = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_success_test( &mut channel, &credential, @@ -175,10 +175,10 @@ pub async fn main() -> Result<(), Box> { second: None, }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_success_test( &mut channel, &credential, @@ -195,10 +195,10 @@ pub async fn main() -> Result<(), Box> { }); let eval_by_credential = HashMap::new(); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_success_test( &mut channel, &credential, @@ -243,10 +243,10 @@ pub async fn main() -> Result<(), Box> { second: None, }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_success_test( &mut channel, &credential, @@ -284,10 +284,10 @@ pub async fn main() -> Result<(), Box> { second: Some([8; 32]), }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_success_test( &mut channel, &credential, @@ -322,10 +322,10 @@ pub async fn main() -> Result<(), Box> { second: Some([8; 32]), }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_success_test( &mut channel, &credential, @@ -349,10 +349,10 @@ pub async fn main() -> Result<(), Box> { second: None, }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_failed_test( &mut channel, Some(&credential), @@ -373,10 +373,10 @@ pub async fn main() -> Result<(), Box> { second: None, }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_failed_test( &mut channel, Some(&credential), @@ -397,10 +397,10 @@ pub async fn main() -> Result<(), Box> { second: None, }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_failed_test( &mut channel, None, @@ -427,7 +427,7 @@ async fn run_success_test( allow: vec![credential.clone()], user_verification: UserVerificationRequirement::Discouraged, extensions: Some(GetAssertionRequestExtensions { - hmac_or_prf, + hmac_or_prf: Some(hmac_or_prf), ..Default::default() }), timeout: TIMEOUT, @@ -469,7 +469,7 @@ async fn run_failed_test( allow: credential.map(|x| vec![x.clone()]).unwrap_or_default(), user_verification: UserVerificationRequirement::Discouraged, extensions: Some(GetAssertionRequestExtensions { - hmac_or_prf, + hmac_or_prf: Some(hmac_or_prf), ..Default::default() }), timeout: TIMEOUT, diff --git a/libwebauthn/src/fido.rs b/libwebauthn/src/fido.rs index 55ce46b..532a08a 100644 --- a/libwebauthn/src/fido.rs +++ b/libwebauthn/src/fido.rs @@ -273,11 +273,10 @@ mod tests { 0x86, 0xce, 0x19, 0x47, ]; let flag_bits = 0b1100_0101; - let flags = - AuthenticatorDataFlags::USER_PRESENT | - AuthenticatorDataFlags::USER_VERIFIED | - AuthenticatorDataFlags::ATTESTED_CREDENTIALS | - AuthenticatorDataFlags::EXTENSION_DATA; + let flags = AuthenticatorDataFlags::USER_PRESENT + | AuthenticatorDataFlags::USER_VERIFIED + | AuthenticatorDataFlags::ATTESTED_CREDENTIALS + | AuthenticatorDataFlags::EXTENSION_DATA; assert_eq!(flag_bits, flags.bits()); let signature_count = 0; let aaguid = [ @@ -316,7 +315,7 @@ mod tests { flags, signature_count, attested_credential: Some(attested_credential.clone()), - extensions: Some(extensions.clone()) + extensions: Some(extensions.clone()), }; let webauthn_auth_data = auth_data.to_response_bytes().unwrap(); assert_eq!(rp_id_hash, &webauthn_auth_data[..32]); @@ -341,14 +340,8 @@ mod tests { let authdata_wrapped = cbor::to_vec(&ByteBuf::from(webauthn_auth_data)).unwrap(); let auth_data_reparsed: AuthenticatorData = cbor::from_slice(authdata_wrapped.as_slice()).unwrap(); - assert_eq!( - auth_data.rp_id_hash, - auth_data_reparsed.rp_id_hash - ); - assert_eq!( - auth_data.flags.bits(), - auth_data_reparsed.flags.bits() - ); + assert_eq!(auth_data.rp_id_hash, auth_data_reparsed.rp_id_hash); + assert_eq!(auth_data.flags.bits(), auth_data_reparsed.flags.bits()); assert_eq!( auth_data.signature_count, auth_data_reparsed.signature_count @@ -366,9 +359,6 @@ mod tests { attested_credential.credential_public_key, attested_credential_reparsed.credential_public_key ); - assert_eq!( - extensions, - auth_data_reparsed.extensions.unwrap() - ); + assert_eq!(extensions, auth_data_reparsed.extensions.unwrap()); } } diff --git a/libwebauthn/src/ops/u2f.rs b/libwebauthn/src/ops/u2f.rs index c442b81..f21dd6a 100644 --- a/libwebauthn/src/ops/u2f.rs +++ b/libwebauthn/src/ops/u2f.rs @@ -9,7 +9,8 @@ use x509_parser::nom::AsBytes; use super::webauthn::MakeCredentialRequest; use crate::fido::{AttestedCredentialData, AuthenticatorData, AuthenticatorDataFlags}; use crate::ops::webauthn::{ - GetAssertionRequest, GetAssertionResponse, MakeCredentialResponse, UserVerificationRequirement, + GetAssertionRequest, GetAssertionResponse, + MakeCredentialResponse, UserVerificationRequirement, }; use crate::proto::ctap1::{Ctap1RegisterRequest, Ctap1SignRequest}; use crate::proto::ctap1::{Ctap1RegisterResponse, Ctap1SignResponse}; diff --git a/libwebauthn/src/ops/webauthn/client_data.rs b/libwebauthn/src/ops/webauthn/client_data.rs new file mode 100644 index 0000000..9145a48 --- /dev/null +++ b/libwebauthn/src/ops/webauthn/client_data.rs @@ -0,0 +1,36 @@ +use crate::ops::webauthn::Operation; + +use serde::Deserialize; +use sha2::{Digest, Sha256}; + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct ClientData { + pub operation: Operation, + pub challenge: Vec, + pub origin: String, + #[serde(rename = "crossOrigin")] + pub cross_origin: Option, +} + +impl ClientData { + pub fn hash(&self) -> Vec { + let op_str = match &self.operation { + Operation::MakeCredential => "webauthn.create", + Operation::GetAssertion => "webauthn.get", + }; + let challenge_str = base64_url::encode(&self.challenge); + let origin_str = &self.origin; + let cross_origin_str = if self.cross_origin.unwrap_or(false) { + "true" + } else { + "false" + }; + let json = + format!("{{\"type\":\"{op_str}\",\"challenge\":\"{challenge_str}\",\"origin\":\"{origin_str}\",\"crossOrigin\":{cross_origin_str}}}"); + + let mut hasher = Sha256::new(); + hasher.update(json.as_bytes()); + hasher.finalize().to_vec() + } +} + diff --git a/libwebauthn/src/ops/webauthn/get_assertion.rs b/libwebauthn/src/ops/webauthn/get_assertion.rs index 336ff00..dc1e10b 100644 --- a/libwebauthn/src/ops/webauthn/get_assertion.rs +++ b/libwebauthn/src/ops/webauthn/get_assertion.rs @@ -6,6 +6,17 @@ use tracing::{debug, error, trace}; use crate::{ fido::AuthenticatorData, + ops::webauthn::{ + client_data::ClientData, + idl::{ + get::{ + HmacGetSecretInputJson, LargeBlobInputJson, PrfInputJson, + PublicKeyCredentialRequestOptionsJSON, + }, + FromInnerModel, JsonError, + }, + Operation, WebAuthnIDL, + }, pin::PinUvAuthProtocol, proto::ctap2::{ Ctap2AttestationStatement, Ctap2GetAssertionResponseExtensions, @@ -14,9 +25,10 @@ use crate::{ webauthn::CtapError, }; -use super::{DowngradableRequest, SignRequest, UserVerificationRequirement}; +use super::timeout::DEFAULT_TIMEOUT; +use super::{DowngradableRequest, RelyingPartyId, SignRequest, UserVerificationRequirement}; -#[derive(Debug, Default, Clone, Serialize)] +#[derive(Debug, Default, Clone, Serialize, PartialEq)] pub struct PRFValue { #[serde(with = "serde_bytes")] pub first: [u8; 32], @@ -24,7 +36,7 @@ pub struct PRFValue { pub second: Option<[u8; 32]>, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct GetAssertionRequest { pub relying_party_id: String, pub hash: Vec, @@ -34,15 +46,170 @@ pub struct GetAssertionRequest { pub timeout: Duration, } -#[derive(Debug, Default, Clone)] +#[derive(thiserror::Error, Debug)] +pub enum GetAssertionRequestParsingError { + /// The client must throw an "EncodingError" DOMException. + #[error("Invalid JSON format: {0}")] + EncodingError(#[from] JsonError), + + #[error("Unexpected length for {0}: {1}")] + UnexpectedLengthError(String, usize), + + #[error("Not supported: {0}")] + NotSupported(String), +} + +impl WebAuthnIDL for GetAssertionRequest { + type Error = GetAssertionRequestParsingError; + type InnerModel = PublicKeyCredentialRequestOptionsJSON; +} + +/** dictionary PublicKeyCredentialRequestOptionsJSON { + required Base64URLString challenge; + unsigned long timeout; + DOMString rpId; + sequence allowCredentials = []; + DOMString userVerification = "preferred"; + sequence hints = []; + AuthenticationExtensionsClientInputsJSON extensions; +}; */ + +impl FromInnerModel + for GetAssertionRequest +{ + fn from_inner_model( + rpid: &RelyingPartyId, + inner: PublicKeyCredentialRequestOptionsJSON, + ) -> Result { + let hmac_or_prf = match inner.extensions.clone() { + Some(ext) => { + if let Some(prf) = ext.prf { + let prf_input = PrfInput::try_from(prf)?; + Some(GetAssertionHmacOrPrfInput::Prf(prf_input)) + } else if let Some(hmac) = ext.hamc_get_secret { + let hmac_input = HMACGetSecretInput::try_from(hmac)?; + Some(GetAssertionHmacOrPrfInput::HmacGetSecret(hmac_input)) + } else { + None + } + } + None => None, + }; + + let extensions = + inner + .extensions + .as_ref() + .map(|extensions_opt| GetAssertionRequestExtensions { + cred_blob: extensions_opt.cred_blob.unwrap_or(false), + large_blob: extensions_opt + .large_blob + .clone() + .map(Option::::try_from) + .transpose() + .ok() + .flatten() + .flatten(), + hmac_or_prf: hmac_or_prf.clone(), + }); + + let timeout: Duration = inner + .timeout + .map(|s| Duration::from_millis(s.into())) + .unwrap_or(DEFAULT_TIMEOUT); + + let client_data_json = ClientData { + operation: Operation::GetAssertion, + challenge: inner.challenge.to_vec(), + origin: rpid.to_string(), + cross_origin: None, + }; + + Ok(GetAssertionRequest { + relying_party_id: rpid.to_string(), + hash: client_data_json.hash(), + allow: inner + .allow_credentials + .into_iter() + .map(|c| c.into()) + .collect(), + extensions, + user_verification: inner.uv_requirement, + timeout, + }) + } +} + +#[derive(Debug, Clone, PartialEq)] pub enum GetAssertionHmacOrPrfInput { - #[default] - None, HmacGetSecret(HMACGetSecretInput), - Prf { - eval: Option, - eval_by_credential: HashMap, - }, + Prf(PrfInput), +} + +#[derive(Debug, Clone, PartialEq)] +pub struct PrfInput { + pub eval: Option, + pub eval_by_credential: HashMap, +} + +impl TryFrom for PrfInput { + type Error = GetAssertionRequestParsingError; + + fn try_from(value: PrfInputJson) -> Result { + let eval = match value.eval { + Some(value) => Some(PRFValue { + first: value.first.as_slice().try_into().map_err(|_| { + GetAssertionRequestParsingError::UnexpectedLengthError( + "extensions.prf.eval.first".to_string(), + value.first.as_slice().len(), + ) + })?, + second: match value.second { + Some(s) => Some(s.as_slice().try_into().map_err(|_| { + GetAssertionRequestParsingError::UnexpectedLengthError( + "extensions.prf.eval.second".to_string(), + s.as_slice().len(), + ) + })?), + None => None, + }, + }), + None => None, + }; + let eval_by_credential = match value.eval_by_credential { + Some(map) => map + .into_iter() + .map(|(k, v)| { + Ok(( + k, + PRFValue { + first: v.first.as_slice().try_into().map_err(|_| { + GetAssertionRequestParsingError::UnexpectedLengthError( + "extensions.prf.eval_by_credential[i].first".to_string(), + v.first.as_slice().len(), + ) + })?, + second: match v.second { + Some(s) => Some(s.as_slice().try_into().map_err(|_| { + GetAssertionRequestParsingError::UnexpectedLengthError( + "extensions.prf.eval_by_credential[i].second".to_string(), + s.as_slice().len(), + ) + })?), + None => None, + }, + }, + )) + }) + .collect::, GetAssertionRequestParsingError>>()?, + None => HashMap::new(), + }; + + Ok(PrfInput { + eval, + eval_by_credential, + }) + } } #[derive(Debug, Default, Clone, Serialize)] @@ -57,15 +224,50 @@ pub struct HMACGetSecretInput { pub salt2: Option<[u8; 32]>, } -#[derive(Debug, Default, Clone, PartialEq, Eq)] +impl TryFrom for HMACGetSecretInput { + type Error = GetAssertionRequestParsingError; + + fn try_from(value: HmacGetSecretInputJson) -> Result { + let salt1 = value.salt1.as_slice().try_into().map_err(|_| { + GetAssertionRequestParsingError::UnexpectedLengthError( + "extensions.hmacCreateSecret.salt1".to_string(), + value.salt1.as_slice().len(), + ) + })?; + let salt2 = match value.salt2 { + Some(s) => Some(s.as_slice().try_into().map_err(|_| { + GetAssertionRequestParsingError::UnexpectedLengthError( + "extensions.hmacCreateSecret.salt2".to_string(), + s.as_slice().len(), + ) + })?), + None => None, + }; + Ok(HMACGetSecretInput { salt1, salt2 }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] pub enum GetAssertionLargeBlobExtension { - #[default] - None, Read, // Not yet supported // Write(Vec), } +impl TryFrom for Option { + type Error = GetAssertionRequestParsingError; + + fn try_from(value: LargeBlobInputJson) -> Result { + match value.read { + Some(true) => Ok(Some(GetAssertionLargeBlobExtension::Read)), + Some(false) => Err(GetAssertionRequestParsingError::NotSupported( + "largeBlob writes not supported".to_string(), + )), + None => Ok(None), + } + } +} + #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize)] pub struct GetAssertionLargeBlobExtensionOutput { #[serde(skip_serializing_if = "Option::is_none")] @@ -75,11 +277,11 @@ pub struct GetAssertionLargeBlobExtensionOutput { // pub written: Option, } -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct GetAssertionRequestExtensions { - pub cred_blob: Option, - pub hmac_or_prf: GetAssertionHmacOrPrfInput, - pub large_blob: GetAssertionLargeBlobExtension, + pub cred_blob: bool, + pub hmac_or_prf: Option, + pub large_blob: Option, } #[derive(Clone, Debug, Default, Serialize)] @@ -227,3 +429,162 @@ impl DowngradableRequest> for GetAssertionRequest { Ok(downgraded_requests) } } + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use serde_bytes::ByteBuf; + + use crate::ops::webauthn::GetAssertionRequest; + use crate::ops::webauthn::RelyingPartyId; + use crate::proto::ctap2::Ctap2PublicKeyCredentialType; + + use super::*; + + pub const REQUEST_BASE_JSON: &str = r#" + { + "challenge": "Y3JlZGVudGlhbHMtZm9yLWxpbnV4L2xpYndlYmF1dGhu", + "timeout": 30000, + "rpId": "example.org", + "allowCredentials": [ + { + "type": "public-key", + "id": "bXktY3JlZGVudGlhbC1pZA" + } + ], + "userVerification": "preferred" + } + "#; + + fn request_base() -> GetAssertionRequest { + let client_data_json = ClientData { + operation: Operation::GetAssertion, + challenge: base64_url::decode("Y3JlZGVudGlhbHMtZm9yLWxpbnV4L2xpYndlYmF1dGhu").unwrap(), + origin: "example.org".to_string(), + cross_origin: None, + }; + GetAssertionRequest { + relying_party_id: "example.org".to_owned(), + hash: client_data_json.hash(), + allow: vec![Ctap2PublicKeyCredentialDescriptor { + r#type: Ctap2PublicKeyCredentialType::PublicKey, + id: ByteBuf::from(base64_url::decode("bXktY3JlZGVudGlhbC1pZA").unwrap()), + transports: None, + }], + extensions: None, // No extensions key in the base JSON + user_verification: UserVerificationRequirement::Preferred, + timeout: Duration::from_secs(30), + } + } + + fn json_field_add(str: &str, field: &str, value: &str) -> String { + let mut v: serde_json::Value = serde_json::from_str(str).unwrap(); + v.as_object_mut() + .unwrap() + .insert(field.to_owned(), serde_json::from_str(value).unwrap()); + serde_json::to_string(&v).unwrap() + } + + fn json_field_rm(str: &str, field: &str) -> String { + let mut v: serde_json::Value = serde_json::from_str(str).unwrap(); + v.as_object_mut().unwrap().remove(field); + serde_json::to_string(&v).unwrap() + } + + #[test] + fn test_request_from_json_base() { + let rpid = RelyingPartyId::try_from("example.org").unwrap(); + let req: GetAssertionRequest = + GetAssertionRequest::from_json(&rpid, REQUEST_BASE_JSON).unwrap(); + assert_eq!(req, request_base()); + } + + #[test] + fn test_request_from_json_ignore_missing_rp_id() { + let rpid = RelyingPartyId::try_from("example.org").unwrap(); + let req_json = json_field_rm(REQUEST_BASE_JSON, "rpId"); + + let req: GetAssertionRequest = GetAssertionRequest::from_json(&rpid, &req_json).unwrap(); + assert_eq!(req, request_base()); + } + + #[test] + fn test_request_from_json_ignore_request_rp_id() { + let rpid = RelyingPartyId::try_from("example.org").unwrap(); + let req_json = json_field_rm(REQUEST_BASE_JSON, "rpId"); + let req_json = json_field_add(&req_json, "rpId", r#""another-example.org""#); + + let req: GetAssertionRequest = GetAssertionRequest::from_json(&rpid, &req_json).unwrap(); + assert_eq!(req, request_base()); + } + + #[test] + fn test_request_from_json_ignore_missing_allow_credentials() { + let rpid = RelyingPartyId::try_from("example.org").unwrap(); + let req_json = json_field_rm(REQUEST_BASE_JSON, "allowCredentials"); + + let req: GetAssertionRequest = GetAssertionRequest::from_json(&rpid, &req_json).unwrap(); + assert_eq!( + req, + GetAssertionRequest { + allow: vec![], + ..request_base() + } + ); + } + + #[test] + fn test_request_from_json_default_timeout() { + let rpid = RelyingPartyId::try_from("example.org").unwrap(); + let req_json = json_field_rm(REQUEST_BASE_JSON, "timeout"); + + let req: GetAssertionRequest = GetAssertionRequest::from_json(&rpid, &req_json).unwrap(); + assert_eq!(req.timeout, DEFAULT_TIMEOUT); + } + + #[test] + fn test_request_from_json_empty_extensions() { + // Test that "extensions": {} results in Some(default) not None + // This is important for strict portals that distinguish between + // no extensions key vs empty extensions object + let rpid = RelyingPartyId::try_from("example.org").unwrap(); + let req_json = json_field_add(REQUEST_BASE_JSON, "extensions", r#"{}"#); + + let req: GetAssertionRequest = GetAssertionRequest::from_json(&rpid, &req_json).unwrap(); + assert_eq!( + req.extensions, + Some(GetAssertionRequestExtensions::default()) + ); + } + + #[test] + #[ignore] // FIXME(#134) allow arbitrary size input + fn test_request_from_json_prf_extension() { + let rpid = RelyingPartyId::try_from("example.org").unwrap(); + let req_json = json_field_add( + REQUEST_BASE_JSON, + "extensions", + r#"{"prf":{"eval":{"first": "second"}}}"#, + ); + + let req: GetAssertionRequest = GetAssertionRequest::from_json(&rpid, &req_json).unwrap(); + if let Some(GetAssertionRequestExtensions { + hmac_or_prf: + Some(GetAssertionHmacOrPrfInput::Prf(PrfInput { + eval: Some(ref prf_value), + .. + })), + .. + }) = &req.extensions + { + assert_eq!(&prf_value.first[..], b"first"); + assert_eq!( + prf_value.second.as_ref().map(|s| &s[..]), + Some(&b"second"[..]) + ); + } else { + panic!("Expected PRF extension with correct values"); + } + } +} diff --git a/libwebauthn/src/ops/webauthn/idl/base64url.rs b/libwebauthn/src/ops/webauthn/idl/base64url.rs new file mode 100644 index 0000000..8d5e43e --- /dev/null +++ b/libwebauthn/src/ops/webauthn/idl/base64url.rs @@ -0,0 +1,67 @@ +use std::ops::Deref; + +use base64_url; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, PartialEq)] +pub struct Base64UrlString(pub Vec); + +impl From> for Base64UrlString { + fn from(bytes: Vec) -> Self { + Base64UrlString(bytes) + } +} + +impl From<&[u8]> for Base64UrlString { + fn from(bytes: &[u8]) -> Self { + Base64UrlString(bytes.to_vec()) + } +} + +impl Deref for Base64UrlString { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + &self.0 + } +} + +impl AsRef<[u8]> for Base64UrlString { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl<'de> Deserialize<'de> for Base64UrlString { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s: String = Deserialize::deserialize(deserializer)?; + base64_url::decode(&s) + .map_err(serde::de::Error::custom) + .map(|bytes| Base64UrlString(bytes)) + } +} + +impl Serialize for Base64UrlString { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let encoded = base64_url::encode(&self.0); + serializer.serialize_str(&encoded) + } +} + +impl Into> for Base64UrlString { + fn into(self) -> Vec { + self.0 + } +} + +impl Base64UrlString { + pub fn as_slice(&self) -> &[u8] { + &self.0 + } +} diff --git a/libwebauthn/src/ops/webauthn/idl/create.rs b/libwebauthn/src/ops/webauthn/idl/create.rs new file mode 100644 index 0000000..c2bf23c --- /dev/null +++ b/libwebauthn/src/ops/webauthn/idl/create.rs @@ -0,0 +1,60 @@ +use super::Base64UrlString; +use crate::{ + ops::webauthn::{ + MakeCredentialsRequestExtensions, ResidentKeyRequirement, UserVerificationRequirement, + }, + proto::ctap2::{ + Ctap2CredentialType, Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialRpEntity, + }, +}; + +use serde::Deserialize; + +/** + * https://www.w3.org/TR/webauthn-3/#sctn-parseCreationOptionsFromJSON + */ + +#[derive(Debug, Clone, Deserialize)] +pub struct AuthenticatorSelectionCriteria { + #[serde(rename = "authenticatorAttachment")] + pub authenticator_attachment: Option, + #[serde(rename = "residentKey")] + pub resident_key: Option, + #[serde(rename = "requireResidentKey")] + #[serde(default)] + pub require_resident_key: bool, + #[serde(rename = "userVerification")] + #[serde(default = "default_user_verification")] + pub user_verification: UserVerificationRequirement, +} + +fn default_user_verification() -> UserVerificationRequirement { + UserVerificationRequirement::Preferred +} + +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct PublicKeyCredentialUserEntity { + pub id: Base64UrlString, + pub name: String, + #[serde(rename = "displayName")] + pub display_name: String, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct PublicKeyCredentialCreationOptionsJSON { + pub rp: Ctap2PublicKeyCredentialRpEntity, + pub user: PublicKeyCredentialUserEntity, + pub challenge: Base64UrlString, + #[serde(rename = "pubKeyCredParams")] + pub params: Vec, + pub timeout: Option, + #[serde(rename = "excludeCredentials")] + pub exclude_credentials: Vec, + #[serde(rename = "authenticatorSelection")] + pub authenticator_selection: Option, + pub hints: Option>, + pub attestation: Option, + #[serde(rename = "attestationFormats")] + pub attestation_formats: Option>, + pub extensions: Option, +} diff --git a/libwebauthn/src/ops/webauthn/idl/get.rs b/libwebauthn/src/ops/webauthn/idl/get.rs new file mode 100644 index 0000000..3267a69 --- /dev/null +++ b/libwebauthn/src/ops/webauthn/idl/get.rs @@ -0,0 +1,81 @@ +use std::collections::HashMap; + +use serde::Deserialize; +use serde_bytes::ByteBuf; + +use crate::{ + ops::webauthn::{Base64UrlString, UserVerificationRequirement}, + proto::ctap2::{ + Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialType, Ctap2Transport, + }, +}; + +#[derive(Deserialize, Debug, Clone)] +pub struct PublicKeyCredentialRequestOptionsJSON { + pub challenge: Base64UrlString, + pub timeout: Option, + #[serde(rename = "rpId")] + pub relying_party_id: Option, + #[serde(rename = "allowCredentials")] + #[serde(default)] + pub allow_credentials: Vec, + #[serde(rename = "userVerification")] + pub uv_requirement: UserVerificationRequirement, + #[serde(default)] + pub hints: Vec, + pub extensions: Option, +} + +#[derive(Debug, Clone, Deserialize, PartialEq)] +pub struct PublicKeyCredentialDescriptorJSON { + pub id: Base64UrlString, + pub r#type: Ctap2PublicKeyCredentialType, + + #[serde(skip_serializing_if = "Option::is_none")] + pub transports: Option>, +} + +impl Into for PublicKeyCredentialDescriptorJSON { + fn into(self) -> Ctap2PublicKeyCredentialDescriptor { + Ctap2PublicKeyCredentialDescriptor { + r#type: self.r#type, + id: ByteBuf::from(self.id), + transports: self.transports, + } + } +} + +#[derive(Debug, Clone, Default, Deserialize)] +pub struct GetAssertionRequestExtensionsJSON { + #[serde(rename = "getCredBlob")] + pub cred_blob: Option, + #[serde(rename = "largeBlobKey")] + pub large_blob: Option, + #[serde(rename = "hmacCreateSecret")] + pub hamc_get_secret: Option, + #[serde(rename = "prf")] + pub prf: Option, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct LargeBlobInputJson { + pub read: Option, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct PrfInputJson { + pub eval: Option, + pub eval_by_credential: Option>, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct PrfValuesJson { + pub first: Base64UrlString, + pub second: Option, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct HmacGetSecretInputJson { + pub salt1: Base64UrlString, + pub salt2: Option, +} diff --git a/libwebauthn/src/ops/webauthn/idl/mod.rs b/libwebauthn/src/ops/webauthn/idl/mod.rs new file mode 100644 index 0000000..c392756 --- /dev/null +++ b/libwebauthn/src/ops/webauthn/idl/mod.rs @@ -0,0 +1,39 @@ +mod base64url; +pub mod create; +pub mod get; +pub mod rpid; + +pub use base64url::Base64UrlString; + +use rpid::RelyingPartyId; + +use serde::de::DeserializeOwned; +use serde_json; + +pub type JsonError = serde_json::Error; + +pub trait WebAuthnIDL: Sized +where + E: std::error::Error, // Validation error type. + Self: FromInnerModel, +{ + /// An error type that can be returned when deserializing from JSON, including + /// JSON parsing errors and any additional validation errors. + type Error: std::error::Error + From + From; + + /// The JSON model that this IDL can deserialize from. + type InnerModel: DeserializeOwned; + + fn from_json(rpid: &RelyingPartyId, json: &str) -> Result { + let inner_model: Self::InnerModel = serde_json::from_str(json)?; + Self::from_inner_model(rpid, inner_model).map_err(From::from) + } +} + +pub trait FromInnerModel: Sized +where + T: DeserializeOwned, + E: std::error::Error, +{ + fn from_inner_model(rpid: &RelyingPartyId, inner: T) -> Result; +} diff --git a/libwebauthn/src/ops/webauthn/idl/rpid.rs b/libwebauthn/src/ops/webauthn/idl/rpid.rs new file mode 100644 index 0000000..a92b12b --- /dev/null +++ b/libwebauthn/src/ops/webauthn/idl/rpid.rs @@ -0,0 +1,49 @@ +use serde::Deserialize; +use std::{convert::TryFrom, ops::Deref}; + +#[derive(Clone, Debug)] +pub struct RelyingPartyId(pub String); + +impl Deref for RelyingPartyId { + type Target = str; + + fn deref(&self) -> &str { + &self.0 + } +} + +impl Into for RelyingPartyId { + fn into(self) -> String { + self.0 + } +} + +#[derive(thiserror::Error, Debug, Clone)] +// TODO(#137): Validate RelyingPartyId +pub enum Error { + #[error("Empty Relying Party ID is not allowed")] + EmptyRelyingPartyId, +} + +impl TryFrom<&str> for RelyingPartyId { + type Error = Error; + + fn try_from(value: &str) -> Result { + // TODO(#137): Validate RelyingPartyId, including IDNA normalization + // and checking for valid characters. + match value { + "" => Err(Error::EmptyRelyingPartyId), + _ => Ok(RelyingPartyId(value.to_string())), + } + } +} + +impl<'de> Deserialize<'de> for RelyingPartyId { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + RelyingPartyId::try_from(s.as_str()).map_err(serde::de::Error::custom) + } +} diff --git a/libwebauthn/src/ops/webauthn/make_credential.rs b/libwebauthn/src/ops/webauthn/make_credential.rs index 1526c98..0e6dd25 100644 --- a/libwebauthn/src/ops/webauthn/make_credential.rs +++ b/libwebauthn/src/ops/webauthn/make_credential.rs @@ -2,11 +2,20 @@ use std::time::Duration; use ctap_types::ctap2::credential_management::CredentialProtectionPolicy as Ctap2CredentialProtectionPolicy; use serde::{Deserialize, Serialize}; +use serde_json::{self, Value as JsonValue}; use sha2::{Digest, Sha256}; use tracing::{debug, instrument, trace}; use crate::{ fido::AuthenticatorData, + ops::webauthn::{ + client_data::ClientData, + idl::{ + create::PublicKeyCredentialCreationOptionsJSON, Base64UrlString, FromInnerModel, + JsonError, WebAuthnIDL, + }, + Operation, RelyingPartyId, + }, proto::{ ctap1::{Ctap1RegisteredKey, Ctap1Version}, ctap2::{ @@ -18,6 +27,7 @@ use crate::{ }, }; +use super::timeout::DEFAULT_TIMEOUT; use super::{DowngradableRequest, RegisterRequest, UserVerificationRequirement}; #[derive(Debug, Clone)] @@ -62,22 +72,18 @@ impl MakeCredentialsResponseUnsignedExtensions { let mut hmac_create_secret = None; let mut prf = None; if let Some(signed_extensions) = signed_extensions { - (hmac_create_secret, prf) = if let Some(incoming_ext) = &request.extensions { - match &incoming_ext.hmac_or_prf { - MakeCredentialHmacOrPrfInput::None => (None, None), - MakeCredentialHmacOrPrfInput::HmacGetSecret => { - (signed_extensions.hmac_secret, None) - } - MakeCredentialHmacOrPrfInput::Prf => ( - None, - Some(MakeCredentialPrfOutput { - enabled: signed_extensions.hmac_secret, - }), - ), + if let Some(incoming_ext) = &request.extensions { + // hmacCreateSecret and prf can both be requested and returned independently. + // Both map to the same underlying CTAP2 hmac-secret extension. + if incoming_ext.hmac_create_secret.is_some() { + hmac_create_secret = signed_extensions.hmac_secret; } - } else { - (None, None) - }; + if incoming_ext.prf.is_some() { + prf = Some(MakeCredentialPrfOutput { + enabled: signed_extensions.hmac_secret, + }); + } + } } // credProps extension @@ -126,7 +132,12 @@ impl MakeCredentialsResponseUnsignedExtensions { // largeBlob extension // https://www.w3.org/TR/webauthn-3/#sctn-large-blob-extension - let large_blob = match &request.extensions.as_ref().map(|x| &x.large_blob) { + let large_blob = match &request + .extensions + .as_ref() + .and_then(|x| x.large_blob.as_ref()) + .map(|x| x.support) + { None | Some(MakeCredentialLargeBlobExtension::None) => None, // Not requested, so we don't give an answer Some(MakeCredentialLargeBlobExtension::Preferred) | Some(MakeCredentialLargeBlobExtension::Required) => { @@ -149,14 +160,17 @@ impl MakeCredentialsResponseUnsignedExtensions { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Deserialize, PartialEq)] pub enum ResidentKeyRequirement { + #[serde(rename = "required")] Required, + #[serde(rename = "preferred")] Preferred, + #[serde(rename = "discouraged", other)] Discouraged, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct MakeCredentialRequest { pub hash: Vec, pub origin: String, @@ -175,22 +189,81 @@ pub struct MakeCredentialRequest { pub timeout: Duration, } -#[derive(Debug, Default, Clone)] -pub enum MakeCredentialHmacOrPrfInput { - #[default] - None, - HmacGetSecret, - Prf, - // The spec tells us that in theory, we could hand in - // an `eval` here, IF the CTAP2 would get an additional - // extension to handle that. There is no such CTAP-extension - // right now, so we don't expose it for now, as it would just - // be ignored anyways. - // https://w3c.github.io/webauthn/#prf - // "If eval is present and a future extension to [FIDO-CTAP] permits evaluation of the PRF at creation time, configure hmac-secret inputs accordingly: .." - // Prf { - // eval: Option, - // }, +impl FromInnerModel + for MakeCredentialRequest +{ + fn from_inner_model( + rpid: &RelyingPartyId, + inner: PublicKeyCredentialCreationOptionsJSON, + ) -> Result { + let resident_key = if inner + .authenticator_selection + .as_ref() + .and_then(|s| Some(s.require_resident_key)) + == Some(true) + { + Some(ResidentKeyRequirement::Required) + } else { + inner + .authenticator_selection + .as_ref() + .and_then(|s| s.resident_key) + }; + + let user_verification = inner + .authenticator_selection + .as_ref() + .map_or(UserVerificationRequirement::Discouraged, |s| { + s.user_verification + }); + + let timeout: Duration = inner + .timeout + .map(|s| Duration::from_millis(s.into())) + .unwrap_or(DEFAULT_TIMEOUT); + + let client_data_json = ClientData { + operation: Operation::MakeCredential, + challenge: inner.challenge.to_vec(), + origin: rpid.to_string(), + cross_origin: None, + }; + + Ok(Self { + hash: client_data_json.hash(), + origin: rpid.to_owned().into(), + relying_party: inner.rp, + user: inner.user.into(), + resident_key, + user_verification, + algorithms: inner.params, + exclude: if inner.exclude_credentials.is_empty() { + None + } else { + Some(inner.exclude_credentials) + }, + extensions: inner.extensions, + timeout: timeout, + }) + } +} + +#[derive(thiserror::Error, Debug)] +pub enum MakeCredentialRequestParsingError { + /// The client must throw an "EncodingError" DOMException. + #[error("Invalid JSON format: {0}")] + EncodingError(#[from] JsonError), +} + +impl WebAuthnIDL for MakeCredentialRequest { + type Error = MakeCredentialRequestParsingError; + type InnerModel = PublicKeyCredentialCreationOptionsJSON; +} + +#[derive(Debug, Clone, Deserialize, PartialEq)] +pub struct MakeCredentialPrfInput { + #[serde(rename = "eval")] + pub _eval: Option, } #[derive(Debug, Default, Clone, Serialize, PartialEq)] @@ -199,9 +272,11 @@ pub struct MakeCredentialPrfOutput { pub enabled: Option, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, PartialEq)] pub struct CredentialProtectionExtension { + #[serde(rename = "credentialProtectionPolicy")] pub policy: CredentialProtectionPolicy, + #[serde(rename = "enforceCredentialProtectionPolicy")] pub enforce_policy: bool, } @@ -254,13 +329,20 @@ pub struct CredentialPropsExtension { pub rk: Option, } -#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Default, Clone, Deserialize, PartialEq)] +pub struct MakeCredentialLargeBlobExtensionInput { + pub support: MakeCredentialLargeBlobExtension, +} + +#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)] pub enum MakeCredentialLargeBlobExtension { - #[default] - None, + #[serde(rename = "preferred")] Preferred, + #[serde(rename = "required")] Required, + #[default] + #[serde(other)] + None, } #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize)] @@ -269,14 +351,22 @@ pub struct MakeCredentialLargeBlobExtensionOutput { pub supported: Option, } -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, Deserialize, PartialEq)] pub struct MakeCredentialsRequestExtensions { + #[serde(rename = "credProps")] pub cred_props: Option, + #[serde(rename = "credProtect")] pub cred_protect: Option, - pub cred_blob: Option>, - pub large_blob: MakeCredentialLargeBlobExtension, + #[serde(rename = "credBlob")] + pub cred_blob: Option, + #[serde(rename = "largeBlob")] + pub large_blob: Option, + #[serde(rename = "minPinLength")] pub min_pin_length: Option, - pub hmac_or_prf: MakeCredentialHmacOrPrfInput, + #[serde(rename = "hmacCreateSecret")] + pub hmac_create_secret: Option, + #[serde(rename = "prf")] + pub prf: Option, } pub type MakeCredentialsResponseExtensions = Ctap2MakeCredentialsResponseExtensions; @@ -367,3 +457,178 @@ impl DowngradableRequest for MakeCredentialRequest { Ok(downgraded) } } + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use crate::ops::webauthn::MakeCredentialRequest; + use crate::ops::webauthn::RelyingPartyId; + use crate::proto::ctap2::Ctap2PublicKeyCredentialType; + + use super::*; + + pub const REQUEST_BASE_JSON: &str = r#" + { + "rp": { + "id": "example.org", + "name": "example.org" + }, + "user": { + "id": "dXNlcmlk", + "name": "mario.rossi", + "displayName": "Mario Rossi" + }, + "challenge": "Y3JlZGVudGlhbHMtZm9yLWxpbnV4L2xpYndlYmF1dGhu", + "pubKeyCredParams": [ + { + "type": "public-key", + "alg": -7 + } + ], + "timeout": 30000, + "excludeCredentials": [], + "authenticatorSelection": { + "residentKey": "discouraged", + "userVerification": "preferred" + }, + "attestation": "none", + "attestationFormats": ["packed", "fido-u2f"] + } + "#; + + fn request_base() -> MakeCredentialRequest { + MakeCredentialRequest { + origin: "example.org".to_string(), + hash: ClientData { + operation: Operation::MakeCredential, + challenge: base64_url::decode("Y3JlZGVudGlhbHMtZm9yLWxpbnV4L2xpYndlYmF1dGhu") + .unwrap(), + origin: "example.org".to_string(), + cross_origin: None, + } + .hash(), + relying_party: Ctap2PublicKeyCredentialRpEntity::new("example.org", "example.org"), + user: Ctap2PublicKeyCredentialUserEntity::new(b"userid", "mario.rossi", "Mario Rossi"), + resident_key: Some(ResidentKeyRequirement::Discouraged), + user_verification: UserVerificationRequirement::Preferred, + algorithms: vec![Ctap2CredentialType::default()], + exclude: None, + extensions: None, + timeout: Duration::from_secs(30), + } + } + + fn json_field_add(str: &str, field: &str, value: &str) -> String { + let mut v: serde_json::Value = serde_json::from_str(str).unwrap(); + v.as_object_mut() + .unwrap() + .insert(field.to_owned(), serde_json::from_str(value).unwrap()); + serde_json::to_string(&v).unwrap() + } + + fn json_field_rm(str: &str, field: &str) -> String { + let mut v: serde_json::Value = serde_json::from_str(str).unwrap(); + v.as_object_mut().unwrap().remove(field); + serde_json::to_string(&v).unwrap() + } + + fn test_request_from_json_required_field(field: &str) { + let rpid = RelyingPartyId::try_from("example.org").unwrap(); + let req_json = json_field_rm(REQUEST_BASE_JSON, field); + + let result = MakeCredentialRequest::from_json(&rpid, &req_json); + assert!(matches!( + result, + Err(MakeCredentialRequestParsingError::EncodingError(_)) + )); + } + + #[test] + fn test_request_from_json_base() { + let rpid = RelyingPartyId::try_from("example.org").unwrap(); + let req: MakeCredentialRequest = + MakeCredentialRequest::from_json(&rpid, REQUEST_BASE_JSON).unwrap(); + assert_eq!(req, request_base()); + } + + #[test] + fn test_request_from_json_require_rp() { + test_request_from_json_required_field("rp"); + } + + #[test] + fn test_request_from_json_require_user() { + test_request_from_json_required_field("user"); + } + + #[test] + fn test_request_from_json_require_pub_key_cred_params() { + test_request_from_json_required_field("pubKeyCredParams"); + } + + #[test] + fn test_request_from_json_require_challenge() { + test_request_from_json_required_field("challenge"); + } + + #[test] + #[ignore] // FIXME(#134): Add validation for challenges + fn test_request_from_json_challenge_empty() { + let rpid = RelyingPartyId::try_from("example.org").unwrap(); + let req_json: String = json_field_rm(REQUEST_BASE_JSON, "challenge"); + let req_json = json_field_add(&req_json, "challenge", r#""""#); + + let result = MakeCredentialRequest::from_json(&rpid, &req_json); + assert!(matches!( + result, + Err(MakeCredentialRequestParsingError::EncodingError(_)) + )); + } + + #[test] + fn test_request_from_json_prf_extension() { + let rpid = RelyingPartyId::try_from("example.org").unwrap(); + let req_json = json_field_add( + REQUEST_BASE_JSON, + "extensions", + r#"{"prf": {"eval": {"first": "second"}}}"#, + ); + + let req: MakeCredentialRequest = + MakeCredentialRequest::from_json(&rpid, &req_json).unwrap(); + assert!(matches!( + req.extensions, + Some(MakeCredentialsRequestExtensions { prf: Some(_), .. }) + )); + } + + #[test] + fn test_request_from_json_unknown_pub_key_cred_params() { + let rpid = RelyingPartyId::try_from("example.org").unwrap(); + let req_json = json_field_add( + REQUEST_BASE_JSON, + "pubKeyCredParams", + r#"[{"type": "something", "alg": -12345}]"#, + ); + let req: MakeCredentialRequest = + MakeCredentialRequest::from_json(&rpid, &req_json).unwrap(); + assert_eq!( + req.algorithms, + vec![Ctap2CredentialType { + algorithm: Ctap2COSEAlgorithmIdentifier::Unknown, // FIXME(#148): Passhtrough unknown algorithms + public_key_type: Ctap2PublicKeyCredentialType::Unknown, + }] + ); + } + + #[test] + fn test_request_from_json_default_timeout() { + let rpid = RelyingPartyId::try_from("example.org").unwrap(); + let req_json = json_field_rm(REQUEST_BASE_JSON, "timeout"); + + let req: MakeCredentialRequest = + MakeCredentialRequest::from_json(&rpid, &req_json).unwrap(); + assert_eq!(req.timeout, DEFAULT_TIMEOUT); + } +} diff --git a/libwebauthn/src/ops/webauthn.rs b/libwebauthn/src/ops/webauthn/mod.rs similarity index 82% rename from libwebauthn/src/ops/webauthn.rs rename to libwebauthn/src/ops/webauthn/mod.rs index 5566576..382fd9e 100644 --- a/libwebauthn/src/ops/webauthn.rs +++ b/libwebauthn/src/ops/webauthn/mod.rs @@ -1,5 +1,8 @@ +mod client_data; mod get_assertion; +pub mod idl; mod make_credential; +mod timeout; use super::u2f::{RegisterRequest, SignRequest}; use crate::webauthn::CtapError; @@ -8,21 +11,32 @@ pub use get_assertion::{ GetAssertionLargeBlobExtension, GetAssertionLargeBlobExtensionOutput, GetAssertionPrfOutput, GetAssertionRequest, GetAssertionRequestExtensions, GetAssertionResponse, GetAssertionResponseExtensions, GetAssertionResponseUnsignedExtensions, HMACGetSecretInput, - HMACGetSecretOutput, PRFValue, + HMACGetSecretOutput, PRFValue, PrfInput, }; +pub use idl::{rpid::RelyingPartyId, Base64UrlString, WebAuthnIDL}; pub use make_credential::{ CredentialPropsExtension, CredentialProtectionExtension, CredentialProtectionPolicy, - MakeCredentialHmacOrPrfInput, MakeCredentialLargeBlobExtension, - MakeCredentialLargeBlobExtensionOutput, MakeCredentialPrfOutput, MakeCredentialRequest, - MakeCredentialResponse, MakeCredentialsRequestExtensions, MakeCredentialsResponseExtensions, + MakeCredentialLargeBlobExtension, MakeCredentialLargeBlobExtensionOutput, + MakeCredentialPrfInput, MakeCredentialPrfOutput, MakeCredentialRequest, MakeCredentialResponse, + MakeCredentialsRequestExtensions, MakeCredentialsResponseExtensions, MakeCredentialsResponseUnsignedExtensions, ResidentKeyRequirement, }; +use serde::Deserialize; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Deserialize, PartialEq)] +pub enum Operation { + MakeCredential, + GetAssertion, +} + +#[derive(Debug, Clone, Copy, Deserialize, PartialEq)] pub enum UserVerificationRequirement { + #[serde(rename = "required")] Required, - Preferred, + #[serde(rename = "discouraged")] Discouraged, + #[serde(rename = "preferred", other)] + Preferred, } impl UserVerificationRequirement { diff --git a/libwebauthn/src/ops/webauthn/timeout.rs b/libwebauthn/src/ops/webauthn/timeout.rs new file mode 100644 index 0000000..05ccdb0 --- /dev/null +++ b/libwebauthn/src/ops/webauthn/timeout.rs @@ -0,0 +1,3 @@ +use std::time::Duration; + +pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60); diff --git a/libwebauthn/src/proto/ctap2/model.rs b/libwebauthn/src/proto/ctap2/model.rs index ebf219b..84373b4 100644 --- a/libwebauthn/src/proto/ctap2/model.rs +++ b/libwebauthn/src/proto/ctap2/model.rs @@ -1,5 +1,5 @@ -use crate::pin::PinUvAuthProtocol; use crate::proto::ctap1::Ctap1Transport; +use crate::{ops::webauthn::idl::create::PublicKeyCredentialUserEntity, pin::PinUvAuthProtocol}; use num_enum::{IntoPrimitive, TryFromPrimitive}; use serde_bytes::ByteBuf; @@ -116,6 +116,16 @@ impl Ctap2PublicKeyCredentialUserEntity { } } +impl From for Ctap2PublicKeyCredentialUserEntity { + fn from(user: PublicKeyCredentialUserEntity) -> Self { + Self { + id: ByteBuf::from(user.id), + name: Some(user.name), + display_name: Some(user.display_name), + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] pub enum Ctap2PublicKeyCredentialType { #[serde(rename = "public-key")] @@ -146,7 +156,7 @@ impl From<&Ctap1Transport> for Ctap2Transport { } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct Ctap2PublicKeyCredentialDescriptor { pub id: ByteBuf, pub r#type: Ctap2PublicKeyCredentialType, diff --git a/libwebauthn/src/proto/ctap2/model/get_assertion.rs b/libwebauthn/src/proto/ctap2/model/get_assertion.rs index 775d520..60996af 100644 --- a/libwebauthn/src/proto/ctap2/model/get_assertion.rs +++ b/libwebauthn/src/proto/ctap2/model/get_assertion.rs @@ -152,18 +152,19 @@ impl Ctap2GetAssertionRequest { ) -> Result { // Cloning it, so we can modify it let mut req = req.clone(); - if let Some(ext) = req.extensions.as_mut() { - // LargeBlob (NOTE: Not to be confused with LargeBlobKey) - // https://w3c.github.io/webauthn/#sctn-large-blob-extension - // If read is present and has the value true: - // [..] - // 3. If successful, set blob to the result. - // - // So we silently drop the extension if the device does not support it. - if !info.option_enabled("largeBlobs") { - ext.large_blob = GetAssertionLargeBlobExtension::None; + // LargeBlob (NOTE: Not to be confused with LargeBlobKey) + // https://w3c.github.io/webauthn/#sctn-large-blob-extension + // If read is present and has the value true: + // [..] + // 3. If successful, set blob to the result. + // + // So we silently drop the extension if the device does not support it. + if !info.option_enabled("largeBlobs") { + if let Some(ref mut ext) = req.extensions { + ext.large_blob = None; } } + Ok(Ctap2GetAssertionRequest::from(req)) } } @@ -174,7 +175,7 @@ impl From for Ctap2GetAssertionRequest { relying_party_id: op.relying_party_id, client_data_hash: ByteBuf::from(op.hash), allow: op.allow, - extensions: op.extensions.map(|x| x.into()), + extensions: op.extensions.map(|ext| ext.into()), options: Some(Ctap2GetAssertionOptions { require_user_presence: true, require_user_verification: op.user_verification.is_required(), @@ -185,11 +186,11 @@ impl From for Ctap2GetAssertionRequest { } } -#[derive(Debug, Default, Clone, Serialize)] +#[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct Ctap2GetAssertionRequestExtensions { - #[serde(skip_serializing_if = "Option::is_none")] - pub cred_blob: Option, + #[serde(skip_serializing_if = "std::ops::Not::not")] + pub cred_blob: bool, // Thanks, FIDO-spec for this consistent naming scheme... #[serde(rename = "hmac-secret", skip_serializing_if = "Option::is_none")] pub hmac_secret: Option, @@ -197,16 +198,16 @@ pub struct Ctap2GetAssertionRequestExtensions { #[serde(skip_serializing_if = "Option::is_none")] pub large_blob_key: Option, #[serde(skip)] - pub hmac_or_prf: GetAssertionHmacOrPrfInput, + pub hmac_or_prf: Option, } impl From for Ctap2GetAssertionRequestExtensions { fn from(other: GetAssertionRequestExtensions) -> Self { Ctap2GetAssertionRequestExtensions { cred_blob: other.cred_blob, - hmac_secret: None, // Get's calculated later + hmac_secret: None, // Gets calculated later hmac_or_prf: other.hmac_or_prf, - large_blob_key: if other.large_blob == GetAssertionLargeBlobExtension::Read { + large_blob_key: if other.large_blob == Some(GetAssertionLargeBlobExtension::Read) { Some(true) } else { None @@ -217,7 +218,10 @@ impl From for Ctap2GetAssertionRequestExtensions impl Ctap2GetAssertionRequestExtensions { pub fn skip_serializing(&self) -> bool { - self.cred_blob.is_none() && self.hmac_secret.is_none() + !self.cred_blob + && self.hmac_secret.is_none() + && self.large_blob_key.is_none() + && self.hmac_or_prf.is_none() } pub fn calculate_hmac( @@ -226,14 +230,13 @@ impl Ctap2GetAssertionRequestExtensions { auth_data: &AuthTokenData, ) -> Result<(), Error> { let input = match &self.hmac_or_prf { - GetAssertionHmacOrPrfInput::None => None, - GetAssertionHmacOrPrfInput::HmacGetSecret(hmacget_secret_input) => { - Some(hmacget_secret_input.clone()) + None => None, + Some(GetAssertionHmacOrPrfInput::HmacGetSecret(hmac_get_secret_input)) => { + Some(hmac_get_secret_input.clone()) + } + Some(GetAssertionHmacOrPrfInput::Prf(prf_input)) => { + Self::prf_to_hmac_input(&prf_input.eval, &prf_input.eval_by_credential, allow_list)? } - GetAssertionHmacOrPrfInput::Prf { - eval, - eval_by_credential, - } => Self::prf_to_hmac_input(eval, eval_by_credential, allow_list)?, }; let input = match input { @@ -451,7 +454,7 @@ impl Ctap2UserVerifiableRequest for Ctap2GetAssertionRequest { let hmac_requested = self .extensions .as_ref() - .map(|e| !matches!(e.hmac_or_prf, GetAssertionHmacOrPrfInput::None)) + .map(|e| e.hmac_or_prf.is_some()) .unwrap_or_default(); hmac_requested && hmac_supported } @@ -506,44 +509,37 @@ impl Ctap2GetAssertionResponseExtensions { response: &Ctap2GetAssertionResponse, auth_data: Option<&AuthTokenData>, ) -> GetAssertionResponseUnsignedExtensions { - let (hmac_get_secret, prf) = if let Some(orig_ext) = &request.extensions { - // Decrypt the raw HMAC extension - let decrypted_hmac = self.hmac_secret.as_ref().and_then(|x| { - if let Some(auth_data) = auth_data { - let uv_proto = auth_data.protocol_version.create_protocol_object(); - x.decrypt_output(&auth_data.shared_secret, &uv_proto) - } else { - None - } - }); - if let Some(decrypted) = decrypted_hmac { - // Repackaging it into output - match &orig_ext.hmac_or_prf { - GetAssertionHmacOrPrfInput::None => (None, None), - GetAssertionHmacOrPrfInput::HmacGetSecret(..) => (Some(decrypted), None), - GetAssertionHmacOrPrfInput::Prf { .. } => ( - None, - Some(GetAssertionPrfOutput { - results: Some(PRFValue { - first: decrypted.output1, - second: decrypted.output2, - }), - }), - ), - } + let decrypted_hmac = self.hmac_secret.as_ref().and_then(|x| { + if let Some(auth_data) = auth_data { + let uv_proto = auth_data.protocol_version.create_protocol_object(); + x.decrypt_output(&auth_data.shared_secret, &uv_proto) } else { - (None, None) + None + } + }); + + let (hmac_get_secret, prf) = if let Some(decrypted) = decrypted_hmac { + match request.extensions.as_ref().and_then(|ext| ext.hmac_or_prf.as_ref()) { + None => (None, None), + Some(GetAssertionHmacOrPrfInput::HmacGetSecret(..)) => (Some(decrypted), None), + Some(GetAssertionHmacOrPrfInput::Prf(..)) => ( + None, + Some(GetAssertionPrfOutput { + results: Some(PRFValue { + first: decrypted.output1, + second: decrypted.output2, + }), + }), + ), } } else { (None, None) }; // LargeBlobs was requested - let large_blob = request - .extensions - .as_ref() - .filter(|x| x.large_blob != GetAssertionLargeBlobExtension::None) - .map(|_| { + let large_blob = match request.extensions.as_ref().and_then(|ext| ext.large_blob.as_ref()) { + None => None, + Some(GetAssertionLargeBlobExtension::Read) => { Some(GetAssertionLargeBlobExtensionOutput { blob: response .large_blob_key @@ -552,8 +548,8 @@ impl Ctap2GetAssertionResponseExtensions { // Not yet supported // written: None, }) - }) - .unwrap_or_default(); + } + }; GetAssertionResponseUnsignedExtensions { hmac_get_secret, diff --git a/libwebauthn/src/proto/ctap2/model/make_credential.rs b/libwebauthn/src/proto/ctap2/model/make_credential.rs index afc2d02..739dc9f 100644 --- a/libwebauthn/src/proto/ctap2/model/make_credential.rs +++ b/libwebauthn/src/proto/ctap2/model/make_credential.rs @@ -7,8 +7,8 @@ use super::{ use crate::{ fido::AuthenticatorData, ops::webauthn::{ - CredentialProtectionPolicy, MakeCredentialHmacOrPrfInput, MakeCredentialLargeBlobExtension, - MakeCredentialRequest, MakeCredentialResponse, MakeCredentialsRequestExtensions, + CredentialProtectionPolicy, MakeCredentialLargeBlobExtension, MakeCredentialRequest, + MakeCredentialResponse, MakeCredentialsRequestExtensions, MakeCredentialsResponseUnsignedExtensions, ResidentKeyRequirement, }, pin::PinUvAuthProtocol, @@ -225,8 +225,12 @@ impl Ctap2MakeCredentialsRequestExtensions { // LargeBlob (NOTE: Not to be confused with LargeBlobKey. LargeBlob has "Preferred" as well) // https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API/WebAuthn_extensions#largeblob // - let large_blob_key = match requested_extensions.large_blob { - MakeCredentialLargeBlobExtension::Required => { + let large_blob_key = match requested_extensions + .large_blob + .as_ref() + .map(|info| info.support) + { + Some(MakeCredentialLargeBlobExtension::Required) => { // "required": The credential will be created with an authenticator to store blobs. The create() call will fail if this is impossible. if !info.option_enabled("largeBlobs") { warn!("This request will potentially fail. Large blob extension required, but device does not support it."); @@ -235,7 +239,7 @@ impl Ctap2MakeCredentialsRequestExtensions { // We only add a warning for easier debugging. Some(true) } - MakeCredentialLargeBlobExtension::Preferred => { + Some(MakeCredentialLargeBlobExtension::Preferred) => { if info.option_enabled("largeBlobs") { Some(true) } else { @@ -244,19 +248,23 @@ impl Ctap2MakeCredentialsRequestExtensions { None } } - MakeCredentialLargeBlobExtension::None => None, + _ => None, }; // HMAC Secret - let hmac_secret = match requested_extensions.hmac_or_prf { - MakeCredentialHmacOrPrfInput::None => None, - MakeCredentialHmacOrPrfInput::HmacGetSecret | MakeCredentialHmacOrPrfInput::Prf => { - Some(true) - } + let hmac_secret = if requested_extensions.hmac_create_secret == Some(true) + || requested_extensions.prf.is_some() + { + Some(true) + } else { + None }; Ok(Ctap2MakeCredentialsRequestExtensions { - cred_blob: requested_extensions.cred_blob.clone(), + cred_blob: requested_extensions + .cred_blob + .as_ref() + .map(|inner| inner.0.clone()), hmac_secret, cred_protect: requested_extensions .cred_protect diff --git a/libwebauthn/src/tests/basic_ctap2.rs b/libwebauthn/src/tests/basic_ctap2.rs index c134410..c383826 100644 --- a/libwebauthn/src/tests/basic_ctap2.rs +++ b/libwebauthn/src/tests/basic_ctap2.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use crate::ops::webauthn::GetAssertionRequest; +use crate::ops::webauthn::{GetAssertionRequest, GetAssertionRequestExtensions}; use crate::proto::ctap2::Ctap2PublicKeyCredentialDescriptor; use crate::transport::hid::get_virtual_device; use crate::transport::{Channel, Device}; @@ -66,7 +66,7 @@ async fn test_webauthn_basic_ctap2() { hash: Vec::from(challenge), allow: vec![credential], user_verification: UserVerificationRequirement::Discouraged, - extensions: None, + extensions: Some(GetAssertionRequestExtensions::default()), timeout: TIMEOUT, }; diff --git a/libwebauthn/src/tests/prf.rs b/libwebauthn/src/tests/prf.rs index 97f135a..95666b4 100644 --- a/libwebauthn/src/tests/prf.rs +++ b/libwebauthn/src/tests/prf.rs @@ -3,8 +3,8 @@ use std::time::Duration; use crate::ops::webauthn::{ GetAssertionHmacOrPrfInput, GetAssertionRequest, GetAssertionRequestExtensions, - MakeCredentialHmacOrPrfInput, MakeCredentialPrfOutput, MakeCredentialsRequestExtensions, - PRFValue, + MakeCredentialPrfInput, MakeCredentialPrfOutput, MakeCredentialsRequestExtensions, PRFValue, + PrfInput, }; use crate::pin::PinManagement; use crate::proto::ctap2::{Ctap2PinUvAuthProtocol, Ctap2PublicKeyCredentialDescriptor}; @@ -101,7 +101,7 @@ async fn run_test_battery(channel: &mut HidChannel<'_>, using_pin: bool) { let challenge: [u8; 32] = thread_rng().gen(); let extensions = MakeCredentialsRequestExtensions { - hmac_or_prf: MakeCredentialHmacOrPrfInput::Prf, + prf: Some(MakeCredentialPrfInput { _eval: None }), ..Default::default() }; @@ -192,10 +192,10 @@ async fn run_test_battery(channel: &mut HidChannel<'_>, using_pin: bool) { second: None, }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_success_test( channel, &credential, @@ -221,10 +221,10 @@ async fn run_test_battery(channel: &mut HidChannel<'_>, using_pin: bool) { second: None, }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_success_test( channel, &credential, @@ -243,10 +243,10 @@ async fn run_test_battery(channel: &mut HidChannel<'_>, using_pin: bool) { }); let eval_by_credential = HashMap::new(); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_success_test( channel, &credential, @@ -293,10 +293,10 @@ async fn run_test_battery(channel: &mut HidChannel<'_>, using_pin: bool) { second: Some([7; 32]), }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_success_test( channel, &credential, @@ -336,10 +336,10 @@ async fn run_test_battery(channel: &mut HidChannel<'_>, using_pin: bool) { second: Some([8; 32]), }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_success_test( channel, &credential, @@ -376,10 +376,10 @@ async fn run_test_battery(channel: &mut HidChannel<'_>, using_pin: bool) { second: Some([8; 32]), }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_success_test( channel, &credential, @@ -405,10 +405,10 @@ async fn run_test_battery(channel: &mut HidChannel<'_>, using_pin: bool) { second: None, }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_failed_test( channel, Some(&credential), @@ -429,10 +429,10 @@ async fn run_test_battery(channel: &mut HidChannel<'_>, using_pin: bool) { second: None, }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_failed_test( channel, Some(&credential), @@ -453,10 +453,10 @@ async fn run_test_battery(channel: &mut HidChannel<'_>, using_pin: bool) { second: None, }, ); - let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf { + let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput { eval, eval_by_credential, - }; + }); run_failed_test( channel, None, @@ -487,7 +487,7 @@ async fn run_success_test( allow: vec![credential.clone()], user_verification: UserVerificationRequirement::Preferred, extensions: Some(GetAssertionRequestExtensions { - hmac_or_prf, + hmac_or_prf: Some(hmac_or_prf), ..Default::default() }), timeout: TIMEOUT, @@ -552,7 +552,7 @@ async fn run_failed_test( allow: credential.map(|x| vec![x.clone()]).unwrap_or_default(), user_verification: UserVerificationRequirement::Discouraged, extensions: Some(GetAssertionRequestExtensions { - hmac_or_prf, + hmac_or_prf: Some(hmac_or_prf), ..Default::default() }), timeout: TIMEOUT, diff --git a/libwebauthn/src/webauthn/pin_uv_auth_token.rs b/libwebauthn/src/webauthn/pin_uv_auth_token.rs index 86bd16d..0072bf7 100644 --- a/libwebauthn/src/webauthn/pin_uv_auth_token.rs +++ b/libwebauthn/src/webauthn/pin_uv_auth_token.rs @@ -610,10 +610,12 @@ mod test { info_extensions.as_deref(), UserVerificationRequirement::Discouraged, Some(GetAssertionRequestExtensions { - hmac_or_prf: GetAssertionHmacOrPrfInput::HmacGetSecret(HMACGetSecretInput { - salt1: [0; 32], - salt2: None, - }), + hmac_or_prf: Some(GetAssertionHmacOrPrfInput::HmacGetSecret( + HMACGetSecretInput { + salt1: [0; 32], + salt2: None, + }, + )), ..Default::default() }), Ok(UsedPinUvAuthToken::None), @@ -653,10 +655,12 @@ mod test { Some(&["hmac-secret"]), UserVerificationRequirement::Preferred, Some(GetAssertionRequestExtensions { - hmac_or_prf: GetAssertionHmacOrPrfInput::HmacGetSecret(HMACGetSecretInput { - salt1: [0; 32], - salt2: None, - }), + hmac_or_prf: Some(GetAssertionHmacOrPrfInput::HmacGetSecret( + HMACGetSecretInput { + salt1: [0; 32], + salt2: None, + }, + )), ..Default::default() }), Ok(UsedPinUvAuthToken::LegacyUV), @@ -722,10 +726,12 @@ mod test { for (info_options, uv_requirement) in testcases { let extensions = Some(GetAssertionRequestExtensions { - hmac_or_prf: GetAssertionHmacOrPrfInput::HmacGetSecret(HMACGetSecretInput { - salt1: [0; 32], - salt2: None, - }), + hmac_or_prf: Some(GetAssertionHmacOrPrfInput::HmacGetSecret( + HMACGetSecretInput { + salt1: [0; 32], + salt2: None, + }, + )), ..Default::default() }); @@ -789,10 +795,12 @@ mod test { for (info_options, uv_requirement) in testcases { let extensions = Some(GetAssertionRequestExtensions { - hmac_or_prf: GetAssertionHmacOrPrfInput::HmacGetSecret(HMACGetSecretInput { - salt1: [0; 32], - salt2: None, - }), + hmac_or_prf: Some(GetAssertionHmacOrPrfInput::HmacGetSecret( + HMACGetSecretInput { + salt1: [0; 32], + salt2: None, + }, + )), ..Default::default() }); @@ -903,10 +911,12 @@ mod test { for (info_options, uv_requirement) in testcases { let extensions = Some(GetAssertionRequestExtensions { - hmac_or_prf: GetAssertionHmacOrPrfInput::HmacGetSecret(HMACGetSecretInput { - salt1: [0; 32], - salt2: None, - }), + hmac_or_prf: Some(GetAssertionHmacOrPrfInput::HmacGetSecret( + HMACGetSecretInput { + salt1: [0; 32], + salt2: None, + }, + )), ..Default::default() });