diff --git a/credentialsd-common/Cargo.lock b/credentialsd-common/Cargo.lock index 89ce0a8..b4caef6 100644 --- a/credentialsd-common/Cargo.lock +++ b/credentialsd-common/Cargo.lock @@ -67,35 +67,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" -[[package]] -name = "apdu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aa1a20ca6e9b354419bd6c2714beb435203b3e942440e09016e6deeffb08ffd" -dependencies = [ - "apdu-core", - "apdu-derive", - "thiserror 1.0.69", -] - -[[package]] -name = "apdu-core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5ab921a56bbe68325ba6d3711ee2c681239fe4c9c295c6a1c2fe6992e27f86" - -[[package]] -name = "apdu-derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd675f7ce10250005ac39b9ee8e618fe51370ce6f39170559726cdd0ff7fe7c" -dependencies = [ - "apdu-core", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "asn1-rs" version = "0.7.1" @@ -120,7 +91,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", "synstructure", ] @@ -132,7 +103,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -143,7 +114,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -177,7 +148,7 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" dependencies = [ - "bindgen 0.69.5", + "bindgen", "cc", "cmake", "dunce", @@ -226,29 +197,6 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" -[[package]] -name = "bindgen" -version = "0.65.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" -dependencies = [ - "bitflags 1.3.2", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.104", - "which", -] - [[package]] name = "bindgen" version = "0.69.5" @@ -268,7 +216,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.104", + "syn", "which", ] @@ -656,7 +604,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -779,7 +727,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -881,17 +829,6 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" -[[package]] -name = "find-winsdk" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8cbf17b871570c1f8612b763bac3e86290602bcf5dc3c5ce657e0e1e9071d9e" -dependencies = [ - "serde", - "serde_derive", - "winreg", -] - [[package]] name = "fnv" version = "1.0.7" @@ -979,7 +916,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -1357,11 +1294,9 @@ dependencies = [ [[package]] name = "libwebauthn" version = "0.2.2" -source = "git+https://github.com/linux-credentials/libwebauthn.git#604b53b6ec86f4c0239518d43cb33f9302d4fcb3" +source = "git+https://github.com/linux-credentials/libwebauthn.git?rev=d97c80d25bdb974472c40de5e5031db5946ad532#d97c80d25bdb974472c40de5e5031db5946ad532" dependencies = [ "aes", - "apdu", - "apdu-core", "async-trait", "base64-url", "bitflags 2.9.1", @@ -1380,13 +1315,10 @@ dependencies = [ "hmac", "maplit", "mockall", - "nfc1", - "nfc1-sys", "num-derive", "num-traits", "num_enum", "p256", - "pcsc", "rand 0.8.5", "rustls", "serde", @@ -1394,6 +1326,7 @@ dependencies = [ "serde_bytes", "serde_cbor_2", "serde_derive", + "serde_json", "serde_repr", "sha2", "snow", @@ -1492,29 +1425,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.104", -] - -[[package]] -name = "nfc1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d6dc2e4110af159c220d2d004661e380b6c40d93c5b04e839e4944f9d5291d" -dependencies = [ - "nfc1-sys", -] - -[[package]] -name = "nfc1-sys" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6652c6cdf52433ff143439595ffb4b945afafbe5f27cec8d2fc5dfb5832796e8" -dependencies = [ - "bindgen 0.65.1", - "cc", - "find-winsdk", - "pkg-config", - "vcpkg", + "syn", ] [[package]] @@ -1551,7 +1462,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -1591,7 +1502,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -1717,31 +1628,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "pcsc" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd833ecf8967e65934c49d3521a175929839bf6d0e497f3bd0d3a2ca08943da" -dependencies = [ - "bitflags 2.9.1", - "pcsc-sys", -] - -[[package]] -name = "pcsc-sys" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ef017e15d2e5592a9e39a346c1dbaea5120bab7ed7106b210ef58ebd97003" -dependencies = [ - "pkg-config", -] - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -1850,7 +1736,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" dependencies = [ "proc-macro2", - "syn 2.0.104", + "syn", ] [[package]] @@ -2115,6 +2001,12 @@ version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +[[package]] +name = "ryu" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" + [[package]] name = "same-file" version = "1.0.6" @@ -2200,7 +2092,7 @@ checksum = "fca2da10b1f1623f47130256065e05e94fd7a98dbd26a780a4c5de831b21e5c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -2211,7 +2103,7 @@ checksum = "8f68cf7478db8b81abcf71b6d195a34a4891bd3d39868731c4d73194d74ec7a3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -2253,7 +2145,19 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.143" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", ] [[package]] @@ -2264,7 +2168,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -2401,17 +2305,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.104" @@ -2431,7 +2324,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -2472,7 +2365,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -2483,7 +2376,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -2545,7 +2438,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -2635,7 +2528,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -2712,12 +2605,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.5" @@ -2771,7 +2658,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn", "wasm-bindgen-shared", ] @@ -2793,7 +2680,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2904,7 +2791,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -2915,7 +2802,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -3192,16 +3079,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a" -dependencies = [ - "serde", - "winapi", -] - [[package]] name = "wit-bindgen-rt" version = "0.39.0" @@ -3251,7 +3128,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -3282,7 +3159,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn", "zvariant_utils", ] @@ -3296,6 +3173,6 @@ dependencies = [ "quote", "serde", "static_assertions", - "syn 2.0.104", + "syn", "winnow", ] diff --git a/credentialsd-common/Cargo.toml b/credentialsd-common/Cargo.toml index aee5050..77ef804 100644 --- a/credentialsd-common/Cargo.toml +++ b/credentialsd-common/Cargo.toml @@ -8,6 +8,6 @@ license = "LGPL-3.0-only" [dependencies] futures-lite = "2.6.0" # libwebauthn = "0.2" -libwebauthn = { git = "https://github.com/linux-credentials/libwebauthn.git", revision="604b53b", features = ["libnfc","pcsc"] } +libwebauthn = { git = "https://github.com/linux-credentials/libwebauthn.git", rev = "d97c80d25bdb974472c40de5e5031db5946ad532" } serde = { version = "1", features = ["derive"] } zvariant = "5.6.0" diff --git a/credentialsd-ui/Cargo.lock b/credentialsd-ui/Cargo.lock index 2396be5..98ea479 100644 --- a/credentialsd-ui/Cargo.lock +++ b/credentialsd-ui/Cargo.lock @@ -67,35 +67,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" -[[package]] -name = "apdu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aa1a20ca6e9b354419bd6c2714beb435203b3e942440e09016e6deeffb08ffd" -dependencies = [ - "apdu-core", - "apdu-derive", - "thiserror 1.0.69", -] - -[[package]] -name = "apdu-core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5ab921a56bbe68325ba6d3711ee2c681239fe4c9c295c6a1c2fe6992e27f86" - -[[package]] -name = "apdu-derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd675f7ce10250005ac39b9ee8e618fe51370ce6f39170559726cdd0ff7fe7c" -dependencies = [ - "apdu-core", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "asn1-rs" version = "0.7.1" @@ -120,7 +91,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", "synstructure", ] @@ -132,7 +103,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -254,7 +225,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -316,7 +287,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -356,7 +327,7 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" dependencies = [ - "bindgen 0.69.5", + "bindgen", "cc", "cmake", "dunce", @@ -405,29 +376,6 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" -[[package]] -name = "bindgen" -version = "0.65.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" -dependencies = [ - "bitflags 1.3.2", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.104", - "which", -] - [[package]] name = "bindgen" version = "0.69.5" @@ -447,7 +395,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.104", + "syn", "which", ] @@ -930,7 +878,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -1053,7 +1001,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -1135,7 +1083,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -1213,17 +1161,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "find-winsdk" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8cbf17b871570c1f8612b763bac3e86290602bcf5dc3c5ce657e0e1e9071d9e" -dependencies = [ - "serde", - "serde_derive", - "winreg", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1311,7 +1248,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -1532,7 +1469,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -1669,7 +1606,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -2001,11 +1938,9 @@ dependencies = [ [[package]] name = "libwebauthn" version = "0.2.2" -source = "git+https://github.com/linux-credentials/libwebauthn.git#604b53b6ec86f4c0239518d43cb33f9302d4fcb3" +source = "git+https://github.com/linux-credentials/libwebauthn.git?rev=d97c80d25bdb974472c40de5e5031db5946ad532#d97c80d25bdb974472c40de5e5031db5946ad532" dependencies = [ "aes", - "apdu", - "apdu-core", "async-trait", "base64-url", "bitflags 2.9.1", @@ -2024,13 +1959,10 @@ dependencies = [ "hmac", "maplit", "mockall", - "nfc1", - "nfc1-sys", "num-derive", "num-traits", "num_enum", "p256", - "pcsc", "rand 0.8.5", "rustls", "serde", @@ -2038,6 +1970,7 @@ dependencies = [ "serde_bytes", "serde_cbor_2", "serde_derive", + "serde_json", "serde_repr", "sha2", "snow", @@ -2176,29 +2109,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.104", -] - -[[package]] -name = "nfc1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d6dc2e4110af159c220d2d004661e380b6c40d93c5b04e839e4944f9d5291d" -dependencies = [ - "nfc1-sys", -] - -[[package]] -name = "nfc1-sys" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6652c6cdf52433ff143439595ffb4b945afafbe5f27cec8d2fc5dfb5832796e8" -dependencies = [ - "bindgen 0.65.1", - "cc", - "find-winsdk", - "pkg-config", - "vcpkg", + "syn", ] [[package]] @@ -2258,7 +2169,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -2298,7 +2209,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -2493,31 +2404,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "pcsc" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd833ecf8967e65934c49d3521a175929839bf6d0e497f3bd0d3a2ca08943da" -dependencies = [ - "bitflags 2.9.1", - "pcsc-sys", -] - -[[package]] -name = "pcsc-sys" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ef017e15d2e5592a9e39a346c1dbaea5120bab7ed7106b210ef58ebd97003" -dependencies = [ - "pkg-config", -] - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -2651,7 +2537,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" dependencies = [ "proc-macro2", - "syn 2.0.104", + "syn", ] [[package]] @@ -2938,6 +2824,12 @@ version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +[[package]] +name = "ryu" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" + [[package]] name = "same-file" version = "1.0.6" @@ -3023,7 +2915,7 @@ checksum = "fca2da10b1f1623f47130256065e05e94fd7a98dbd26a780a4c5de831b21e5c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -3034,7 +2926,7 @@ checksum = "8f68cf7478db8b81abcf71b6d195a34a4891bd3d39868731c4d73194d74ec7a3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -3076,7 +2968,19 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.143" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", ] [[package]] @@ -3087,7 +2991,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -3242,17 +3146,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.104" @@ -3272,7 +3165,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -3351,7 +3244,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -3362,7 +3255,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -3433,7 +3326,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -3540,7 +3433,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -3666,12 +3559,6 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version-compare" version = "0.2.0" @@ -3731,7 +3618,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn", "wasm-bindgen-shared", ] @@ -3766,7 +3653,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3887,7 +3774,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -3898,7 +3785,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -4175,16 +4062,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a" -dependencies = [ - "serde", - "winapi", -] - [[package]] name = "wit-bindgen-rt" version = "0.39.0" @@ -4259,7 +4136,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn", "zbus_names", "zvariant", "zvariant_utils", @@ -4294,7 +4171,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn", ] [[package]] @@ -4326,7 +4203,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn", "zvariant_utils", ] @@ -4340,6 +4217,6 @@ dependencies = [ "quote", "serde", "static_assertions", - "syn 2.0.104", + "syn", "winnow", ] diff --git a/credentialsd/Cargo.lock b/credentialsd/Cargo.lock index bbe63a3..62d331c 100644 --- a/credentialsd/Cargo.lock +++ b/credentialsd/Cargo.lock @@ -969,7 +969,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1244,7 +1244,7 @@ dependencies = [ "gobject-sys", "libc", "system-deps", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1421,11 +1421,11 @@ 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]] @@ -1604,7 +1604,7 @@ dependencies = [ [[package]] name = "libwebauthn" version = "0.2.2" -source = "git+https://github.com/linux-credentials/libwebauthn.git#604b53b6ec86f4c0239518d43cb33f9302d4fcb3" +source = "git+https://github.com/linux-credentials/libwebauthn.git?rev=d97c80d25bdb974472c40de5e5031db5946ad532#d97c80d25bdb974472c40de5e5031db5946ad532" dependencies = [ "aes", "apdu", @@ -1641,6 +1641,7 @@ dependencies = [ "serde_bytes", "serde_cbor_2", "serde_derive", + "serde_json", "serde_repr", "sha2", "snow", @@ -2394,7 +2395,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2407,7 +2408,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2861,7 +2862,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix 1.1.2", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3387,7 +3388,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] diff --git a/credentialsd/Cargo.toml b/credentialsd/Cargo.toml index 9f2fa89..bade28e 100644 --- a/credentialsd/Cargo.toml +++ b/credentialsd/Cargo.toml @@ -14,7 +14,7 @@ async-trait = "0.1.88" base64 = "0.22.1" credentialsd-common = { path = "../credentialsd-common" } futures-lite = "2.6.0" -libwebauthn = { git = "https://github.com/linux-credentials/libwebauthn.git", revision="604b53b", features = ["libnfc","pcsc"] } +libwebauthn = { git = "https://github.com/linux-credentials/libwebauthn.git", rev = "d97c80d25bdb974472c40de5e5031db5946ad532", features = ["libnfc", "pcsc"] } # libwebauthn = "~0.2.2" openssl = "0.10.72" rand = "0.9.2" diff --git a/credentialsd/src/credential_service/mod.rs b/credentialsd/src/credential_service/mod.rs index dd80bd0..44b60f5 100644 --- a/credentialsd/src/credential_service/mod.rs +++ b/credentialsd/src/credential_service/mod.rs @@ -375,6 +375,7 @@ impl From for AuthenticatorResponse { mod test { use std::{sync::Arc, time::Duration}; + use base64::Engine as _; use libwebauthn::{ ops::webauthn::{ResidentKeyRequirement, UserVerificationRequirement}, proto::ctap2::{ @@ -387,9 +388,8 @@ mod test { use crate::{ credential_service::usb::InProcessUsbHandler, dbus::test::{DummyFlowServer, DummyUiServer}, - webauthn, }; - use credentialsd_common::model::{CredentialRequest, MakeCredentialRequest, Operation}; + use credentialsd_common::model::{CredentialRequest, MakeCredentialRequest}; use super::{ hybrid::{test::DummyHybridHandler, HybridStateInternal}, @@ -449,16 +449,14 @@ mod test { let challenge = "Ox0AXQz7WUER7BGQFzvVrQbReTkS3sepVGj26qfUhhrWSarkDbGF4T4NuCY1aAwHYzOzKMJJ2YRSatetl0D9bQ"; let origin = "webauthn.io".to_string(); let is_cross_origin = false; - let client_data_json = webauthn::format_client_data_json( - Operation::Create, - challenge, - &origin, - is_cross_origin, - ); - let client_data_hash = webauthn::create_client_data_hash(&client_data_json); + // Decode the challenge from base64url + let challenge_bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD + .decode(challenge) + .expect("valid base64url challenge"); let make_request = MakeCredentialRequest { - hash: client_data_hash, - origin: "webauthn.io".to_string(), + challenge: challenge_bytes, + origin: origin.clone(), + cross_origin: Some(is_cross_origin), relying_party: Ctap2PublicKeyCredentialRpEntity { id: "webauthn.io".to_string(), name: Some("webauthn.io".to_string()), diff --git a/credentialsd/src/credential_service/nfc.rs b/credentialsd/src/credential_service/nfc.rs index b96307a..c2dcf53 100644 --- a/credentialsd/src/credential_service/nfc.rs +++ b/credentialsd/src/credential_service/nfc.rs @@ -36,8 +36,11 @@ impl InProcessNfcHandler { prev_nfc_state: &NfcStateInternal, ) -> Result { match libwebauthn::transport::nfc::get_nfc_device().await { - Ok(None) => Ok(NfcStateInternal::Waiting), - Ok(Some(hid_device)) => Ok(NfcStateInternal::Connected(hid_device)), + Ok(Some(nfc_device)) => Ok(NfcStateInternal::Connected(nfc_device)), + Ok(None) => { + let state = NfcStateInternal::Waiting; + Ok(state) + } Err(err) => { *failures += 1; if *failures == 5 { diff --git a/credentialsd/src/dbus/model.rs b/credentialsd/src/dbus/model.rs index 55a2112..f4160c8 100644 --- a/credentialsd/src/dbus/model.rs +++ b/credentialsd/src/dbus/model.rs @@ -2,33 +2,31 @@ //! //! Types shared between components within this service belong in credentialsd_common::model. -use std::{collections::HashMap, time::Duration}; - -use base64::{self, engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; - use credentialsd_common::{ - model::{ - GetAssertionResponseInternal, MakeCredentialResponseInternal, Operation, WebAuthnError, - }, + model::{GetAssertionResponseInternal, MakeCredentialResponseInternal, WebAuthnError}, server::{ CreateCredentialRequest, CreatePublicKeyCredentialResponse, GetCredentialRequest, GetPublicKeyCredentialResponse, }, }; -use crate::{ - cose::CoseKeyAlgorithmIdentifier, - webauthn::{ - self, CredentialProtectionExtension, Ctap2PublicKeyCredentialDescriptor, - Ctap2PublicKeyCredentialRpEntity, Ctap2PublicKeyCredentialUserEntity, - GetAssertionHmacOrPrfInput, GetAssertionLargeBlobExtension, GetAssertionRequest, - GetAssertionRequestExtensions, GetPublicKeyCredentialUnsignedExtensionsResponse, - MakeCredentialHmacOrPrfInput, MakeCredentialRequest, MakeCredentialsRequestExtensions, - PublicKeyCredentialParameters, ResidentKeyRequirement, UserVerificationRequirement, - }, +use crate::webauthn::{ + self, GetAssertionRequest, GetPublicKeyCredentialUnsignedExtensionsResponse, + MakeCredentialRequest, RelyingPartyId, WebAuthnIDL, }; -// Helper functions for translating D-Bus types into internal types +/// Parses a WebAuthn create credential request from D-Bus into a CTAP2 MakeCredentialRequest. +/// +/// Uses libwebauthn's `WebAuthnIDL::from_json()` for parsing, which handles: +/// - Challenge decoding from base64url +/// - User entity parsing with base64url-encoded user ID +/// - Relying party entity parsing +/// - Extension parsing (credProps, credBlob, largeBlobSupport, prf, etc.) +/// - Authenticator selection criteria (residentKey, userVerification) +/// - Excluded credentials list +/// - Public key credential parameters +/// +/// Returns the parsed request and the client data JSON (needed for response serialization). pub(super) fn create_credential_request_try_into_ctap2( request: &CreateCredentialRequest, ) -> std::result::Result<(MakeCredentialRequest, String), WebAuthnError> { @@ -40,6 +38,19 @@ pub(super) fn create_credential_request_try_into_ctap2( WebAuthnError::TypeError })?; + // Get origin and determine relying party ID + let (origin, _is_cross_origin) = + match (request.origin.as_ref(), request.is_same_origin.as_ref()) { + (Some(origin), Some(is_same_origin)) => (origin.to_string(), !is_same_origin), + (Some(origin), None) => (origin.to_string(), true), + (None, _) => { + tracing::info!("Error reading origin from request."); + return Err(WebAuthnError::TypeError); + } + }; + + // Extract rpId from JSON for RelyingPartyId construction + // libwebauthn validates that the rpId in the request matches this let request_value = serde_json::from_str::(&options.request_json).map_err(|err| { tracing::info!("Invalid request JSON: {err}"); @@ -49,171 +60,48 @@ pub(super) fn create_credential_request_try_into_ctap2( tracing::info!("Invalid request JSON: not an object"); WebAuthnError::TypeError })?; - let challenge = json - .get("challenge") - .and_then(|c| c.as_str()) - .ok_or_else(|| { - tracing::info!("JSON missing `challenge` field."); - WebAuthnError::TypeError - })? - .to_owned(); - let rp = json + + // Get rpId from the request, or derive from origin + let rp_id_str = json .get("rp") - .and_then(|val| { - serde_json::from_str::(&val.to_string()).ok() - }) - .ok_or_else(|| { - tracing::info!("JSON missing `rp` field"); + .and_then(|rp| rp.get("id")) + .and_then(|id| id.as_str()) + .map(|s| s.to_string()) + .unwrap_or_else(|| { + // Default to effective domain from origin + origin + .rsplit_once('/') + .map(|(_, d)| d.to_string()) + .unwrap_or(origin.clone()) + }); + + let rp_id = RelyingPartyId::try_from(rp_id_str.as_str()).map_err(|_| { + tracing::info!("Invalid relying party ID"); + WebAuthnError::TypeError + })?; + + // Use libwebauthn's JSON parsing + let mut make_cred_request = MakeCredentialRequest::from_json(&rp_id, &options.request_json) + .map_err(|err| { + tracing::info!("Failed to parse MakeCredential request JSON: {err}"); WebAuthnError::TypeError })?; - let mut user = - json.get("user") - .ok_or_else(|| { - tracing::info!("JSON missing `user` field."); - WebAuthnError::TypeError - }) - .and_then(|val| { - serde_json::from_str::(&val.to_string()) - .map_err(|e| { - tracing::info!("JSON missing `user` field: {e}"); - WebAuthnError::TypeError - }) - })?; - user.id = URL_SAFE_NO_PAD - .decode(user.id) - .map_err(|_| { - tracing::info!("user ID is not a valid base64url string"); - WebAuthnError::TypeError - })? - .into(); - let other_options = - serde_json::from_str::(&request_value.to_string()) - .map_err(|e| { - tracing::info!("Received invalid request JSON: {e}"); - WebAuthnError::TypeError - })?; - let (resident_key, user_verification) = - if let Some(authenticator_selection) = other_options.authenticator_selection { - let resident_key = match authenticator_selection.resident_key.as_deref() { - Some("required") => Some(ResidentKeyRequirement::Required), - Some("preferred") => Some(ResidentKeyRequirement::Preferred), - Some("discouraged") => Some(ResidentKeyRequirement::Discouraged), - Some(_) => None, - // legacy webauthn-1 member - None if authenticator_selection.require_resident_key == Some(true) => { - Some(ResidentKeyRequirement::Required) - } - None => None, - }; - let user_verification = authenticator_selection - .user_verification - .map(|uv| match uv.as_ref() { - "required" => UserVerificationRequirement::Required, - "preferred" => UserVerificationRequirement::Preferred, - "discouraged" => UserVerificationRequirement::Discouraged, - _ => todo!("This should be fixed in the future"), - }) - .unwrap_or(UserVerificationRequirement::Preferred); + // Set origin and cross_origin from D-Bus request context + make_cred_request.origin = origin; + make_cred_request.cross_origin = request.is_same_origin.as_ref().map(|same| !same); - (resident_key, user_verification) - } else { - (None, UserVerificationRequirement::Preferred) - }; - let extensions = if let Some(incoming_extensions) = other_options.extensions { - let extensions = MakeCredentialsRequestExtensions { - cred_props: incoming_extensions.cred_props, - cred_blob: incoming_extensions - .cred_blob - .and_then(|x| URL_SAFE_NO_PAD.decode(x).ok()), - min_pin_length: incoming_extensions.min_pin_length, - cred_protect: match incoming_extensions.credential_protection_policy { - Some(cred_prot_policy) => Some(CredentialProtectionExtension { - policy: cred_prot_policy, - enforce_policy: incoming_extensions - .enforce_credential_protection_policy - .unwrap_or_default(), - }), - None => None, - }, - large_blob: incoming_extensions - .large_blob - .map(|x| x.support.unwrap_or_default()) - .unwrap_or_default(), - hmac_or_prf: if incoming_extensions.prf.is_some() { - // CTAP currently doesn't support PRF queries at credentials.create() - // So we ignore any potential value set in the request and only mark this - // credential to activate HMAC for future PRF queries using credentials.get() - MakeCredentialHmacOrPrfInput::Prf - } else { - // MakeCredentialHmacOrPrfInput::Hmac is not used directly by webauthn - MakeCredentialHmacOrPrfInput::None - }, - }; - Some(extensions) - } else { - None - }; - - let credential_parameters = match request_value.clone().get("pubKeyCredParams") { - // https://www.w3.org/TR/webauthn-3/#sctn-createCredential Section 5.1.3.10 - // Default to ES256 and RS256 if no params are given. - None => Ok(vec![ - PublicKeyCredentialParameters { - alg: CoseKeyAlgorithmIdentifier::ES256.into(), - }, - PublicKeyCredentialParameters { - alg: CoseKeyAlgorithmIdentifier::RS256.into(), - }, - ]), - Some(val) => serde_json::from_str::>(&val.to_string()) - .map_err(|e| { - tracing::info!("Request JSON missing or invalid `pubKeyCredParams` key: {e}."); - WebAuthnError::TypeError - }), - }?; - let algorithms = credential_parameters - .iter() - .filter_map(|p| p.try_into().ok()) - .collect(); - let exclude = other_options.excluded_credentials.map(|v| { - v.iter() - .map(|e| e.try_into()) - .filter_map(|e| e.ok()) - .collect() - }); - let (origin, is_cross_origin) = match (request.origin.as_ref(), request.is_same_origin.as_ref()) - { - (Some(origin), Some(is_same_origin)) => (origin.to_string(), !is_same_origin), - (Some(origin), None) => (origin.to_string(), true), - // origin should always be set on request either by client or D-Bus service, - // so this shouldn't be called - (None, _) => { - tracing::info!("Error reading origin from request."); - return Err(WebAuthnError::TypeError); - } - }; + // Get the client data JSON from the request for response serialization let client_data_json = - webauthn::format_client_data_json(Operation::Create, &challenge, &origin, is_cross_origin); - let client_data_hash = webauthn::create_client_data_hash(&client_data_json); - Ok(( - MakeCredentialRequest { - hash: client_data_hash, - origin, + String::from_utf8(make_cred_request.client_data_json()).map_err(|_| { + tracing::info!("Failed to serialize client data JSON"); + WebAuthnError::TypeError + })?; - relying_party: rp, - user, - resident_key, - user_verification, - algorithms, - exclude, - extensions, - timeout: other_options.timeout.unwrap_or(Duration::from_secs(300)), - }, - client_data_json, - )) + Ok((make_cred_request, client_data_json)) } +/// Serializes a CTAP2 MakeCredentialResponse to WebAuthn JSON format. pub(super) fn create_credential_response_try_from_ctap2( response: &MakeCredentialResponseInternal, client_data_json: String, @@ -239,7 +127,7 @@ pub(super) fn create_credential_response_try_from_ctap2( response.ctap.enterprise_attestation.unwrap_or(false), ) .map_err(|_| "Failed to create attestation object".to_string())?; - // TODO: do we need to check that the client_data_hash is the same? + let registration_response_json = webauthn::CreatePublicKeyCredentialResponse::new( attested_credential.credential_id.clone(), attestation_object, @@ -255,128 +143,88 @@ pub(super) fn create_credential_response_try_from_ctap2( Ok(response) } +/// Parses a WebAuthn get credential request from D-Bus into a CTAP2 GetAssertionRequest. +/// +/// Uses libwebauthn's `WebAuthnIDL::from_json()` for parsing, which handles: +/// - Challenge decoding from base64url +/// - Allowed credentials list with transports +/// - Extension parsing (getCredBlob, largeBlob, prf, hmac-secret) +/// - User verification requirement +/// +/// Returns the parsed request and the client data JSON (needed for response serialization). pub(super) fn get_credential_request_try_into_ctap2( request: &GetCredentialRequest, ) -> std::result::Result<(GetAssertionRequest, String), WebAuthnError> { if request.public_key.is_none() { return Err(WebAuthnError::NotSupportedError); } - let options: webauthn::GetCredentialOptions = request - .public_key - .as_ref() - .ok_or_else(|| { - tracing::info!("Invalid request: no \"publicKey\" options specified."); + let options = request.public_key.as_ref().ok_or_else(|| { + tracing::info!("Invalid request: no \"publicKey\" options specified."); + WebAuthnError::TypeError + })?; + + // Get origin + let (origin, _is_cross_origin) = + match (request.origin.as_ref(), request.is_same_origin.as_ref()) { + (Some(origin), Some(is_same_origin)) => (origin.to_string(), !is_same_origin), + (Some(origin), None) => (origin.to_string(), true), + (None, _) => { + tracing::info!("Error reading origin from client request."); + return Err(WebAuthnError::TypeError); + } + }; + + // Extract rpId from JSON for RelyingPartyId construction + let request_value = + serde_json::from_str::(&options.request_json).map_err(|err| { + tracing::info!("Invalid request JSON: {err}"); WebAuthnError::TypeError - }) - .and_then(|o| { - serde_json::from_str(&o.request_json).map_err(|e| { - tracing::info!("Received invalid request JSON: {:?}", e); - WebAuthnError::TypeError - }) })?; - let mut allow: Vec = options - .allow_credentials - .iter() - .filter_map(|cred| { - if cred.cred_type == "public-key" { - cred.try_into().ok() - } else { - None - } - }) - .collect(); - // TODO: The allow is returning an empty list instead of either None or a list of transports. - // This should be investigated, but this is just a UI hint and isn't necessary to pass to the authenticator. - // Just removing it for now. - for c in allow.iter_mut() { - c.transports = None; - } - let (origin, is_cross_origin) = match (request.origin.as_ref(), request.is_same_origin.as_ref()) - { - (Some(origin), Some(is_same_origin)) => (origin.to_string(), !is_same_origin), - (Some(origin), None) => (origin.to_string(), true), - // origin should always be set on request either by client or D-Bus service, - // so this shouldn't be called - (None, _) => { - tracing::info!("Error reading origin from client request."); - return Err(WebAuthnError::TypeError); - } - }; + let json = request_value.as_object().ok_or_else(|| { + tracing::info!("Invalid request JSON: not an object"); + WebAuthnError::TypeError + })?; - let client_data_json = webauthn::format_client_data_json( - Operation::Get, - &options.challenge, - &origin, - is_cross_origin, - ); - let client_data_hash = webauthn::create_client_data_hash(&client_data_json); - // TODO: actually calculate correct effective domain, and use fallback to related origin requests to fill this in. For now, just default to origin. - let user_verification = match options - .user_verification - .unwrap_or_else(|| String::from("preferred")) - .as_ref() - { - "required" => UserVerificationRequirement::Required, - "preferred" => UserVerificationRequirement::Preferred, - "discouraged" => UserVerificationRequirement::Discouraged, - _ => { - tracing::info!("Invalid user verification requirement specified by client."); - return Err(WebAuthnError::TypeError); - } - }; - let relying_party_id = options.rp_id.unwrap_or_else(|| { - // TODO: We're assuming that the origin is `://data`, which is - // currently checked by the caller, but we should encode this in a type. - let (_, effective_domain) = origin.rsplit_once('/').unwrap(); - effective_domain.to_string() - }); + // Get rpId from the request, or derive from origin + let rp_id_str = json + .get("rpId") + .and_then(|id| id.as_str()) + .map(|s| s.to_string()) + .unwrap_or_else(|| { + // Default to effective domain from origin + origin + .rsplit_once('/') + .map(|(_, d)| d.to_string()) + .unwrap_or(origin.clone()) + }); - let extensions = if let Some(incoming_extensions) = options.extensions { - let extensions = GetAssertionRequestExtensions { - cred_blob: incoming_extensions.get_cred_blob, - hmac_or_prf: incoming_extensions - .prf - .and_then(|x| { - x.eval.map(|eval| { - let eval = Some(eval.decode()); - let mut eval_by_credential = HashMap::new(); - if let Some(incoming_eval) = x.eval_by_credential { - for (key, val) in incoming_eval.iter() { - eval_by_credential.insert(key.clone(), val.decode()); - } - } - GetAssertionHmacOrPrfInput::Prf { - eval, - eval_by_credential, - } - }) - }) - .unwrap_or_default(), - large_blob: incoming_extensions - .large_blob - // TODO: Implement GetAssertionLargeBlobExtension::Write, once libwebauthn supports it - .filter(|x| x.read == Some(true)) - .map(|_| GetAssertionLargeBlobExtension::Read) - .unwrap_or(GetAssertionLargeBlobExtension::None), - }; - Some(extensions) - } else { - None - }; + let rp_id = RelyingPartyId::try_from(rp_id_str.as_str()).map_err(|_| { + tracing::info!("Invalid relying party ID"); + WebAuthnError::TypeError + })?; - Ok(( - GetAssertionRequest { - hash: client_data_hash, - relying_party_id, - user_verification, - allow, - extensions, - timeout: options.timeout.unwrap_or(Duration::from_secs(300)), - }, - client_data_json, - )) + // Use libwebauthn's JSON parsing + let mut get_assertion_request = GetAssertionRequest::from_json(&rp_id, &options.request_json) + .map_err(|err| { + tracing::info!("Failed to parse GetAssertion request JSON: {err}"); + WebAuthnError::TypeError + })?; + + // Set origin and cross_origin from D-Bus request context + get_assertion_request.origin = origin; + get_assertion_request.cross_origin = request.is_same_origin.as_ref().map(|same| !same); + + // Get the client data JSON from the request for response serialization + let client_data_json = + String::from_utf8(get_assertion_request.client_data_json()).map_err(|_| { + tracing::info!("Failed to serialize client data JSON"); + WebAuthnError::TypeError + })?; + + Ok((get_assertion_request, client_data_json)) } +/// Serializes a CTAP2 GetAssertionResponse to WebAuthn JSON format. pub(super) fn get_credential_response_try_from_ctap2( response: &GetAssertionResponseInternal, client_data_json: String, @@ -387,12 +235,6 @@ pub(super) fn get_credential_response_try_from_ctap2( .to_response_bytes() .map_err(|err| format!("Failed to parse authenticator data: {err}"))?; - // We can't just do this here, because we need encode all byte arrays for the JS-communication: - // let unsigned_extensions = response - // .ctap - // .unsigned_extensions_output - // .as_ref() - // .map(|extensions| serde_json::to_string(&extensions).unwrap()); let unsigned_extensions = response .ctap .unsigned_extensions_output diff --git a/credentialsd/src/webauthn.rs b/credentialsd/src/webauthn.rs index 5424839..86c0393 100644 --- a/credentialsd/src/webauthn.rs +++ b/credentialsd/src/webauthn.rs @@ -1,31 +1,14 @@ -use std::{collections::HashMap, time::Duration}; - -use base64::{self, engine::general_purpose::URL_SAFE_NO_PAD, Engine}; -use libwebauthn::{ - ops::webauthn::{CredentialProtectionPolicy, MakeCredentialLargeBlobExtension}, - proto::ctap2::{ - Ctap2AttestationStatement, Ctap2CredentialType, Ctap2PublicKeyCredentialType, - Ctap2Transport, - }, -}; -use ring::digest; -use serde::{Deserialize, Serialize}; +use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; +use libwebauthn::proto::ctap2::Ctap2AttestationStatement; +use serde::Serialize; use serde_json::json; use tracing::debug; -use credentialsd_common::model::Operation; - -use crate::cose::{CoseKeyAlgorithmIdentifier, CoseKeyType}; +use crate::cose::CoseKeyAlgorithmIdentifier; +// Re-exports from libwebauthn pub use libwebauthn::ops::webauthn::{ - CredentialProtectionExtension, GetAssertionHmacOrPrfInput, GetAssertionLargeBlobExtension, - GetAssertionRequest, GetAssertionRequestExtensions, MakeCredentialHmacOrPrfInput, - MakeCredentialRequest, MakeCredentialsRequestExtensions, ResidentKeyRequirement, - UserVerificationRequirement, -}; -pub use libwebauthn::proto::ctap2::{ - Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialRpEntity, - Ctap2PublicKeyCredentialUserEntity, + GetAssertionRequest, MakeCredentialRequest, RelyingPartyId, WebAuthnIDL, }; #[derive(Debug)] @@ -95,255 +78,6 @@ pub(crate) fn create_attestation_object( Ok(attestation_object) } -#[derive(Debug, Deserialize)] -pub(crate) struct MakeCredentialOptions { - /// Timeout in milliseconds - #[serde(deserialize_with = "crate::serde::duration::from_opt_ms")] - #[serde(default)] - pub timeout: Option, - #[serde(rename = "excludeCredentials")] - pub excluded_credentials: Option>, - #[serde(rename = "authenticatorSelection")] - pub authenticator_selection: Option, - /// https://www.w3.org/TR/webauthn-3/#enum-attestation-convey - #[allow(dead_code)] - pub attestation: Option, - /// extensions input as a JSON object - pub extensions: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub(crate) struct MakeCredentialExtensions { - #[serde(skip_serializing_if = "Option::is_none")] - pub cred_blob: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub cred_props: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub min_pin_length: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub credential_protection_policy: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub enforce_credential_protection_policy: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub large_blob: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub prf: Option, -} - -#[derive(Debug, Default, Deserialize)] -pub(crate) struct LargeBlobExtension { - #[serde(skip_serializing_if = "Option::is_none")] - pub support: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub read: Option, - #[allow(dead_code)] // TODO: Not currently used, but we should eventually implement - #[serde(skip_serializing_if = "Option::is_none")] - pub write: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub(crate) struct Prf { - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) eval: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) eval_by_credential: Option>, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct PRFValue { - // base64 encoded data - pub first: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub second: Option, -} - -impl PRFValue { - pub(crate) fn decode(&self) -> libwebauthn::ops::webauthn::PRFValue { - let mut res = libwebauthn::ops::webauthn::PRFValue::default(); - let first = URL_SAFE_NO_PAD.decode(&self.first).unwrap(); - let len_to_copy = std::cmp::min(first.len(), 32); // Determine how many bytes to copy - res.first[..len_to_copy].copy_from_slice(&first[..len_to_copy]); - if let Some(second) = self - .second - .as_ref() - .map(|second| URL_SAFE_NO_PAD.decode(second).unwrap()) - { - let len_to_copy = std::cmp::min(second.len(), 32); // Determine how many bytes to copy - let mut res_second = [0u8; 32]; - res_second[..len_to_copy].copy_from_slice(&second[..len_to_copy]); - res.second = Some(res_second); - } - res - } -} - -#[derive(Debug, Deserialize)] -pub(crate) struct GetCredentialOptions { - /// Challenge bytes in base64url-encoding with no padding. - pub(crate) challenge: String, - - #[serde(deserialize_with = "crate::serde::duration::from_opt_ms")] - #[serde(default)] - pub(crate) timeout: Option, - - /// Relying Party ID. - /// If not set, the request origin's effective domain will be used instead. - #[serde(rename = "rpId")] - pub(crate) rp_id: Option, - - /// An list of allowed credentials, in descending order of RP preference. - /// If empty, then any credential that can fulfill the request is allowed. - #[serde(rename = "allowCredentials")] - #[serde(default)] - pub(crate) allow_credentials: Vec, - - /// Defaults to `preferred` - #[serde(rename = "userVerification")] - pub(crate) user_verification: Option, - - /// Contextual information from the RP to help the client guide the user - /// through the authentication ceremony. - #[allow(dead_code)] // TODO: Not currently used, but we should eventually implement support for hints. - #[serde(default)] - pub(crate) hints: Vec, - - pub(crate) extensions: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub(crate) struct GetCredentialExtensions { - // TODO: appid - #[serde(skip_serializing_if = "Option::is_none")] - pub get_cred_blob: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub large_blob: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub prf: Option, -} - -#[derive(Debug, Deserialize)] -/// https://www.w3.org/TR/webauthn-3/#dictionary-credential-descriptor -pub(crate) struct CredentialDescriptor { - /// Type of the public key credential the caller is referring to. - /// - /// The value SHOULD be a member of PublicKeyCredentialType but client - /// platforms MUST ignore any PublicKeyCredentialDescriptor with an unknown - /// type. - #[serde(rename = "type")] - pub(crate) cred_type: String, - /// Credential ID of the public key credential the caller is referring to. - #[serde(with = "crate::serde::b64")] - pub(crate) id: Vec, - pub(crate) transports: Option>, -} - -impl TryFrom<&CredentialDescriptor> for Ctap2PublicKeyCredentialDescriptor { - type Error = Error; - fn try_from(value: &CredentialDescriptor) -> Result { - let transports = value.transports.as_ref().filter(|t| !t.is_empty()); - let transports = match transports { - Some(transports) => { - let mut transport_list = transports.iter().map(|t| match t.as_ref() { - "ble" => Some(Ctap2Transport::Ble), - "nfc" => Some(Ctap2Transport::Nfc), - "usb" => Some(Ctap2Transport::Usb), - "internal" => Some(Ctap2Transport::Internal), - _ => None, - }); - if transport_list.any(|t| t.is_none()) { - return Err(Error::Internal( - "Invalid transport type specified".to_owned(), - )); - } - transport_list.collect() - } - None => None, - }; - Ok(Self { - r#type: Ctap2PublicKeyCredentialType::PublicKey, - id: value.id.clone().into(), - transports, - }) - } -} -impl TryFrom for Ctap2PublicKeyCredentialDescriptor { - type Error = Error; - fn try_from(value: CredentialDescriptor) -> Result { - Ctap2PublicKeyCredentialDescriptor::try_from(&value) - } -} - -#[derive(Debug, Deserialize)] -/// https://www.w3.org/TR/webauthn-3/#dictionary-authenticatorSelection -pub(crate) struct AuthenticatorSelectionCriteria { - // /// https://www.w3.org/TR/webauthn-3/#enum-attachment - // #[zvariant(rename = "authenticatorAttachment")] - // pub authenticator_attachment: Option, - // - /// https://www.w3.org/TR/webauthn-3/#enum-residentKeyRequirement - #[serde(rename = "residentKey")] - pub resident_key: Option, - - // Implied by resident_key == "required", deprecated in webauthn - // https://www.w3.org/TR/webauthn-3/#enum-residentKeyRequirement - #[serde(rename = "requireResidentKey")] - pub require_resident_key: Option, - - /// https://www.w3.org/TR/webauthn-3/#enumdef-userverificationrequirement - #[serde(rename = "userVerification")] - pub user_verification: Option, -} - -#[derive(Clone, Deserialize)] -/// https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialparameters -pub(crate) struct PublicKeyCredentialParameters { - pub alg: i64, -} - -impl TryFrom<&PublicKeyCredentialParameters> for Ctap2CredentialType { - type Error = Error; - - fn try_from(value: &PublicKeyCredentialParameters) -> Result { - let algorithm = match value.alg { - -7 => libwebauthn::proto::ctap2::Ctap2COSEAlgorithmIdentifier::ES256, - -8 => libwebauthn::proto::ctap2::Ctap2COSEAlgorithmIdentifier::EDDSA, - // TODO: we should still pass on the raw value to the authenticator and let it decide whether it's supported. - _ => { - return Err(Error::Internal( - "Invalid algorithm passed for new credential".to_owned(), - )) - } - }; - Ok(Self { - public_key_type: Ctap2PublicKeyCredentialType::PublicKey, - algorithm, - }) - } -} - -impl TryFrom<&PublicKeyCredentialParameters> for CoseKeyType { - type Error = String; - fn try_from(value: &PublicKeyCredentialParameters) -> Result { - match value.alg { - -7 => Ok(CoseKeyType::Es256P256), - -8 => Ok(CoseKeyType::EddsaEd25519), - -257 => Ok(CoseKeyType::RS256), - _ => Err("Invalid or unsupported algorithm specified".to_owned()), - } - } -} - -impl TryFrom for CoseKeyType { - type Error = String; - fn try_from(value: PublicKeyCredentialParameters) -> Result { - CoseKeyType::try_from(&value) - } -} - #[derive(Debug, PartialEq)] pub(crate) enum AttestationStatement { None, @@ -381,7 +115,11 @@ impl TryFrom<&Ctap2AttestationStatement> for AttestationStatement { } Ctap2AttestationStatement::FidoU2F(att_stmt) => Ok(Self::U2F { signature: att_stmt.signature.as_ref().to_vec(), - certificate: att_stmt.certificate.to_vec(), + certificate: att_stmt + .certificates + .first() + .map(|c| c.as_ref().to_vec()) + .unwrap_or_default(), }), _ => { debug!("Unsupported attestation type: {:?}", value); @@ -671,23 +409,3 @@ impl GetPublicKeyCredentialResponse { output.to_string() } } - -pub fn create_client_data_hash(json: &str) -> Vec { - digest::digest(&digest::SHA256, json.as_bytes()) - .as_ref() - .to_owned() -} - -pub fn format_client_data_json( - op: Operation, - challenge: &str, - origin: &str, - is_cross_origin: bool, -) -> String { - let op_str = match op { - Operation::Create => "webauthn.create", - Operation::Get => "webauthn.get", - }; - let cross_origin_str = if is_cross_origin { "true" } else { "false" }; - format!("{{\"type\":\"{op_str}\",\"challenge\":\"{challenge}\",\"origin\":\"{origin}\",\"crossOrigin\":{cross_origin_str}}}") -}