diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b760e269..645244fe 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,7 +37,7 @@ jobs: run: meson test --interactive working-directory: build/ - name: Check clippy recommendations - run: env CARGO_HOME=build/cargo-home cargo clippy --manifest-path xyz-iinuwa-credential-manager-portal-gtk/Cargo.toml --target-dir build/xyz-iinuwa-credential-manager-portal-gtk/src + run: env CARGO_HOME=build/cargo-home cargo clippy --manifest-path credsd/Cargo.toml --target-dir build/credsd/target/release - name: Check formatting run: cargo fmt --check - working-directory: xyz-iinuwa-credential-manager-portal-gtk + working-directory: credsd diff --git a/.vscode/launch.json b/.vscode/launch.json index 3d7c7926..f7954062 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,16 +7,35 @@ { "type": "lldb", "request": "launch", - "name": "Debug executable 'xicm-portal-gtk'", - "program": "${workspaceFolder}/build/xyz-iinuwa-credential-manager-portal-gtk/src/xyz-iinuwa-credential-manager-portal-gtk", + "name": "Debug Daemon (credsd)", + "program": "${workspaceFolder}/build/credsd/src/credsd", "args": [], "env": { - "GSETTINGS_SCHEMA_DIR": "${workspaceFolder}/build/xyz-iinuwa-credential-manager-portal-gtk/data", - "RUST_LOG": "xyz_iinuwa_credential_manager_portal_gtk=debug,libwebauthn=debug,libwebauthn::webauthn=debug,libwebauthn=warn,libwebauthn::proto::ctap2::preflight=debug,libwebauthn::transport::channel=debug" + "RUST_LOG": "credsd=debug,libwebauthn=debug,libwebauthn::webauthn=debug,libwebauthn=warn,libwebauthn::proto::ctap2::preflight=debug,libwebauthn::transport::channel=debug,zbus::object_server::debug,zbus=debug" }, "sourceLanguages": ["rust"], "cwd": "${workspaceFolder}", - "preLaunchTask": "Meson: Build all targets" + "preLaunchTask": "Meson: Build Daemon" }, + { + "type": "lldb", + "request": "launch", + "name": "Debug UI (creds-ui)", + "program": "${workspaceFolder}/build/creds-ui/src/creds-ui", + "args": [], + "env": { + "GSETTINGS_SCHEMA_DIR": "${workspaceFolder}/build/creds-ui/data", + "RUST_LOG": "creds_ui=debug,zbus::trace,zbus::object_server::debug" + }, + "sourceLanguages": ["rust"], + "cwd": "${workspaceFolder}", + "preLaunchTask": "Meson: Build UI" + }, + ], + "compounds": [ + { + "name": "Server/Client", + "configurations": ["Debug UI (creds-ui)", "Debug Daemon (credsd)"] + } ] } diff --git a/README.md b/README.md index 76502d0e..862b433e 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,16 @@ This project uses Meson and Ninja. Package requirements: - - GTK4 - - gettext - - libdbus-1 - - libssl/openssl - - libudev - - desktop-file-utils + +- GTK4 +- gettext +- libdbus-1 +- libssl/openssl +- libudev +- desktop-file-utils For example, on Ubuntu: + ```shell sudo apt update && sudo apt install \ # Build dependencies @@ -42,9 +44,10 @@ ninja -C build ```shell # Run the server, with debug logging enabled -export GSETTINGS_SCHEMA_DIR=build/xyz-iinuwa-credential-manager-portal-gtk/data -export RUST_LOG=xyz_iinuwa_credential_manager_portal_gtk=debug -./build/xyz-iinuwa-credential-manager-portal-gtk/src/xyz-iinuwa-credential-manager-portal-gtk +export GSETTINGS_SCHEMA_DIR=build/creds-ui/data +export RUST_LOG=credsd=debug,creds_ui=debug +./build/credsd/target/debug/credsd & +./build/creds-ui/target/debug/creds-ui ``` ### Clients @@ -59,7 +62,6 @@ cd demo_client/ There is also a demo web extension that can be used to test the service in Firefox. Instructions are in [webext/README.md](). - ## Goals The goal of this repository is to define a spec for clients (apps, browsers, @@ -77,28 +79,30 @@ Some high-level goals: etc.) to hook into Some nice-to-haves: + - Design a specification for a platform authenticator. I'm not sure whether this -needs to be specified, or whether it could be considered and implemented as a -first-party credential provider. + needs to be specified, or whether it could be considered and implemented as a + first-party credential provider. Some non-goals: -- Fully implement the proposed specification. This repo is focused on defining -the D-Bus API for clients and portal frontend/backend implementations to use. -Though I would love to help implement, I don't think I will have the time to -fully implement the features specced by the API, so I welcome collaboration -from others to help implement. For now, any implementation in this repository -is for reference purposes. +- Fully integrate with any specific desktop environment. Each desktop + environment (GNOME, KDE, etc.) has its own UI and UX conventions, as well as + system configuration methods (e.g., GNOME Settings), which this API will need to integrate with. + Because of the variation, we intend to leave integration with these other + components to developers more familiar with each of the desktop environments. + For now, we are using bare GTK to build a UI for testing, but any UI + implementation in this repository is for reference purposes. If anyone is willing to do some of this integration work, feel free to contact us! - Create a full-featured password manager. Features like Password syncing, -password generation, rotation, etc. is not part of this specficiation. Other -password manager projects should be able to use this to make their credentials -available to the user uniformly, though. + password generation, rotation, etc. is not part of this specficiation. Other + password manager projects should be able to use this to make their credentials + available to the user uniformly, though. -- BSD support. While I'd love to help out all open desktop environments, I don't -know enough about any BSD to make it useful for them. Hopefully, the design -process is transparent enough that someone else could design something that -works for BSDs. +- BSD support. While we'd love to help out all open desktop environments, we don't + know enough about any BSD to make it useful for them. Hopefully, the design + process is transparent enough that someone else could design something that + works for BSDs. ## Current Work @@ -106,8 +110,8 @@ works for BSDs. - March 2025: Integrated libwebauthn to support USB authenticators. - May 2024: Met with developers in GNOME and systemd to design internals for securely storing device credentials. -- Jan 2024: I've defined the [scenarios](doc/scenarios.md) that I expect this - API to cover. I am working on extracting [API methods](doc/api.md) required to +- Jan 2024: Defined the [scenarios](doc/scenarios.md) that we expect this + API to cover. We are working on extracting [API methods](doc/api.md) required to implement the interactions between the client, portal frontend, portal backend, machine and mobile devices. Once that is done, I intend to convert the API into a [portal spec](doc/design-doc.md), making it fit normal D-Bus/portal patterns. @@ -141,9 +145,8 @@ Alternatively, lock out the credential based on incorrect attempts. ![](images/security-key-3.png) ![](images/end.png) - - ## Related projects: + - https://github.com/linux-credentials/libwebauthn (previously https://github.com/AlfioEmanueleFresta/xdg-credentials-portal) - authenticator-rs - webauthn-rs diff --git a/contrib/xyz.iinuwa.credentials.CredentialManager.xml b/contrib/xyz.iinuwa.credentials.CredentialManager.xml index 056fcd89..f8182445 100644 --- a/contrib/xyz.iinuwa.credentials.CredentialManager.xml +++ b/contrib/xyz.iinuwa.credentials.CredentialManager.xml @@ -7,7 +7,7 @@ - + diff --git a/creds-lib/Cargo.lock b/creds-lib/Cargo.lock new file mode 100644 index 00000000..1b2c0e44 --- /dev/null +++ b/creds-lib/Cargo.lock @@ -0,0 +1,3523 @@ +# This file is automatically @generated by Cargo. +# 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "asn1-rs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 2.0.12", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.0.8", + "slab", + "windows-sys 0.60.2", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 1.0.8", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-signal" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.0.8", + "signal-hook-registry", + "slab", + "windows-sys 0.60.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "aws-lc-rs" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" +dependencies = [ + "bindgen", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64-url" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2b6c78c06f7288d5e3c3d683bde35a79531127c83b087e5d0d77c974b4b28" +dependencies = [ + "base64", +] + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.9.1", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bluez-async" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84ae4213cc2a8dc663acecac67bbdad05142be4d8ef372b6903abf878b0c690a" +dependencies = [ + "bitflags 2.9.1", + "bluez-generated", + "dbus", + "dbus-tokio", + "futures", + "itertools 0.14.0", + "log", + "serde", + "serde-xml-rs", + "thiserror 2.0.12", + "tokio", + "uuid", +] + +[[package]] +name = "bluez-generated" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9676783265eadd6f11829982792c6f303f3854d014edfba384685dcf237dd062" +dependencies = [ + "dbus", +] + +[[package]] +name = "btleplug" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a11621cb2c8c024e444734292482b1ad86fb50ded066cf46252e46643c8748" +dependencies = [ + "async-trait", + "bitflags 2.9.1", + "bluez-async", + "dashmap 6.1.0", + "dbus", + "futures", + "jni", + "jni-utils", + "log", + "objc2", + "objc2-core-bluetooth", + "objc2-foundation", + "once_cell", + "static_assertions", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "uuid", + "windows", + "windows-future", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cbor-smol" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087b31faa4ad4ba21c9bd0209204eef424dae6424195aafc7242006b69fc8d" +dependencies = [ + "delog", + "heapless", + "heapless-bytes", + "serde", +] + +[[package]] +name = "cc" +version = "1.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cosey" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75494895fa1a9713ca725ddf2db084ee84fb0c20938fdd7c89293febe732d30a" +dependencies = [ + "heapless-bytes", + "serde", + "serde_repr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "creds-lib" +version = "0.1.0" +dependencies = [ + "futures-lite", + "libwebauthn", + "serde", + "zbus", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctap-types" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb8b105c5e728afd373e99874f0c1911c170b3a56848456cc16feb4506321606" +dependencies = [ + "bitflags 1.3.2", + "cbor-smol", + "cosey", + "delog", + "heapless", + "heapless-bytes", + "iso7816", + "serde", + "serde-indexed 0.1.1", + "serde_bytes", + "serde_repr", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "futures-channel", + "futures-util", + "libc", + "libdbus-sys", + "winapi", +] + +[[package]] +name = "dbus-tokio" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "007688d459bc677131c063a3a77fb899526e17b7980f390b69644bdbc41fad13" +dependencies = [ + "dbus", + "libc", + "tokio", +] + +[[package]] +name = "delog" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af2b93368262340c9d4441251b824500d1b641a50957ecf4219a2cc41b9eac8f" +dependencies = [ + "log", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "serdect", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "heapless-bytes" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7285eba272c6af3e9f15fb9e1c1b6e7d35aa70580ffe0d47af017e97dfb6f48b" +dependencies = [ + "heapless", + "serde", + "typenum", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hidapi" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b876ecf37e86b359573c16c8366bc3eba52b689884a0fc42ba3f67203d2a8b" +dependencies = [ + "cc", + "cfg-if", + "libc", + "pkg-config", + "windows-sys 0.48.0", +] + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown 0.15.4", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + +[[package]] +name = "iso7816" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3c7e91da489667bb054f9cd2f1c60cc2ac4478a899f403d11dbc62189215b0" +dependencies = [ + "heapless", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jni-utils" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "259e9f2c3ead61de911f147000660511f07ab00adeed1d84f5ac4d0386e7a6c4" +dependencies = [ + "dashmap 5.5.3", + "futures", + "jni", + "log", + "once_cell", + "static_assertions", + "uuid", +] + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.53.3", +] + +[[package]] +name = "libwebauthn" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56ed4eb5e9a63098f7eeaf0fe0fc7b63c84977e8b8253ebe3707b124b6d8036" +dependencies = [ + "aes", + "async-trait", + "base64-url", + "bitflags 2.9.1", + "btleplug", + "byteorder", + "cbc", + "cosey", + "ctap-types", + "curve25519-dalek", + "dbus", + "futures", + "heapless", + "hex", + "hidapi", + "hkdf", + "hmac", + "maplit", + "mockall", + "num-derive", + "num-traits", + "num_enum", + "p256", + "rand 0.8.5", + "rustls", + "serde", + "serde-indexed 0.2.0", + "serde_bytes", + "serde_cbor_2", + "serde_derive", + "serde_repr", + "sha2", + "snow", + "text_io", + "thiserror 2.0.12", + "time", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tracing", + "tungstenite", + "uuid", + "x509-parser", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] +name = "mockall" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-core-bluetooth" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a644b62ffb826a5277f536cf0f701493de420b13d40e700c452c36567771111" +dependencies = [ + "bitflags 2.9.1", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.9.1", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "serdect", + "sha2", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "polling" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.0.8", + "windows-sys 0.60.2", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettyplease" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", + "serdect", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "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_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.60.2", +] + +[[package]] +name = "rustls" +version = "0.23.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.9.1", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-indexed" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca2da10b1f1623f47130256065e05e94fd7a98dbd26a780a4c5de831b21e5c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde-indexed" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68cf7478db8b81abcf71b6d195a34a4891bd3d39868731c4d73194d74ec7a3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde-xml-rs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53630160a98edebde0123eb4dfd0fce6adff091b2305db3154a9e920206eb510" +dependencies = [ + "log", + "serde", + "thiserror 1.0.69", + "xml-rs", +] + +[[package]] +name = "serde_bytes" +version = "0.11.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_cbor_2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aec2709de9078e077090abd848e967abab63c9fb3fdb5d4799ad359d8d482c" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "slab" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "snow" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "599b506ccc4aff8cf7844bc42cf783009a434c1e26c964432560fb6d6ad02d82" +dependencies = [ + "aes-gcm", + "blake2", + "chacha20poly1305", + "curve25519-dalek", + "getrandom 0.3.3", + "p256", + "ring", + "rustc_version", + "sha2", + "subtle", +] + +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix 1.0.8", + "windows-sys 0.59.0", +] + +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + +[[package]] +name = "text_io" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d8d3ca3b06292094e03841d8995e910712d2a10b5869c8f9725385b29761115" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tokio" +version = "1.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "slab", + "socket2", + "tokio-macros", + "windows-sys 0.59.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.2", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror 2.0.12", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +dependencies = [ + "getrandom 0.3.3", + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +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.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "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", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "x509-parser" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror 2.0.12", + "time", +] + +[[package]] +name = "xml-rs" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" + +[[package]] +name = "zbus" +version = "5.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb4f9a464286d42851d18a605f7193b8febaf5b0919d71c6399b7b26e5b0aad" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "nix", + "ordered-stream", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "windows-sys 0.59.0", + "winnow", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef9859f68ee0c4ee2e8cde84737c78e3f4c54f946f2a38645d0d4c7a95327659" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +dependencies = [ + "serde", + "static_assertions", + "winnow", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zvariant" +version = "5.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91b3680bb339216abd84714172b5138a4edac677e641ef17e1d8cb1b3ca6e6f" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8c68501be459a8dbfffbe5d792acdd23b4959940fc87785fb013b32edbc208" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "static_assertions", + "syn", + "winnow", +] diff --git a/creds-lib/Cargo.toml b/creds-lib/Cargo.toml new file mode 100644 index 00000000..1f435551 --- /dev/null +++ b/creds-lib/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "creds-lib" +version = "0.1.0" +edition = "2024" + +[dependencies] +futures-lite = "2.6.0" +libwebauthn = "0.2" +serde = { version = "1", features = ["derive"] } +zbus = "5.9.0" diff --git a/creds-lib/meson.build b/creds-lib/meson.build new file mode 100644 index 00000000..d1462102 --- /dev/null +++ b/creds-lib/meson.build @@ -0,0 +1,39 @@ +# Currently, we're not building this with meson and just letting cargo path dependencies for the other projects build this lib. +# Not efficient, since the UI and daemon projects will build it separately, but leaving it this way for now. + +# common_lib_name = 'xyzii-credman-portal-gtk' +# base_id = 'xyz.iinuwa.CredentialManagerUi' +# +# cargo = find_program('cargo', required: true) +# +# version = meson.project_version() +# +# if get_option('profile') == 'development' +# profile = 'Devel' +# vcs_tag = run_command('git', 'rev-parse', '--short', 'HEAD', check: false).stdout().strip() +# if vcs_tag == '' +# version_suffix = '-devel' +# else +# version_suffix = '-@0@'.format(vcs_tag) +# endif +# application_id = '@0@.@1@'.format(base_id, profile) +# else +# profile = '' +# version_suffix = '' +# application_id = base_id +# endif +# +# meson.add_dist_script( +# meson.project_source_root() / 'build-aux/dist-vendor.sh', +# meson.project_build_root() / 'meson-dist' / common_lib_name + '-' + version, +# meson.project_source_root(), +# ) +# +# cargo_options = [ +# '--manifest-path', meson.project_source_root() / meson.current_source_dir() / 'Cargo.toml', +# ] +# cargo_options += [ +# '--target-dir', meson.project_build_root() / meson.current_build_dir() / 'target', +# ] +# +# subdir('src') diff --git a/creds-lib/src/client.rs b/creds-lib/src/client.rs new file mode 100644 index 00000000..be656fa9 --- /dev/null +++ b/creds-lib/src/client.rs @@ -0,0 +1,25 @@ +use std::pin::Pin; + +use futures_lite::Stream; + +use crate::model::{BackgroundEvent, Device}; + +/// Used for communication from trusted UI to credential service +pub trait CredentialServiceClient { + fn get_available_public_key_devices( + &self, + ) -> impl Future, ()>> + Send; + + fn get_hybrid_credential(&mut self) -> impl Future> + Send; + fn get_usb_credential(&mut self) -> impl Future> + Send; + fn initiate_event_stream( + &mut self, + ) -> impl Future< + Output = Result + Send + 'static>>, ()>, + > + Send; + fn enter_client_pin(&mut self, pin: String) -> impl Future> + Send; + fn select_credential( + &self, + credential_id: String, + ) -> impl Future> + Send; +} diff --git a/creds-lib/src/lib.rs b/creds-lib/src/lib.rs new file mode 100644 index 00000000..cd7f939f --- /dev/null +++ b/creds-lib/src/lib.rs @@ -0,0 +1,3 @@ +pub mod client; +pub mod model; +pub mod server; diff --git a/creds-lib/src/meson.build b/creds-lib/src/meson.build new file mode 100644 index 00000000..441a13a9 --- /dev/null +++ b/creds-lib/src/meson.build @@ -0,0 +1,34 @@ +# Currently, we're not building this with meson and just letting cargo path dependencies for the other projects build this lib. +# Not efficient, since the UI and daemon projects will build it separately, but leaving it this way for now. + +# if get_option('profile') == 'default' +# cargo_options += ['--release'] +# rust_target = 'release' +# message('Building in release mode') +# else +# rust_target = 'debug' +# message('Building in debug mode') +# endif +# +# cargo_env = ['CARGO_HOME=' + meson.project_build_root() / 'cargo-home'] +# +# custom_target( +# 'cargo-build', +# build_by_default: true, +# build_always_stale: true, +# output: common_lib_name, +# console: true, +# install: true, +# install_dir: bindir, +# command: [ +# 'env', +# cargo_env, +# cargo, +# 'build', +# cargo_options, +# '&&', +# 'cp', +# common_lib_name / 'src' / rust_target / common_lib_name, +# '@OUTPUT@', +# ], +# ) diff --git a/creds-lib/src/model.rs b/creds-lib/src/model.rs new file mode 100644 index 00000000..97b5fde3 --- /dev/null +++ b/creds-lib/src/model.rs @@ -0,0 +1,333 @@ +use std::fmt::Display; + +use serde::{Deserialize, Serialize}; +use zbus::zvariant::{SerializeDict, Type}; + +pub use libwebauthn::ops::webauthn::{ + Assertion, GetAssertionRequest, MakeCredentialRequest, MakeCredentialResponse, +}; + +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct Credential { + pub id: String, + pub name: String, + pub username: Option, +} + +#[derive(Clone, Debug)] +pub enum CredentialRequest { + CreatePublicKeyCredentialRequest(MakeCredentialRequest), + GetPublicKeyCredentialRequest(GetAssertionRequest), +} + +#[derive(Clone, Debug)] +pub enum CredentialResponse { + CreatePublicKeyCredentialResponse(MakeCredentialResponseInternal), + GetPublicKeyCredentialResponse(GetAssertionResponseInternal), +} + +impl CredentialResponse { + pub fn from_make_credential( + response: &MakeCredentialResponse, + transports: &[&str], + modality: &str, + ) -> CredentialResponse { + CredentialResponse::CreatePublicKeyCredentialResponse(MakeCredentialResponseInternal::new( + response.clone(), + transports.iter().map(|s| s.to_string()).collect(), + modality.to_string(), + )) + } + + pub fn from_get_assertion(assertion: &Assertion, modality: &str) -> CredentialResponse { + CredentialResponse::GetPublicKeyCredentialResponse(GetAssertionResponseInternal::new( + assertion.clone(), + modality.to_string(), + )) + } +} + +#[derive(Clone, Debug)] +pub struct MakeCredentialResponseInternal { + pub ctap: MakeCredentialResponse, + pub transport: Vec, + pub attachment_modality: String, +} + +impl MakeCredentialResponseInternal { + pub fn new( + response: MakeCredentialResponse, + transport: Vec, + attachment_modality: String, + ) -> Self { + Self { + ctap: response, + transport, + attachment_modality, + } + } +} + +#[derive(Clone, Debug)] +pub struct GetAssertionResponseInternal { + pub ctap: Assertion, + pub attachment_modality: String, +} + +impl GetAssertionResponseInternal { + pub fn new(ctap: Assertion, attachment_modality: String) -> Self { + Self { + ctap, + attachment_modality, + } + } +} + +#[derive(SerializeDict, Type)] +#[zvariant(signature = "dict", rename_all = "camelCase")] +pub struct GetClientCapabilitiesResponse { + pub conditional_create: bool, + pub conditional_get: bool, + pub hybrid_transport: bool, + pub passkey_platform_authenticator: bool, + pub user_verifying_platform_authenticator: bool, + pub related_origins: bool, + pub signal_all_accepted_credentials: bool, + pub signal_current_user_details: bool, + pub signal_unknown_credential: bool, +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum CredentialType { + Passkey, + // Password, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Device { + pub id: String, + pub transport: Transport, +} + +#[derive(Debug, Serialize, Deserialize, Type)] +pub enum Operation { + Create, + Get, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum Transport { + Ble, + HybridLinked, + HybridQr, + Internal, + Nfc, + Usb, +} + +impl TryInto for String { + type Error = String; + + fn try_into(self) -> Result { + let value: &str = self.as_ref(); + value.try_into() + } +} + +impl TryInto for &str { + type Error = String; + + fn try_into(self) -> Result { + match self { + "BLE" => Ok(Transport::Ble), + "HybridLinked" => Ok(Transport::HybridLinked), + "HybridQr" => Ok(Transport::HybridQr), + "Internal" => Ok(Transport::Internal), + "NFC" => Ok(Transport::Nfc), + "USB" => Ok(Transport::Usb), + _ => Err(format!("Unrecognized transport: {}", self.to_owned())), + } + } +} + +impl From for String { + fn from(val: Transport) -> Self { + val.as_str().to_string() + } +} + +impl Transport { + pub fn as_str(&self) -> &'static str { + match self { + Transport::Ble => "BLE", + Transport::HybridLinked => "HybridLinked", + Transport::HybridQr => "HybridQr", + Transport::Internal => "Internal", + Transport::Nfc => "NFC", + Transport::Usb => "USB", + } + } +} + +#[derive(Serialize, Deserialize)] +pub enum ViewUpdate { + SetTitle(String), + SetDevices(Vec), + SetCredentials(Vec), + + WaitingForDevice(Device), + SelectingDevice, + + UsbNeedsPin { attempts_left: Option }, + UsbNeedsUserVerification { attempts_left: Option }, + UsbNeedsUserPresence, + + HybridNeedsQrCode(String), + HybridConnecting, + HybridConnected, + + Completed, + Failed(String), +} + +#[derive(Clone, Debug, Default)] +pub enum HybridState { + /// Default state, not listening for hybrid transport. + #[default] + Idle, + + /// QR code flow is starting, awaiting QR code scan and BLE advert from phone. + Started(String), + + /// BLE advert received, connecting to caBLE tunnel with shared secret. + Connecting, + + /// Connected to device via caBLE tunnel. + Connected, + + /// Credential received over tunnel. + Completed, + + // This isn't actually sent from the server. + UserCancelled, + + /// Failed to receive a credential + Failed, +} + +/// Used to share public state between credential service and UI. +#[derive(Clone, Debug, Default)] +pub enum UsbState { + /// Not polling for FIDO USB device. + #[default] + Idle, + + /// Awaiting FIDO USB device to be plugged in. + Waiting, + + // When we encounter multiple devices, we let all of them blink and continue + // with the one that was tapped. + SelectingDevice, + + /// USB device connected, prompt user to tap + Connected, + + /// The device needs the PIN to be entered. + NeedsPin { + attempts_left: Option, + }, + + /// The device needs on-device user verification. + NeedsUserVerification { + attempts_left: Option, + }, + + /// The device needs evidence of user presence (e.g. touch) to release the credential. + NeedsUserPresence, + // TODO: implement cancellation + // This isn't actually sent from the server. + //UserCancelled, + /// Multiple credentials have been found and the user has to select which to use + SelectCredential { + /// List of user-identities to decide which to use. + creds: Vec, + }, + + /// USB tapped, received credential + Completed, + + /// Interaction with the authenticator failed. + Failed(Error), +} + +pub enum BackgroundEvent { + UsbStateChanged(UsbState), + HybridQrStateChanged(HybridState), +} + +#[derive(Debug, Clone)] +pub enum Error { + /// Some unknown error with the authenticator occurred. + AuthenticatorError, + /// No matching credentials were found on the device. + NoCredentials, + /// Too many incorrect PIN attempts, and authenticator must be removed and + /// reinserted to continue any more PIN attempts. + /// + /// Note that this is different than exhausting the PIN count that fully + /// locks out the device. + PinAttemptsExhausted, + // TODO: We may want to hide the details on this variant from the public API. + /// Something went wrong with the credential service itself, not the authenticator. + Internal(String), +} + +impl std::error::Error for Error {} + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::AuthenticatorError => f.write_str("AuthenticatorError"), + Self::NoCredentials => f.write_str("NoCredentials"), + Self::PinAttemptsExhausted => f.write_str("PinAttemptsExhausted"), + Self::Internal(s) => write!(f, "InternalError: {s}"), + } + } +} + +pub enum WebAuthnError { + /// The ceremony was cancelled by an AbortController. See § 5.6 Abort + /// Operations with AbortSignal and § 1.3.4 Aborting Authentication + /// Operations. + AbortError, + + /// Either `residentKey` was set to required and no available authenticator + /// supported resident keys, or `userVerification` was set to required and no + /// available authenticator could perform user verification. + ConstraintError, + + /// The authenticator used in the ceremony recognized an entry in + /// `excludeCredentials` after the user consented to registering a credential. + InvalidStateError, + + /// No entry in `pubKeyCredParams` had a type property of `public-key`, or the + /// authenticator did not support any of the signature algorithms specified + /// in `pubKeyCredParams`. + NotSupportedError, + + /// The effective domain was not a valid domain, or `rp.id` was not equal to + /// or a registrable domain suffix of the effective domain. In the latter + /// case, the client does not support related origin requests or the related + /// origins validation procedure failed. + SecurityError, + + /// A catch-all error covering a wide range of possible reasons, including + /// common ones like the user canceling out of the ceremony. Some of these + /// causes are documented throughout this spec, while others are + /// client-specific. + NotAllowedError, + + /// The options argument was not a valid `CredentialCreationOptions` value, or + /// the value of `user.id` was empty or was longer than 64 bytes. + TypeError, +} diff --git a/creds-lib/src/server.rs b/creds-lib/src/server.rs new file mode 100644 index 00000000..aa915b7a --- /dev/null +++ b/creds-lib/src/server.rs @@ -0,0 +1,727 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; +use zbus::zvariant::{self, DeserializeDict, LE, Optional, OwnedValue, SerializeDict, Type, Value}; + +use crate::model::{Operation, ViewUpdate}; + +#[derive(Clone, Debug, Serialize, Deserialize, Type)] +pub enum BackgroundEvent { + UsbStateChanged(OwnedValue), + HybridStateChanged(OwnedValue), +} + +impl TryFrom for crate::model::BackgroundEvent { + type Error = zvariant::Error; + + fn try_from(value: BackgroundEvent) -> Result { + let ret = match value { + BackgroundEvent::HybridStateChanged(hybrid_state_val) => { + HybridState::try_from(Value::<'_>::from(hybrid_state_val)) + .and_then(crate::model::HybridState::try_from) + .map(crate::model::BackgroundEvent::HybridQrStateChanged) + } + BackgroundEvent::UsbStateChanged(usb_state_val) => { + UsbState::try_from(Value::<'_>::from(usb_state_val)) + .and_then(crate::model::UsbState::try_from) + .map(crate::model::BackgroundEvent::UsbStateChanged) + } + }?; + Ok(ret) + } +} + +impl TryFrom for BackgroundEvent { + type Error = zvariant::Error; + fn try_from(value: crate::model::BackgroundEvent) -> Result { + let event = match value { + crate::model::BackgroundEvent::HybridQrStateChanged(state) => { + let state: HybridState = state.into(); + let value = Value::new(state) + .try_to_owned() + .expect("non-file descriptor value to succeed"); + BackgroundEvent::HybridStateChanged(value) + } + crate::model::BackgroundEvent::UsbStateChanged(state) => { + let state: UsbState = state.into(); + let value = Value::new(state) + .try_to_owned() + .expect("non-file descriptor value to succeed"); + + BackgroundEvent::UsbStateChanged(value) + } + }; + Ok(event) + } +} + +#[derive(Clone, Debug, DeserializeDict, Type)] +#[zvariant(signature = "dict")] +pub struct CreateCredentialRequest { + pub origin: Option, + pub is_same_origin: Option, + #[zvariant(rename = "type")] + pub r#type: String, + #[zvariant(rename = "publicKey")] + pub public_key: Option, +} + +#[derive(SerializeDict, Type)] +#[zvariant(signature = "dict")] +pub struct CreateCredentialResponse { + #[zvariant(rename = "type")] + r#type: String, + public_key: Option, +} + +#[derive(Clone, Debug, DeserializeDict, Type)] +#[zvariant(signature = "dict")] +pub struct CreatePublicKeyCredentialRequest { + pub request_json: String, +} + +#[derive(SerializeDict, Type)] +#[zvariant(signature = "dict")] +pub struct CreatePublicKeyCredentialResponse { + pub registration_response_json: String, +} + +impl From for CreateCredentialResponse { + fn from(response: CreatePublicKeyCredentialResponse) -> Self { + CreateCredentialResponse { + // TODO: Decide on camelCase or kebab-case for cred types + r#type: "public-key".to_string(), + public_key: Some(response), + } + } +} + +/// Updates to send to the client +#[derive(Serialize, Deserialize, Type)] +pub enum ClientUpdate { + SetTitle(OwnedValue), + SetDevices(OwnedValue), + SetCredentials(OwnedValue), + + WaitingForDevice(OwnedValue), + SelectingDevice(OwnedValue), + + UsbNeedsPin(OwnedValue), + UsbNeedsUserVerification(OwnedValue), + UsbNeedsUserPresence(OwnedValue), + + HybridNeedsQrCode(OwnedValue), + HybridConnecting(OwnedValue), + HybridConnected(OwnedValue), + + Completed(OwnedValue), + Failed(OwnedValue), +} + +impl TryFrom for ViewUpdate { + type Error = zvariant::Error; + fn try_from(value: ClientUpdate) -> std::result::Result { + match value { + ClientUpdate::SetTitle(v) => v.try_into().map(Self::SetTitle), + ClientUpdate::SetDevices(v) => { + let dbus_devices: Vec = Value::<'_>::from(v).try_into()?; + let devices: std::result::Result, zvariant::Error> = + dbus_devices + .into_iter() + .map(|d| { + d.try_into().map_err(|_| { + zvariant::Error::Message( + "Could not deserialize devices".to_string(), + ) + }) + }) + .collect(); + Ok(Self::SetDevices(devices?)) + } + ClientUpdate::SetCredentials(v) => { + let dbus_credentials: Vec = Value::<'_>::from(v).try_into()?; + let credentials: std::result::Result< + Vec, + zvariant::Error, + > = dbus_credentials + .into_iter() + .map(|creds| Ok(creds.into())) + .collect(); + Ok(Self::SetCredentials(credentials?)) + } + + ClientUpdate::WaitingForDevice(v) => { + let dbus_device: Device = Value::<'_>::from(v).try_into()?; + let device: crate::model::Device = dbus_device.try_into().map_err(|_| { + zvariant::Error::Message("Could not deserialize device".to_string()) + })?; + Ok(Self::WaitingForDevice(device)) + } + ClientUpdate::SelectingDevice(_) => Ok(Self::SelectingDevice), + + ClientUpdate::UsbNeedsPin(v) => v.try_into().map(|x: i32| { + let attempts_left = if x == -1 { None } else { Some(x as u32) }; + Self::UsbNeedsPin { attempts_left } + }), + ClientUpdate::UsbNeedsUserVerification(v) => v.try_into().map(|x: i32| { + let attempts_left = if x == -1 { None } else { Some(x as u32) }; + Self::UsbNeedsUserVerification { attempts_left } + }), + ClientUpdate::UsbNeedsUserPresence(_) => Ok(Self::UsbNeedsUserPresence), + + ClientUpdate::HybridNeedsQrCode(v) => v.try_into().map(Self::HybridNeedsQrCode), + ClientUpdate::HybridConnecting(_) => Ok(Self::HybridConnecting), + ClientUpdate::HybridConnected(_) => Ok(Self::HybridConnected), + + ClientUpdate::Completed(_) => Ok(Self::Completed), + ClientUpdate::Failed(v) => v.try_into().map(Self::Failed), + } + } +} + +#[derive(SerializeDict, DeserializeDict, Type, Value)] +#[zvariant(signature = "dict")] +pub struct Credential { + id: String, + name: String, + username: Optional, +} + +impl From for crate::model::Credential { + fn from(value: Credential) -> Self { + Self { + id: value.id, + name: value.name, + username: value.username.into(), + } + } +} + +impl From for Credential { + fn from(value: crate::model::Credential) -> Self { + Self { + id: value.id, + name: value.name, + username: value.username.into(), + } + } +} + +/* +impl TryFrom> for Credential { + type Error = zvariant::Error; + fn try_from(value: Value<'_>) -> std::result::Result { + let ctx = zvariant::serialized::Context::new_dbus(LE, 0); + let encoded = zvariant::to_bytes(ctx, &value)?; + let credential: Credential = encoded.deserialize()?.0; + Ok(credential) + } +} + */ + +#[derive(SerializeDict, DeserializeDict, Type)] +#[zvariant(signature = "a{sv}")] +pub struct Device { + pub id: String, + pub transport: String, +} + +impl TryFrom> for Device { + type Error = zvariant::Error; + fn try_from(value: Value<'_>) -> std::result::Result { + let ctx = zvariant::serialized::Context::new_dbus(LE, 0); + let encoded = zvariant::to_bytes(ctx, &value)?; + let device: Device = encoded.deserialize()?.0; + Ok(device) + } +} + +impl From for Device { + fn from(value: crate::model::Device) -> Self { + Device { + id: value.id, + transport: value.transport.as_str().to_owned(), + } + } +} + +impl TryFrom for crate::model::Device { + type Error = (); + fn try_from(value: Device) -> std::result::Result { + let transport = value.transport.try_into().map_err(|_| ())?; + Ok(Self { + id: value.id, + transport, + }) + } +} + +#[derive(Clone, Debug, DeserializeDict, Type)] +#[zvariant(signature = "dict")] +pub struct GetCredentialRequest { + pub origin: Option, + pub is_same_origin: Option, + #[zvariant(rename = "type")] + pub r#type: String, + #[zvariant(rename = "publicKey")] + pub public_key: Option, +} + +#[derive(Clone, Debug, DeserializeDict, Type)] +#[zvariant(signature = "dict")] +pub struct GetPublicKeyCredentialRequest { + pub request_json: String, +} + +#[derive(SerializeDict, Type)] +#[zvariant(signature = "dict")] +pub struct GetCredentialResponse { + #[zvariant(rename = "type")] + r#type: String, + public_key: Option, +} + +#[derive(SerializeDict, Type)] +#[zvariant(signature = "dict")] +pub struct GetPublicKeyCredentialResponse { + pub authentication_response_json: String, +} + +impl From for GetCredentialResponse { + fn from(response: GetPublicKeyCredentialResponse) -> Self { + GetCredentialResponse { + // TODO: Decide on camelCase or kebab-case for cred types + r#type: "public-key".to_string(), + public_key: Some(response), + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, Type)] +pub enum HybridState { + /// Default state, not listening for hybrid transport. + Idle(OwnedValue), + + /// QR code flow is starting, awaiting QR code scan and BLE advert from phone. + Started(OwnedValue), + + /// BLE advert received, connecting to caBLE tunnel with shared secret. + Connecting(OwnedValue), + + /// Connected to device via caBLE tunnel. + Connected(OwnedValue), + + /// Credential received over tunnel. + Completed(OwnedValue), + + // This isn't actually sent from the server. + UserCancelled(OwnedValue), + + /// Failed to receive a credential + Failed(OwnedValue), +} + +impl From for HybridState { + fn from(value: crate::model::HybridState) -> Self { + match value { + crate::model::HybridState::Idle => HybridState::Idle(OwnedValue::from(false)), + crate::model::HybridState::Started(qr_code) => { + HybridState::Started(value_to_owned(&Value::from(qr_code))) + } + crate::model::HybridState::Connecting => { + HybridState::Connecting(OwnedValue::from(false)) + } + crate::model::HybridState::Connected => HybridState::Connected(OwnedValue::from(false)), + crate::model::HybridState::Completed => HybridState::Completed(OwnedValue::from(false)), + crate::model::HybridState::UserCancelled => { + HybridState::UserCancelled(OwnedValue::from(false)) + } + crate::model::HybridState::Failed => HybridState::Failed(OwnedValue::from(false)), + } + } +} +impl TryFrom for crate::model::HybridState { + type Error = zvariant::Error; + fn try_from(value: HybridState) -> std::result::Result { + match value { + HybridState::Idle(_) => Ok(Self::Idle), + HybridState::Started(value) => value.try_into().map(Self::Started), + HybridState::Connecting(_) => Ok(Self::Connecting), + HybridState::Connected(_) => Ok(Self::Connected), + HybridState::Completed(_) => Ok(Self::Completed), + HybridState::UserCancelled(_) => Ok(Self::UserCancelled), + HybridState::Failed(_) => Ok(Self::Failed), + } + } +} + +impl TryFrom> for HybridState { + type Error = zvariant::Error; + fn try_from(value: Value<'_>) -> std::result::Result { + let fields: HashMap> = value.try_into()?; + let tag = fields + .get("type") + .ok_or(zvariant::Error::Message( + "Expected a dictionary with `type` key".to_string(), + )) + .and_then(|t| t.try_into())?; + let value = fields.get("value").ok_or(zvariant::Error::Message( + "Expected a dictionary with `value` key".to_string(), + ))?; + match tag { + "IDLE" => Ok(Self::Idle(value_to_owned(value))), + "STARTED" => Ok(Self::Started(value_to_owned(value))), + "CONNECTING" => Ok(Self::Connecting(value_to_owned(value))), + "CONNECTED" => Ok(Self::Connected(value_to_owned(value))), + "COMPLETED" => Ok(Self::Completed(value_to_owned(value))), + "USER_CANCELLED" => Ok(Self::Completed(value_to_owned(value))), + "FAILED" => Ok(Self::Failed(value_to_owned(value))), + _ => Err(zvariant::Error::Message(format!( + "Invalid HybridState type passed: {tag}" + ))), + } + } +} + +impl From for Value<'_> { + fn from(value: HybridState) -> Self { + let mut fields = HashMap::new(); + match value { + HybridState::Idle(owned_value) => { + fields.insert("type", value_to_owned(&Value::from("IDLE"))); + fields.insert("value", owned_value); + } + HybridState::Started(owned_value) => { + fields.insert("type", value_to_owned(&Value::from("STARTED"))); + fields.insert("value", owned_value); + } + HybridState::Connecting(owned_value) => { + fields.insert("type", value_to_owned(&Value::from("CONNECTING"))); + fields.insert("value", owned_value); + } + HybridState::Connected(owned_value) => { + fields.insert("type", value_to_owned(&Value::from("CONNECTED"))); + fields.insert("value", owned_value); + } + HybridState::Completed(owned_value) => { + fields.insert("type", value_to_owned(&Value::from("COMPLETED"))); + fields.insert("value", owned_value); + } + HybridState::UserCancelled(owned_value) => { + fields.insert("type", value_to_owned(&Value::from("USER_CANCELLED"))); + fields.insert("value", owned_value); + } + HybridState::Failed(owned_value) => { + fields.insert("type", value_to_owned(&Value::from("FAILED"))); + fields.insert("value", owned_value); + } + } + Value::from(fields) + } +} + +#[derive(Serialize, Deserialize, Type)] +pub enum ServiceError { + /// Some unknown error with the authenticator occurred. + AuthenticatorError, + + /// No matching credentials were found on the device. + NoCredentials, + + /// Too many incorrect PIN attempts, and authenticator must be removed and + /// reinserted to continue any more PIN attempts. + /// + /// Note that this is different than exhausting the PIN count that fully + /// locks out the device. + PinAttemptsExhausted, + + // TODO: We may want to hide the details on this variant from the public API. + /// Something went wrong with the credential service itself, not the authenticator. + Internal, +} + +impl TryFrom> for ServiceError { + type Error = zvariant::Error; + fn try_from(value: Value<'_>) -> std::result::Result { + let ctx = zvariant::serialized::Context::new_dbus(LE, 0); + let encoded = zvariant::to_bytes(ctx, &value)?; + let obj: Self = encoded.deserialize()?.0; + Ok(obj) + } +} + +impl From for crate::model::Error { + fn from(value: ServiceError) -> Self { + match value { + ServiceError::AuthenticatorError => Self::AuthenticatorError, + ServiceError::NoCredentials => Self::NoCredentials, + ServiceError::PinAttemptsExhausted => Self::PinAttemptsExhausted, + // TODO: this is bogus, we should refactor to remove the tuple field + // and let the client decide how to render the error. + ServiceError::Internal => { + Self::Internal("Something went wrong. Please try again later.".to_string()) + } + } + } +} + +/// Used to de-/serialize state D-Bus and model::UsbState. +#[derive(Serialize, Deserialize, Type)] +pub enum UsbState { + Idle(OwnedValue), + Waiting(OwnedValue), + SelectingDevice(OwnedValue), + Connected(OwnedValue), + NeedsPin(OwnedValue), /* { + attempts_left: Option, + }, + */ + NeedsUserVerification(OwnedValue), /* { + attempts_left: Option, + },*/ + + NeedsUserPresence(OwnedValue), + //UserCancelled, + SelectCredential(OwnedValue), /* { + creds: Vec, + },*/ + Completed(OwnedValue), + // Failed(crate::credential_service::Error), + Failed(OwnedValue), +} + +impl TryFrom for crate::model::UsbState { + type Error = zvariant::Error; + fn try_from(value: UsbState) -> std::result::Result { + let ret = match value { + UsbState::Idle(_) => Ok(Self::Idle), + UsbState::Waiting(_) => Ok(Self::Waiting), + UsbState::SelectingDevice(_) => Ok(Self::SelectingDevice), + UsbState::Connected(_) => Ok(Self::Connected), + UsbState::NeedsPin(value) => value.try_into().map(|attempts_left: i32| { + let attempts_left = if attempts_left < 0 { + None + } else { + Some(u32::try_from(attempts_left).unwrap()) + }; + Self::NeedsPin { attempts_left } + }), + UsbState::NeedsUserVerification(value) => value.try_into().map(|attempts_left: i32| { + let attempts_left = if attempts_left < 0 { + None + } else { + Some(u32::try_from(attempts_left).unwrap()) + }; + Self::NeedsUserVerification { attempts_left } + }), + UsbState::NeedsUserPresence(_) => Ok(Self::NeedsUserPresence), + UsbState::SelectCredential(value) => value + .try_into() + .map(|creds: Vec| { + creds + .into_iter() + .map(crate::model::Credential::from) + .collect() + }) + .map(|creds| Self::SelectCredential { creds }), + UsbState::Completed(_) => Ok(Self::Completed), + UsbState::Failed(value) => { + let error_code: String = Value::<'_>::from(value).try_into()?; + Ok(Self::Failed( + match error_code.as_ref() { + "AuthenticatorError" => ServiceError::AuthenticatorError, + "NoCredentials" => ServiceError::NoCredentials, + "PinAttemptsExhausted" => ServiceError::PinAttemptsExhausted, + _ => ServiceError::Internal, + } + .into(), + )) + } + }?; + Ok(ret) + } +} + +impl From for UsbState { + fn from(value: crate::model::UsbState) -> Self { + match value { + crate::model::UsbState::Idle => UsbState::Idle(OwnedValue::from(false)), + crate::model::UsbState::Waiting => UsbState::Waiting(OwnedValue::from(false)), + crate::model::UsbState::SelectingDevice => { + UsbState::SelectingDevice(OwnedValue::from(false)) + } + crate::model::UsbState::Connected => UsbState::Connected(OwnedValue::from(false)), + crate::model::UsbState::NeedsPin { attempts_left } => { + let num = match attempts_left { + Some(num) => num as i32, + None => -1, + }; + UsbState::NeedsPin(OwnedValue::from(num)) + } + crate::model::UsbState::NeedsUserVerification { attempts_left } => { + let num = match attempts_left { + Some(num) => num as i32, + None => -1, + }; + UsbState::NeedsPin(OwnedValue::from(num)) + } + crate::model::UsbState::NeedsUserPresence => { + UsbState::NeedsUserPresence(OwnedValue::from(false)) + } + crate::model::UsbState::SelectCredential { creds } => { + let creds: Vec = creds.into_iter().map(Credential::from).collect(); + let value = Value::new(creds) + .try_to_owned() + .expect("All non-file descriptors to convert to OwnedValue successfully"); + UsbState::SelectCredential(value) + } + crate::model::UsbState::Completed => UsbState::Completed(OwnedValue::from(false)), + crate::model::UsbState::Failed(error) => UsbState::Failed( + Value::<'_>::from(error.to_string()) + .try_to_owned() + .expect("non-file descriptor value to convert"), + ), + } + } +} + +impl TryFrom> for UsbState { + type Error = zvariant::Error; + fn try_from(value: Value<'_>) -> std::result::Result { + let fields: HashMap> = value.try_into()?; + let tag = fields + .get("type") + .ok_or(zvariant::Error::Message( + "Expected a dictionary with `type` key".to_string(), + )) + .and_then(|t| t.try_into())?; + let value = fields.get("value").ok_or(zvariant::Error::Message( + "Expected a dictionary with `value` key".to_string(), + ))?; + match tag { + "IDLE" => Ok(Self::Idle(value_to_owned(value))), + "WAITING" => Ok(Self::Waiting(value_to_owned(value))), + "SELECTING_DEVICE" => Ok(Self::SelectingDevice(value_to_owned(value))), + "CONNECTED" => Ok(Self::SelectingDevice(value_to_owned(value))), + "NEEDS_PIN" => Ok(Self::NeedsPin(value_to_owned(value))), + "NEEDS_USER_VERIFICATION" => Ok(Self::NeedsUserVerification(value_to_owned(value))), + "NEEDS_USER_PRESENCE" => Ok(Self::NeedsUserPresence(value_to_owned(value))), + "SELECT_CREDENTIAL" => Ok(Self::SelectCredential(value_to_owned(value))), + "COMPLETED" => Ok(Self::Completed(value_to_owned(value))), + "FAILED" => Ok(Self::Failed(value_to_owned(value))), + _ => Err(zvariant::Error::Message(format!( + "Invalid UsbState type passed: {tag}" + ))), + } + } +} + +impl From for Value<'_> { + fn from(value: UsbState) -> Self { + let mut fields = HashMap::new(); + match value { + UsbState::Idle(owned_value) => { + fields.insert( + "type", + Value::from("IDLE") + .try_to_owned() + .expect("non-file descriptor fields to succeed"), + ); + fields.insert("value", owned_value); + } + UsbState::Waiting(owned_value) => { + fields.insert( + "type", + Value::from("WAITING") + .try_to_owned() + .expect("non-file descriptor fields to succeed"), + ); + fields.insert("value", owned_value); + } + UsbState::SelectingDevice(owned_value) => { + fields.insert( + "type", + Value::from("SELECTING_DEVICE") + .try_to_owned() + .expect("non-file descriptor fields to succeed"), + ); + fields.insert("value", owned_value); + } + UsbState::Connected(owned_value) => { + fields.insert( + "type", + Value::from("CONNECTED") + .try_to_owned() + .expect("non-file descriptor fields to succeed"), + ); + fields.insert("value", owned_value); + } + UsbState::NeedsPin(owned_value) => { + fields.insert( + "type", + Value::from("NEEDS_PIN") + .try_to_owned() + .expect("non-file descriptor fields to succeed"), + ); + fields.insert("value", owned_value); + } + UsbState::NeedsUserVerification(owned_value) => { + fields.insert( + "type", + Value::from("NEEDS_USER_VERIFICATION") + .try_to_owned() + .expect("non-file descriptor fields to succeed"), + ); + fields.insert("value", owned_value); + } + UsbState::NeedsUserPresence(owned_value) => { + fields.insert( + "type", + Value::from("NEEDS_USER_PRESENCE") + .try_to_owned() + .expect("non-file descriptor fields to succeed"), + ); + fields.insert("value", owned_value); + } + UsbState::SelectCredential(owned_value) => { + fields.insert( + "type", + Value::from("SELECT_CREDENTIAL") + .try_to_owned() + .expect("non-file descriptor fields to succeed"), + ); + fields.insert("value", owned_value); + } + UsbState::Completed(owned_value) => { + fields.insert( + "type", + Value::from("COMPLETED") + .try_to_owned() + .expect("non-file descriptor fields to succeed"), + ); + fields.insert("value", owned_value); + } + UsbState::Failed(owned_value) => { + fields.insert( + "type", + Value::from("FAILED") + .try_to_owned() + .expect("non-file descriptor fields to succeed"), + ); + fields.insert("value", owned_value); + } + }; + Value::from(fields) + } +} + +#[derive(Serialize, Deserialize, Type)] +pub struct ViewRequest { + pub operation: Operation, +} + +fn value_to_owned(value: &Value<'_>) -> OwnedValue { + value + .try_to_owned() + .expect("non-file descriptor values to succeed") +} diff --git a/creds-ui/.gitignore b/creds-ui/.gitignore new file mode 100644 index 00000000..f0c26c5c --- /dev/null +++ b/creds-ui/.gitignore @@ -0,0 +1 @@ +src/config.rs diff --git a/xyz-iinuwa-credential-manager-portal-gtk/Cargo.lock b/creds-ui/Cargo.lock similarity index 86% rename from xyz-iinuwa-credential-manager-portal-gtk/Cargo.lock rename to creds-ui/Cargo.lock index 79979596..b908a54a 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/Cargo.lock +++ b/creds-ui/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" @@ -63,9 +63,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "asn1-rs" @@ -131,9 +131,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", "event-listener-strategy", @@ -143,14 +143,15 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" dependencies = [ "async-task", "concurrent-queue", "fastrand", "futures-lite", + "pin-project-lite", "slab", ] @@ -160,7 +161,7 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-executor", "async-io", "async-lock", @@ -171,9 +172,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" dependencies = [ "async-lock", "cfg-if", @@ -182,10 +183,9 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 0.38.44", + "rustix 1.0.8", "slab", - "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -201,11 +201,11 @@ dependencies = [ [[package]] name = "async-process" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-io", "async-lock", "async-signal", @@ -214,8 +214,7 @@ dependencies = [ "cfg-if", "event-listener 5.4.0", "futures-lite", - "rustix 0.38.44", - "tracing", + "rustix 1.0.8", ] [[package]] @@ -231,9 +230,9 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" dependencies = [ "async-io", "async-lock", @@ -241,10 +240,10 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.44", + "rustix 1.0.8", "signal-hook-registry", "slab", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -274,28 +273,6 @@ dependencies = [ "wasm-bindgen-futures", ] -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "async-task" version = "4.7.1" @@ -330,15 +307,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.13.1" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7" +checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" dependencies = [ "aws-lc-sys", "zeroize", @@ -346,9 +323,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079" +checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" dependencies = [ "bindgen", "cc", @@ -359,9 +336,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -395,9 +372,9 @@ dependencies = [ [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bindgen" @@ -405,7 +382,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cexpr", "clang-sys", "itertools 0.12.1", @@ -430,9 +407,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "blake2" @@ -478,11 +455,11 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-task", "futures-io", "futures-lite", @@ -491,16 +468,16 @@ dependencies = [ [[package]] name = "bluez-async" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353dc0fbd494ab1d066ffdff16f07acbea46ca63f507e093c07fdf2408d84300" +checksum = "84ae4213cc2a8dc663acecac67bbdad05142be4d8ef372b6903abf878b0c690a" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "bluez-generated", "dbus", "dbus-tokio", "futures", - "itertools 0.13.0", + "itertools 0.14.0", "log", "serde", "serde-xml-rs", @@ -520,12 +497,12 @@ dependencies = [ [[package]] name = "btleplug" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b668804e0728a09c83cd9b94c9e1176717ea5522e8a3cb3688c2ac9a5f6e137c" +checksum = "c9a11621cb2c8c024e444734292482b1ad86fb50ded066cf46252e46643c8748" dependencies = [ "async-trait", - "bitflags 2.9.0", + "bitflags 2.9.1", "bluez-async", "dashmap 6.1.0", "dbus", @@ -543,19 +520,20 @@ dependencies = [ "tokio-stream", "uuid", "windows", + "windows-future", ] [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytemuck" -version = "1.23.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" [[package]] name = "byteorder" @@ -577,11 +555,11 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cairo-rs" -version = "0.20.7" +version = "0.20.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae50b5510d86cf96ac2370e66d8dc960882f3df179d6a5a1e52bd94a1416c0f7" +checksum = "91e3bd0f4e25afa9cabc157908d14eeef9067d6448c49414d17b3fb55f0eadd0" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cairo-sys-rs", "glib", "libc", @@ -589,9 +567,9 @@ dependencies = [ [[package]] name = "cairo-sys-rs" -version = "0.20.7" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f18b6bb8e43c7eb0f2aac7976afe0c61b6f5fc2ab7bc4c139537ea56c92290df" +checksum = "059cc746549898cbfd9a47754288e5a958756650ef4652bbb6c5f71a6bda4f8b" dependencies = [ "glib-sys", "libc", @@ -621,9 +599,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.19" +version = "1.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" dependencies = [ "jobserver", "libc", @@ -647,9 +625,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.17.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d4ba6e40bd1184518716a6e1a781bf9160e286d219ccdb8ab2612e74cfe4789" +checksum = "0d0390889d58f934f01cd49736275b4c2da15bcfc328c78ff2349907e6cabf22" dependencies = [ "smallvec", "target-lexicon", @@ -657,9 +635,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -783,6 +761,32 @@ dependencies = [ "libc", ] +[[package]] +name = "creds-lib" +version = "0.1.0" +dependencies = [ + "futures-lite", + "libwebauthn", + "serde", + "zbus", +] + +[[package]] +name = "creds-ui" +version = "0.1.0" +dependencies = [ + "async-std", + "creds-lib", + "futures-lite", + "gettext-rs", + "gtk4", + "qrcode", + "serde", + "tracing", + "tracing-subscriber", + "zbus", +] + [[package]] name = "critical-section" version = "1.2.0" @@ -945,9 +949,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", "pem-rfc7468", @@ -1063,9 +1067,9 @@ checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" [[package]] name = "enumflags2" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ "enumflags2_derive", "serde", @@ -1073,9 +1077,9 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", @@ -1090,12 +1094,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1163,21 +1167,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "fragile" version = "2.0.1" @@ -1294,9 +1283,9 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7563afd6ff0a221edfbb70a78add5075b8d9cb48e637a40a24c3ece3fea414d0" +checksum = "2fd242894c084f4beed508a56952750bce3e96e85eb68fdc153637daa163e10c" dependencies = [ "gdk-pixbuf-sys", "gio", @@ -1306,9 +1295,9 @@ dependencies = [ [[package]] name = "gdk-pixbuf-sys" -version = "0.20.7" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67f2587c9202bf997476bbba6aaed4f78a11538a2567df002a5f57f5331d0b5c" +checksum = "5b34f3b580c988bd217e9543a2de59823fafae369d1a055555e5f95a8b130b96" dependencies = [ "gio-sys", "glib-sys", @@ -1362,20 +1351,20 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -1421,9 +1410,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "gio" -version = "0.20.9" +version = "0.20.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4f00c70f8029d84ea7572dd0e1aaa79e5329667b4c17f329d79ffb1e6277487" +checksum = "8e27e276e7b6b8d50f6376ee7769a71133e80d093bdc363bd0af71664228b831" dependencies = [ "futures-channel", "futures-core", @@ -1438,9 +1427,9 @@ dependencies = [ [[package]] name = "gio-sys" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "160eb5250a26998c3e1b54e6a3d4ea15c6c7762a6062a19a7b63eff6e2b33f9e" +checksum = "521e93a7e56fc89e84aea9a52cfc9436816a4b363b030260b699950ff1336c83" dependencies = [ "glib-sys", "gobject-sys", @@ -1451,11 +1440,11 @@ dependencies = [ [[package]] name = "glib" -version = "0.20.9" +version = "0.20.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707b819af8059ee5395a2de9f2317d87a53dbad8846a2f089f0bb44703f37686" +checksum = "ffc4b6e352d4716d84d7dde562dd9aee2a7d48beb872dd9ece7f2d1515b2d683" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "futures-channel", "futures-core", "futures-executor", @@ -1472,9 +1461,9 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.20.7" +version = "0.20.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715601f8f02e71baef9c1f94a657a9a77c192aea6097cf9ae7e5e177cd8cde68" +checksum = "e8084af62f09475a3f529b1629c10c429d7600ee1398ae12dd3bf175d74e7145" dependencies = [ "heck", "proc-macro-crate", @@ -1485,9 +1474,9 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8928869a44cfdd1fccb17d6746e4ff82c8f82e41ce705aa026a52ca8dc3aefb" +checksum = "8ab79e1ed126803a8fb827e3de0e2ff95191912b8db65cee467edb56fc4cc215" dependencies = [ "libc", "system-deps", @@ -1513,9 +1502,9 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c773a3cb38a419ad9c26c81d177d96b4b08980e8bdbbf32dace883e96e96e7e3" +checksum = "ec9aca94bb73989e3cfdbf8f2e0f1f6da04db4d291c431f444838925c4c63eda" dependencies = [ "glib-sys", "libc", @@ -1524,9 +1513,9 @@ dependencies = [ [[package]] name = "graphene-rs" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbc5911bfb32d68dcfa92c9510c462696c2f715548fcd7f3f1be424c739de19" +checksum = "6b86dfad7d14251c9acaf1de63bc8754b7e3b4e5b16777b6f5a748208fe9519b" dependencies = [ "glib", "graphene-sys", @@ -1535,9 +1524,9 @@ dependencies = [ [[package]] name = "graphene-sys" -version = "0.20.7" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a68d39515bf340e879b72cecd4a25c1332557757ada6e8aba8654b4b81d23a" +checksum = "df583a85ba2d5e15e1797e40d666057b28bc2f60a67c9c24145e6db2cc3861ea" dependencies = [ "glib-sys", "libc", @@ -1589,9 +1578,9 @@ dependencies = [ [[package]] name = "gtk4" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1c491051f030994fd0cde6f3c44f3f5640210308cff1298c7673c47408091d" +checksum = "f274dd0102c21c47bbfa8ebcb92d0464fab794a22fad6c3f3d5f165139a326d6" dependencies = [ "cairo-rs", "field-offset", @@ -1666,9 +1655,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "heapless" @@ -1703,9 +1692,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -1783,12 +1772,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.4", ] [[package]] @@ -1801,6 +1790,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "iso7816" version = "0.1.4" @@ -1821,9 +1821,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] @@ -1875,7 +1875,7 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "libc", ] @@ -1912,9 +1912,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libdbus-sys" @@ -1932,18 +1932,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.3", ] [[package]] name = "libwebauthn" -version = "0.2.1" -source = "git+https://github.com/linux-credentials/libwebauthn?rev=34f8a59cb1634175b8baf866e6d30d1869f5a221#34f8a59cb1634175b8baf866e6d30d1869f5a221" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56ed4eb5e9a63098f7eeaf0fe0fc7b63c84977e8b8253ebe3707b124b6d8036" dependencies = [ "aes", "async-trait", "base64-url", - "bitflags 2.9.0", + "bitflags 2.9.1", "btleplug", "byteorder", "cbc", @@ -2012,9 +2013,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -2046,9 +2047,9 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memoffset" @@ -2067,22 +2068,22 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] @@ -2113,11 +2114,11 @@ dependencies = [ [[package]] name = "nix" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg-if", "cfg_aliases", "libc", @@ -2191,18 +2192,19 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2252,7 +2254,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a644b62ffb826a5277f536cf0f701493de420b13d40e700c452c36567771111" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "objc2", "objc2-foundation", ] @@ -2269,7 +2271,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "libc", "objc2", @@ -2314,50 +2316,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "openssl" -version = "0.10.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" -dependencies = [ - "bitflags 2.9.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "openssl-probe" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" -[[package]] -name = "openssl-sys" -version = "0.9.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "ordered-stream" version = "0.2.0" @@ -2389,9 +2353,9 @@ dependencies = [ [[package]] name = "pango" -version = "0.20.9" +version = "0.20.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1f5dc1b8cf9bc08bfc0843a04ee0fa2e78f1e1fa4b126844a383af4f25f0ec" +checksum = "6576b311f6df659397043a5fa8a021da8f72e34af180b44f7d57348de691ab5c" dependencies = [ "gio", "glib", @@ -2401,9 +2365,9 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dbb9b751673bd8fe49eb78620547973a1e719ed431372122b20abd12445bab5" +checksum = "186909673fc09be354555c302c0b3dcf753cd9fa08dcb8077fa663c80fb243fa" dependencies = [ "glib-sys", "gobject-sys", @@ -2419,9 +2383,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -2429,9 +2393,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -2490,17 +2454,16 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "polling" -version = "3.7.4" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 0.38.44", - "tracing", - "windows-sys 0.59.0", + "rustix 1.0.8", + "windows-sys 0.60.2", ] [[package]] @@ -2569,9 +2532,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.33" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" dependencies = [ "proc-macro2", "syn", @@ -2625,9 +2588,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -2642,9 +2605,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -2676,7 +2639,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -2685,16 +2648,16 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -2744,7 +2707,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -2752,9 +2715,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -2786,7 +2749,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -2795,22 +2758,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "rustls" -version = "0.23.27" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "aws-lc-rs", "log", @@ -2845,9 +2808,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "aws-lc-rs", "ring", @@ -2857,15 +2820,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" - -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "same-file" @@ -2912,7 +2869,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "core-foundation", "core-foundation-sys", "libc", @@ -2968,9 +2925,9 @@ dependencies = [ [[package]] name = "serde-xml-rs" -version = "0.6.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782" +checksum = "53630160a98edebde0123eb4dfd0fce6adff091b2305db3154a9e920206eb510" dependencies = [ "log", "serde", @@ -3008,18 +2965,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serde_json" -version = "1.0.140" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - [[package]] name = "serde_repr" version = "0.1.20" @@ -3033,9 +2978,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -3063,9 +3008,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -3089,9 +3034,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -3108,18 +3053,15 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "snow" @@ -3131,7 +3073,7 @@ dependencies = [ "blake2", "chacha20poly1305", "curve25519-dalek", - "getrandom 0.3.2", + "getrandom 0.3.3", "p256", "ring", "rustc_version", @@ -3141,12 +3083,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.9" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3188,9 +3130,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -3199,9 +3141,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", @@ -3210,9 +3152,9 @@ dependencies = [ [[package]] name = "system-deps" -version = "7.0.3" +version = "7.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005" +checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb" dependencies = [ "cfg-expr", "heck", @@ -3223,26 +3165,26 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.16" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" [[package]] name = "temp-dir" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc1ee6eef34f12f765cb94725905c6312b6610ab2b0940889cfe58dae7bc3c72" +checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964" [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.3", "once_cell", - "rustix 1.0.5", + "rustix 1.0.8", "windows-sys 0.59.0", ] @@ -3300,12 +3242,11 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -3341,21 +3282,22 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.1" +version = "1.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", - "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3409,9 +3351,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -3422,9 +3364,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", @@ -3434,18 +3376,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", @@ -3467,9 +3409,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", @@ -3478,9 +3420,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -3522,7 +3464,7 @@ dependencies = [ "http", "httparse", "log", - "rand 0.9.1", + "rand 0.9.2", "rustls", "rustls-pki-types", "sha1", @@ -3577,12 +3519,14 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", + "js-sys", "serde", + "wasm-bindgen", ] [[package]] @@ -3597,12 +3541,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" @@ -3627,9 +3565,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -3766,31 +3704,55 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.57.0" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ "windows-core", - "windows-targets 0.52.6", ] [[package]] name = "windows-core" -version = "0.57.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", + "windows-link", "windows-result", - "windows-targets 0.52.6", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", ] [[package]] name = "windows-implement" -version = "0.57.0" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", @@ -3799,22 +3761,47 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.57.0" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core", + "windows-link", +] + [[package]] name = "windows-result" -version = "0.1.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-targets 0.52.6", + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", ] [[package]] @@ -3844,6 +3831,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -3868,13 +3864,39 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "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", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -3887,6 +3909,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -3899,6 +3927,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -3911,12 +3945,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -3929,6 +3975,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -3941,6 +3993,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -3953,6 +4011,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3965,11 +4029,17 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" -version = "0.7.6" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -3980,7 +4050,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -4000,56 +4070,27 @@ dependencies = [ "time", ] -[[package]] -name = "xdg-home" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "xml-rs" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" - -[[package]] -name = "xyz-iinuwa-credential-manager-portal-gtk" -version = "0.1.0" -dependencies = [ - "async-std", - "async-stream", - "async-trait", - "base64", - "cosey", - "futures-lite", - "gettext-rs", - "gtk4", - "libwebauthn", - "openssl", - "qrcode", - "ring", - "rustls", - "serde", - "serde_json", - "tokio", - "tracing", - "tracing-subscriber", - "zbus", -] +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" [[package]] name = "zbus" -version = "5.5.0" +version = "5.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236" +checksum = "4bb4f9a464286d42851d18a605f7193b8febaf5b0919d71c6399b7b26e5b0aad" dependencies = [ "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", "async-recursion", + "async-task", "async-trait", + "blocking", "enumflags2", "event-listener 5.4.0", "futures-core", @@ -4059,13 +4100,10 @@ dependencies = [ "ordered-stream", "serde", "serde_repr", - "static_assertions", - "tokio", "tracing", "uds_windows", "windows-sys 0.59.0", "winnow", - "xdg-home", "zbus_macros", "zbus_names", "zvariant", @@ -4073,9 +4111,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.5.0" +version = "5.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0" +checksum = "ef9859f68ee0c4ee2e8cde84737c78e3f4c54f946f2a38645d0d4c7a95327659" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -4100,18 +4138,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.24" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.24" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", @@ -4126,14 +4164,13 @@ checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zvariant" -version = "5.4.0" +version = "5.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2df9ee044893fcffbdc25de30546edef3e32341466811ca18421e3cd6c5a3ac" +checksum = "d91b3680bb339216abd84714172b5138a4edac677e641ef17e1d8cb1b3ca6e6f" dependencies = [ "endi", "enumflags2", "serde", - "static_assertions", "winnow", "zvariant_derive", "zvariant_utils", @@ -4141,9 +4178,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.4.0" +version = "5.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74170caa85b8b84cc4935f2d56a57c7a15ea6185ccdd7eadb57e6edd90f94b2f" +checksum = "3a8c68501be459a8dbfffbe5d792acdd23b4959940fc87785fb013b32edbc208" dependencies = [ "proc-macro-crate", "proc-macro2", diff --git a/creds-ui/Cargo.toml b/creds-ui/Cargo.toml new file mode 100644 index 00000000..2df26d80 --- /dev/null +++ b/creds-ui/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "creds-ui" +version = "0.1.0" +edition = "2024" + +[dependencies] +async-std = { version = "1.13.1", features = ["unstable"] } +creds-lib = { path = "../creds-lib" } +futures-lite = "2.6.0" +gettext-rs = { version = "0.7", features = ["gettext-system"] } +gtk = { version = "0.9.6", package = "gtk4", features = ["v4_6"] } +qrcode = "0.14.1" +serde = { version = "1.0.219", features = ["derive"] } +tracing = "0.1.41" +tracing-subscriber = "0.3.19" +zbus = "5.9.0" diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/icons/check-round-outline-symbolic.svg b/creds-ui/data/icons/check-round-outline-symbolic.svg similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/icons/check-round-outline-symbolic.svg rename to creds-ui/data/icons/check-round-outline-symbolic.svg diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/icons/dialpad-symbolic.svg b/creds-ui/data/icons/dialpad-symbolic.svg similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/icons/dialpad-symbolic.svg rename to creds-ui/data/icons/dialpad-symbolic.svg diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/icons/fingerprint-symbolic.svg b/creds-ui/data/icons/fingerprint-symbolic.svg similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/icons/fingerprint-symbolic.svg rename to creds-ui/data/icons/fingerprint-symbolic.svg diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/icons/meson.build b/creds-ui/data/icons/meson.build similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/icons/meson.build rename to creds-ui/data/icons/meson.build diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/icons/symbolic-link-symbolic.svg b/creds-ui/data/icons/symbolic-link-symbolic.svg similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/icons/symbolic-link-symbolic.svg rename to creds-ui/data/icons/symbolic-link-symbolic.svg diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/icons/xyz.iinuwa.CredentialManagerUi-symbolic.svg b/creds-ui/data/icons/xyz.iinuwa.CredentialManagerUi-symbolic.svg similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/icons/xyz.iinuwa.CredentialManagerUi-symbolic.svg rename to creds-ui/data/icons/xyz.iinuwa.CredentialManagerUi-symbolic.svg diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/icons/xyz.iinuwa.CredentialManagerUi.Devel.svg b/creds-ui/data/icons/xyz.iinuwa.CredentialManagerUi.Devel.svg similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/icons/xyz.iinuwa.CredentialManagerUi.Devel.svg rename to creds-ui/data/icons/xyz.iinuwa.CredentialManagerUi.Devel.svg diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/icons/xyz.iinuwa.CredentialManagerUi.svg b/creds-ui/data/icons/xyz.iinuwa.CredentialManagerUi.svg similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/icons/xyz.iinuwa.CredentialManagerUi.svg rename to creds-ui/data/icons/xyz.iinuwa.CredentialManagerUi.svg diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/meson.build b/creds-ui/data/meson.build similarity index 64% rename from xyz-iinuwa-credential-manager-portal-gtk/data/meson.build rename to creds-ui/data/meson.build index ead7b4c9..8960582c 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/data/meson.build +++ b/creds-ui/data/meson.build @@ -8,21 +8,19 @@ desktop_file = i18n.merge_file( input: configure_file( input: '@0@.desktop.in.in'.format(base_id), output: '@BASENAME@', - configuration: desktop_conf + configuration: desktop_conf, ), output: '@0@.desktop'.format(application_id), po_dir: podir, install: true, - install_dir: datadir / 'applications' + install_dir: datadir / 'applications', ) # Validate Desktop file if desktop_file_validate.found() test( 'validate-desktop', desktop_file_validate, - args: [ - desktop_file.full_path() - ], + args: [desktop_file.full_path()], depends: desktop_file, ) endif @@ -35,20 +33,19 @@ appdata_file = i18n.merge_file( input: configure_file( input: '@0@.metainfo.xml.in.in'.format(base_id), output: '@BASENAME@', - configuration: appdata_conf + configuration: appdata_conf, ), output: '@0@.metainfo.xml'.format(application_id), po_dir: podir, install: true, - install_dir: datadir / 'metainfo' + install_dir: datadir / 'metainfo', ) # Validate Appdata if appstreamcli.found() test( - 'validate-appdata', appstreamcli, - args: [ - 'validate', '--no-net', '--explain', appdata_file.full_path() - ], + 'validate-appdata', + appstreamcli, + args: ['validate', '--no-net', '--explain', appdata_file.full_path()], depends: appdata_file, ) endif @@ -62,25 +59,26 @@ gschema_xml = configure_file( output: '@0@.gschema.xml'.format(application_id), configuration: gschema_conf, install: true, - install_dir: datadir / 'glib-2.0' / 'schemas' + install_dir: datadir / 'glib-2.0' / 'schemas', ) # Validata GSchema if glib_compile_schemas.found() test( - 'validate-gschema', glib_compile_schemas, - args: [ - '--strict', '--dry-run', meson.current_build_dir() - ], + 'validate-gschema', + glib_compile_schemas, + args: ['--strict', '--dry-run', meson.current_build_dir()], ) endif if get_option('profile') == 'development' - custom_target('gschema', - input : gschema_xml, - output : 'gschema.compiled', - command : [glib_compile_schemas, '--strict', meson.current_build_dir()], - install: false, - build_by_default : true, - build_always_stale : true) -endif + custom_target( + 'gschema', + input: gschema_xml, + output: 'gschema.compiled', + command: [glib_compile_schemas, '--strict', meson.current_build_dir()], + install: false, + build_by_default: true, + build_always_stale: true, + ) +endif \ No newline at end of file diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/resources/meson.build b/creds-ui/data/resources/meson.build similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/resources/meson.build rename to creds-ui/data/resources/meson.build diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/resources/resources.gresource.xml b/creds-ui/data/resources/resources.gresource.xml similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/resources/resources.gresource.xml rename to creds-ui/data/resources/resources.gresource.xml diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/resources/style.css b/creds-ui/data/resources/style.css similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/resources/style.css rename to creds-ui/data/resources/style.css diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/resources/ui/shortcuts.ui b/creds-ui/data/resources/ui/shortcuts.ui similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/resources/ui/shortcuts.ui rename to creds-ui/data/resources/ui/shortcuts.ui diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/resources/ui/window.ui b/creds-ui/data/resources/ui/window.ui similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/resources/ui/window.ui rename to creds-ui/data/resources/ui/window.ui diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/xyz.iinuwa.CredentialManagerUi.desktop.in.in b/creds-ui/data/xyz.iinuwa.CredentialManagerUi.desktop.in.in similarity index 89% rename from xyz-iinuwa-credential-manager-portal-gtk/data/xyz.iinuwa.CredentialManagerUi.desktop.in.in rename to creds-ui/data/xyz.iinuwa.CredentialManagerUi.desktop.in.in index 4bf41a81..10694f2e 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/data/xyz.iinuwa.CredentialManagerUi.desktop.in.in +++ b/creds-ui/data/xyz.iinuwa.CredentialManagerUi.desktop.in.in @@ -2,7 +2,7 @@ Name=Credential Manager Comment=Write a GTK + Rust application Type=Application -Exec=xyz-iinuwa-credential-manager-portal-gtk +Exec=credsd Terminal=false Categories=GNOME;GTK; # Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/xyz.iinuwa.CredentialManagerUi.gschema.xml.in b/creds-ui/data/xyz.iinuwa.CredentialManagerUi.gschema.xml.in similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/xyz.iinuwa.CredentialManagerUi.gschema.xml.in rename to creds-ui/data/xyz.iinuwa.CredentialManagerUi.gschema.xml.in diff --git a/xyz-iinuwa-credential-manager-portal-gtk/data/xyz.iinuwa.CredentialManagerUi.metainfo.xml.in.in b/creds-ui/data/xyz.iinuwa.CredentialManagerUi.metainfo.xml.in.in similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/data/xyz.iinuwa.CredentialManagerUi.metainfo.xml.in.in rename to creds-ui/data/xyz.iinuwa.CredentialManagerUi.metainfo.xml.in.in diff --git a/xyz-iinuwa-credential-manager-portal-gtk/meson.build b/creds-ui/meson.build similarity index 80% rename from xyz-iinuwa-credential-manager-portal-gtk/meson.build rename to creds-ui/meson.build index 50339cdc..cff09cf5 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/meson.build +++ b/creds-ui/meson.build @@ -1,16 +1,15 @@ i18n = import('i18n') gnome = import('gnome') -backend_executable_name = 'xyz-iinuwa-credential-manager-portal-gtk' +gui_executable_name = 'creds-ui' +gui_build_dir = meson.current_build_dir() +gui_source_dir = meson.current_source_dir() base_id = 'xyz.iinuwa.CredentialManagerUi' dependency('dbus-1', version: '>= 1.6') dependency('glib-2.0', version: '>= 2.66') dependency('gio-2.0', version: '>= 2.66') dependency('gtk4', version: '>= 4.6.2') -dependency('ssl', 'openssl', version: '>= 3.0') -dependency('udev', version: '>= 249') - glib_compile_resources = find_program('glib-compile-resources', required: true) glib_compile_schemas = find_program('glib-compile-schemas', required: true) @@ -30,10 +29,10 @@ bindir = prefix / get_option('bindir') localedir = prefix / get_option('localedir') datadir = prefix / get_option('datadir') -pkgdatadir = datadir / backend_executable_name +pkgdatadir = datadir / gui_executable_name iconsdir = datadir / 'icons' -podir = meson.project_source_root() / backend_executable_name / 'po' -gettext_package = backend_executable_name +podir = meson.project_source_root() / meson.current_source_dir() / 'po' +gettext_package = gui_executable_name if get_option('profile') == 'development' profile = 'Devel' @@ -52,8 +51,10 @@ endif meson.add_dist_script( meson.project_source_root() / 'build-aux/dist-vendor.sh', - meson.project_build_root() / 'meson-dist' / backend_executable_name + '-' + version, - meson.project_source_root() + meson.project_build_root() / 'meson-dist' / gui_executable_name + + '-' + + version, + meson.project_source_root(), ) if get_option('profile') == 'development' @@ -65,7 +66,6 @@ endif subdir('data') subdir('po') subdir('src') -subdir('tests') gnome.post_install( gtk_update_icon_cache: true, diff --git a/xyz-iinuwa-credential-manager-portal-gtk/po/LINGUAS b/creds-ui/po/LINGUAS similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/po/LINGUAS rename to creds-ui/po/LINGUAS diff --git a/xyz-iinuwa-credential-manager-portal-gtk/po/POTFILES.in b/creds-ui/po/POTFILES.in similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/po/POTFILES.in rename to creds-ui/po/POTFILES.in diff --git a/xyz-iinuwa-credential-manager-portal-gtk/po/meson.build b/creds-ui/po/meson.build similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/po/meson.build rename to creds-ui/po/meson.build diff --git a/creds-ui/src/client.rs b/creds-ui/src/client.rs new file mode 100644 index 00000000..c5c6d9e1 --- /dev/null +++ b/creds-ui/src/client.rs @@ -0,0 +1,102 @@ +use async_std::stream::Stream; +use creds_lib::client::CredentialServiceClient; +use futures_lite::StreamExt; +use zbus::{Connection, zvariant}; + +use crate::dbus::FlowControlServiceProxy; + +pub struct DbusCredentialClient { + conn: Connection, +} + +impl DbusCredentialClient { + pub fn new(conn: Connection) -> Self { + Self { conn } + } + async fn proxy(&self) -> std::result::Result { + FlowControlServiceProxy::new(&self.conn) + .await + .map_err(|err| tracing::error!("Failed to communicate with D-Bus service: {err}")) + } +} + +impl CredentialServiceClient for DbusCredentialClient { + async fn get_available_public_key_devices( + &self, + ) -> std::result::Result, ()> { + let dbus_devices = self + .proxy() + .await? + .get_available_public_key_devices() + .await + .map_err(|err| { + tracing::error!("Failed to retrieve available devices/transports: {err}") + })?; + dbus_devices.into_iter().map(|d| d.try_into()).collect() + } + + async fn get_hybrid_credential(&mut self) -> std::result::Result<(), ()> { + self.proxy() + .await? + .get_hybrid_credential() + .await + .inspect_err(|err| tracing::error!("Failed to start hybrid credential flow: {err}")) + .map_err(|_| ()) + } + + async fn get_usb_credential(&mut self) -> std::result::Result<(), ()> { + self.proxy() + .await? + .get_usb_credential() + .await + .inspect_err(|err| tracing::error!("Failed to start USB credential flow: {err}")) + .map_err(|_| ()) + } + + async fn initiate_event_stream( + &mut self, + ) -> std::result::Result< + std::pin::Pin + Send + 'static>>, + (), + > { + let stream = self + .proxy() + .await? + .receive_state_changed() + .await + .map_err(|err| tracing::error!("Failed to initalize event stream: {err}"))? + .filter_map(|msg| { + msg.args() + .and_then(|args| { + args.update + .try_into() + .map_err(|err: zvariant::Error| err.into()) + }) + .inspect_err(|err| tracing::warn!("Failed to parse StateChanged signal: {err}")) + .ok() + }) + .boxed(); + self.proxy() + .await? + .initiate_event_stream() + .await + .map_err(|err| tracing::error!("Failed to initialize event stream: {err}")) + .and_then(|_| Ok(stream)) + } + + async fn enter_client_pin(&mut self, pin: String) -> std::result::Result<(), ()> { + self.proxy() + .await? + .enter_client_pin(pin) + .await + .map_err(|err| tracing::error!("Failed to send PIN to authenticator: {err}")) + } + + async fn select_credential(&self, credential_id: String) -> std::result::Result<(), ()> { + self.proxy() + .await? + .select_credential(credential_id) + .await + .map_err(|err| tracing::error!("Failed to select credential: {err}")) + } +} diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/config.rs.in b/creds-ui/src/config.rs.in similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/src/config.rs.in rename to creds-ui/src/config.rs.in diff --git a/creds-ui/src/dbus.rs b/creds-ui/src/dbus.rs new file mode 100644 index 00000000..18bbe3aa --- /dev/null +++ b/creds-ui/src/dbus.rs @@ -0,0 +1,44 @@ +use async_std::channel::Sender; +use creds_lib::server::{BackgroundEvent, Device, ViewRequest}; +use zbus::{fdo, interface, proxy}; + +#[proxy( + gen_blocking = false, + interface = "xyz.iinuwa.credentials.FlowControl1", + default_path = "/xyz/iinuwa/credentials/FlowControl", + default_service = "xyz.iinuwa.credentials.FlowControl" +)] +pub trait FlowControlService { + async fn initiate_event_stream(&self) -> fdo::Result<()>; + + async fn get_available_public_key_devices(&self) -> fdo::Result>; + + async fn get_hybrid_credential(&self) -> fdo::Result<()>; + + async fn get_usb_credential(&self) -> fdo::Result<()>; + + async fn select_device(&self, device_id: String) -> fdo::Result<()>; + async fn enter_client_pin(&self, pin: String) -> fdo::Result<()>; + async fn select_credential(&self, credential_id: String) -> fdo::Result<()>; + + #[zbus(signal)] + async fn state_changed(update: BackgroundEvent) -> zbus::Result<()>; +} + +pub struct UiControlService { + pub request_tx: Sender, + // pub update_tx: Sender, +} + +/// These methods are called by the credential service to control the UI. +#[interface(name = "xyz.iinuwa.credentials.UiControl1")] +impl UiControlService { + async fn launch_ui(&self, request: creds_lib::server::ViewRequest) -> fdo::Result<()> { + tracing::debug!("Received UI launch request"); + self.request_tx + .send(request) + .await + .map_err(|_| fdo::Error::Failed("UI failed to launch".to_string())) + } + // fn send_state_changed(&self, event: BackgroundEvent) {} +} diff --git a/creds-ui/src/gui/mod.rs b/creds-ui/src/gui/mod.rs new file mode 100644 index 00000000..ffe3643d --- /dev/null +++ b/creds-ui/src/gui/mod.rs @@ -0,0 +1,80 @@ +pub mod view_model; + +use std::thread; +use std::{sync::Arc, thread::JoinHandle}; + +use async_std::{channel::Receiver, sync::Mutex as AsyncMutex}; + +use creds_lib::server::ViewRequest; +use creds_lib::{ + client::CredentialServiceClient, + model::{Operation, ViewUpdate}, +}; + +use view_model::ViewEvent; + +pub(super) fn start_gui_thread( + rx: Receiver, + client: C, +) -> Result, std::io::Error> { + thread::Builder::new().name("gui".into()).spawn(move || { + let client = Arc::new(AsyncMutex::new(client)); + // D-Bus received a request and needs a window open + while let Ok(view_request) = rx.recv_blocking() { + run_gui(client.clone(), view_request); + } + }) +} + +fn run_gui( + client: Arc>, + request: ViewRequest, +) { + let operation = request.operation; + let (tx_update, rx_update) = async_std::channel::unbounded::(); + let (tx_event, rx_event) = async_std::channel::unbounded::(); + let event_loop = async_std::task::spawn(async move { + let mut vm = view_model::ViewModel::new(operation, client, rx_event, tx_update); + vm.start_event_loop().await; + println!("event loop ended?"); + }); + + view_model::gtk::start_gtk_app(tx_event, rx_update); + + async_std::task::block_on(event_loop.cancel()); +} + +trait GuiClient { + /// Mark the GUI as ready to receive events from credential service. + /// Returns a queue of updates for updating the GUI state. + async fn initiate_event_stream(&self) -> Result, ()>; + + /// Select a authenticator or transport to interact with a credential. + async fn select_device(&self, device_id: String) -> Result<(), ()>; + + /// Send the client PIN to an authenticator. + async fn enter_client_pin(&self, pin: String) -> Result<(), ()>; + + /// Confirm user's credential selection when an authenticator returns multiple credentials. + async fn select_credential(&self, credential_id: String) -> Result<(), ()>; +} + +struct InProcessGuiClient {} + +impl GuiClient for InProcessGuiClient { + async fn initiate_event_stream(&self) -> Result, ()> { + todo!() + } + + async fn select_device(&self, device_id: String) -> Result<(), ()> { + todo!() + } + + async fn enter_client_pin(&self, pin: String) -> Result<(), ()> { + todo!() + } + + async fn select_credential(&self, credential_id: String) -> Result<(), ()> { + todo!() + } +} diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/gtk/application.rs b/creds-ui/src/gui/view_model/gtk/application.rs similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/gtk/application.rs rename to creds-ui/src/gui/view_model/gtk/application.rs diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/gtk/credential.rs b/creds-ui/src/gui/view_model/gtk/credential.rs similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/gtk/credential.rs rename to creds-ui/src/gui/view_model/gtk/credential.rs diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/gtk/device.rs b/creds-ui/src/gui/view_model/gtk/device.rs similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/gtk/device.rs rename to creds-ui/src/gui/view_model/gtk/device.rs diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/gtk/mod.rs b/creds-ui/src/gui/view_model/gtk/mod.rs similarity index 99% rename from xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/gtk/mod.rs rename to creds-ui/src/gui/view_model/gtk/mod.rs index d74a0f1f..16522b2a 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/gtk/mod.rs +++ b/creds-ui/src/gui/view_model/gtk/mod.rs @@ -312,10 +312,6 @@ impl ViewModel { self.set_prompt("Multiple devices found. Please select with which to proceed."); } - pub async fn send_thingy(&self) { - self.send_event(ViewEvent::ButtonClicked).await; - } - pub async fn send_usb_device_pin(&self, pin: String) { self.send_event(ViewEvent::UsbPinEntered(pin)).await; } diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/gtk/window.rs b/creds-ui/src/gui/view_model/gtk/window.rs similarity index 93% rename from xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/gtk/window.rs rename to creds-ui/src/gui/view_model/gtk/window.rs index 2db244ba..c1f41cb2 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/gtk/window.rs +++ b/creds-ui/src/gui/view_model/gtk/window.rs @@ -41,20 +41,6 @@ mod imp { #[gtk::template_callbacks] impl ExampleApplicationWindow { - #[template_callback] - fn handle_button_clicked(&self, _: >k::Button) { - println!("clicked"); - let view_model = &self.view_model.borrow(); - let view_model = view_model.as_ref().unwrap(); - glib::spawn_future_local(clone!( - #[weak] - view_model, - async move { - view_model.send_thingy().await; - } - )); - } - #[template_callback] fn handle_usb_pin_entered(&self, entry: >k::PasswordEntry) { let view_model = &self.view_model.borrow(); diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/mod.rs b/creds-ui/src/gui/view_model/mod.rs similarity index 54% rename from xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/mod.rs rename to creds-ui/src/gui/view_model/mod.rs index ed35dd4e..3454baa0 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/src/gui/view_model/mod.rs +++ b/creds-ui/src/gui/view_model/mod.rs @@ -5,13 +5,17 @@ use std::sync::Arc; use async_std::prelude::*; use async_std::{ channel::{Receiver, Sender}, - sync::Mutex, + sync::Mutex as AsyncMutex, }; -use tokio::sync::mpsc; +use serde::{Deserialize, Serialize}; use tracing::{error, info}; -use crate::credential_service::{ - CredentialServiceClient, Error as CredentialServiceError, UsbState, +use creds_lib::{ + client::CredentialServiceClient, + model::{ + BackgroundEvent, Credential, Device, Error, HybridState, Operation, Transport, UsbState, + ViewUpdate, + }, }; #[derive(Debug)] @@ -19,10 +23,9 @@ pub(crate) struct ViewModel where C: CredentialServiceClient + Send, { - credential_service: Arc>, + credential_service: Arc>, tx_update: Sender, rx_event: Receiver, - bg_update: Sender, bg_event: Receiver, title: String, operation: Operation, @@ -32,8 +35,7 @@ where selected_device: Option, // providers: Vec, - usb_pin_tx: Option>>>, - usb_cred_tx: Option>>>, + usb_cred_tx: Option>>>, hybrid_qr_state: HybridState, hybrid_qr_code_data: Option>, @@ -43,22 +45,20 @@ where impl ViewModel { pub(crate) fn new( operation: Operation, - credential_service: C, + credential_service: Arc>, rx_event: Receiver, tx_update: Sender, ) -> Self { let (bg_update, bg_event) = async_std::channel::unbounded::(); Self { - credential_service: Arc::new(Mutex::new(credential_service)), + credential_service, rx_event, tx_update, - bg_update, bg_event, operation, title: String::default(), devices: Vec::new(), selected_device: None, - usb_pin_tx: None, usb_cred_tx: None, hybrid_qr_state: HybridState::default(), hybrid_qr_code_data: None, @@ -94,7 +94,7 @@ impl ViewModel { pub(crate) async fn select_device(&mut self, id: &str) { let device = self.devices.iter().find(|d| d.id == id).unwrap(); - println!("{:?}", device); + tracing::debug!("Device selected: {:?}", device); // Handle previous device if let Some(prev_device) = self.selected_device.replace(device.clone()) { @@ -117,35 +117,12 @@ impl ViewModel { // start discovery for newly selected device match device.transport { Transport::Usb => { - let cred_service = self.credential_service.clone(); - let tx = self.bg_update.clone(); - let mut stream = { - let cred_service = cred_service.lock().await; - cred_service.get_usb_credential().await - }; - async_std::task::spawn(async move { - // TODO: add cancellation - while let Some(usb_state) = stream.next().await { - // forward to background event loop - tx.send(BackgroundEvent::UsbStateChanged(usb_state)) - .await - .unwrap(); - } - }); + let mut cred_service = self.credential_service.lock().await; + (*cred_service).get_usb_credential().await.unwrap(); } Transport::HybridQr => { - let tx = self.bg_update.clone(); - let cred_service = self.credential_service.clone(); - let mut stream = cred_service.lock().await.get_hybrid_credential().await; - async_std::task::spawn(async move { - while let Some(state) = stream.next().await { - // forward to background event loop - tx.send(BackgroundEvent::HybridQrStateChanged(state.into())) - .await - .unwrap(); - } - tracing::debug!("Broke out of hybrid QR state stream"); - }); + let mut cred_service = self.credential_service.lock().await; + cred_service.get_hybrid_credential().await.unwrap(); } _ => { todo!() @@ -160,26 +137,25 @@ impl ViewModel { pub(crate) async fn start_event_loop(&mut self) { let view_events = self.rx_event.clone().map(Event::View); - let bg_events = self.bg_event.clone().map(Event::Background); - let mut all_events = view_events.merge(bg_events); + let bg_events = { + let mut cred_service = self.credential_service.lock().await; + cred_service.initiate_event_stream().await.unwrap() + }; + let mut all_events = view_events.merge(bg_events.map(Event::Background)); while let Some(event) = all_events.next().await { match event { Event::View(ViewEvent::Initiated) => { self.update_title().await; self.update_devices().await; } - Event::View(ViewEvent::ButtonClicked) => { - println!("Got it!") - } Event::View(ViewEvent::DeviceSelected(id)) => { self.select_device(&id).await; println!("Selected device {id}"); } Event::View(ViewEvent::UsbPinEntered(pin)) => { - if let Some(pin_tx) = self.usb_pin_tx.take() { - if pin_tx.lock().await.send(pin).await.is_err() { - error!("Failed to send pin to device"); - } + let mut cred_service = self.credential_service.lock().await; + if cred_service.enter_client_pin(pin).await.is_err() { + error!("Failed to send pin to device"); } } Event::View(ViewEvent::CredentialSelected(cred_id)) => { @@ -188,10 +164,19 @@ impl ViewModel { cred_id, self.selected_device ); - if let Some(cred_tx) = self.usb_cred_tx.take() { - if cred_tx.lock().await.send(cred_id.clone()).await.is_err() { - error!("Failed to send selected credential to device"); - } + if let Err(_) = self + .credential_service + .lock() + .await + .select_credential(cred_id) + .await + { + let error_msg = "Failed to select credential from device."; + tracing::error!(error_msg); + self.tx_update + .send(ViewUpdate::Failed(error_msg.to_string())) + .await + .unwrap(); } } @@ -201,11 +186,7 @@ impl ViewModel { info!("Found USB device") } - UsbState::NeedsPin { - attempts_left, - pin_tx, - } => { - let _ = self.usb_pin_tx.insert(Arc::new(Mutex::new(pin_tx))); + UsbState::NeedsPin { attempts_left } => { self.tx_update .send(ViewUpdate::UsbNeedsPin { attempts_left }) .await @@ -233,8 +214,7 @@ impl ViewModel { .unwrap(); } UsbState::Idle | UsbState::Waiting => {} - UsbState::SelectCredential { creds, cred_tx } => { - let _ = self.usb_cred_tx.insert(Arc::new(Mutex::new(cred_tx))); + UsbState::SelectCredential { creds } => { self.tx_update .send(ViewUpdate::SetCredentials(creds)) .await @@ -243,9 +223,15 @@ impl ViewModel { // TODO: Provide more specific error messages using the wrapped Error. UsbState::Failed(err) => { let error_msg = String::from(match err { - CredentialServiceError::NoCredentials => "No matching credentials found on this authenticator.", - CredentialServiceError::PinAttemptsExhausted => "No more PIN attempts allowed. Try removing your device and plugging it back in.", - CredentialServiceError::AuthenticatorError | CredentialServiceError::Internal(_) => "Something went wrong while retrieving a credential. Please try again later or use a different authenticator.", + Error::NoCredentials => { + "No matching credentials found on this authenticator." + } + Error::PinAttemptsExhausted => { + "No more PIN attempts allowed. Try removing your device and plugging it back in." + } + Error::AuthenticatorError | Error::Internal(_) => { + "Something went wrong while retrieving a credential. Please try again later or use a different authenticator." + } }); self.tx_update .send(ViewUpdate::Failed(error_msg)) @@ -300,173 +286,15 @@ impl ViewModel { } } +#[derive(Serialize, Deserialize)] pub enum ViewEvent { Initiated, - ButtonClicked, DeviceSelected(String), CredentialSelected(String), UsbPinEntered(String), } -pub enum ViewUpdate { - SetTitle(String), - SetDevices(Vec), - SetCredentials(Vec), - - WaitingForDevice(Device), - SelectingDevice, - - UsbNeedsPin { attempts_left: Option }, - UsbNeedsUserVerification { attempts_left: Option }, - UsbNeedsUserPresence, - - HybridNeedsQrCode(String), - HybridConnecting, - HybridConnected, - - Completed, - Failed(String), -} - -pub enum BackgroundEvent { - UsbStateChanged(UsbState), - HybridQrStateChanged(HybridState), -} - pub enum Event { Background(BackgroundEvent), View(ViewEvent), } - -#[derive(Clone, Debug, Default)] -pub struct Credential { - pub(crate) id: String, - pub(crate) name: String, - pub(crate) username: Option, -} - -#[derive(Debug, Default)] -pub enum FingerprintSensorState { - #[default] - Idle, -} - -#[derive(Debug)] -pub enum CredentialType { - Passkey, - // Password, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct Device { - pub id: String, - pub transport: Transport, -} - -#[derive(Clone, Debug, Default)] -pub enum HybridState { - /// Default state, not listening for hybrid transport. - #[default] - Idle, - - /// QR code flow is starting, awaiting QR code scan and BLE advert from phone. - Started(String), - - /// BLE advert received, connecting to caBLE tunnel with shared secret. - Connecting, - - /// Connected to device via caBLE tunnel. - Connected, - - /// Credential received over tunnel. - Completed, - - // This isn't actually sent from the server. - UserCancelled, - - /// Failed to receive a credential - Failed, -} - -impl From for HybridState { - fn from(value: crate::credential_service::hybrid::HybridState) -> Self { - match value { - crate::credential_service::hybrid::HybridState::Init(qr_code) => { - HybridState::Started(qr_code) - } - crate::credential_service::hybrid::HybridState::Connecting => HybridState::Connecting, - crate::credential_service::hybrid::HybridState::Connected => HybridState::Connected, - crate::credential_service::hybrid::HybridState::Completed => HybridState::Completed, - crate::credential_service::hybrid::HybridState::UserCancelled => { - HybridState::UserCancelled - } - crate::credential_service::hybrid::HybridState::Failed => HybridState::Failed, - } - } -} - -#[derive(Debug)] -pub enum Operation { - Create { cred_type: CredentialType }, - Get { cred_types: Vec }, -} - -#[derive(Debug, Default)] -pub struct Provider; - -#[derive(Clone, Debug, PartialEq)] -pub enum Transport { - Ble, - HybridLinked, - HybridQr, - Internal, - Nfc, - Usb, -} - -impl TryInto for String { - type Error = String; - - fn try_into(self) -> Result { - let value: &str = self.as_ref(); - value.try_into() - } -} - -impl TryInto for &str { - type Error = String; - - fn try_into(self) -> Result { - match self { - "BLE" => Ok(Transport::Ble), - "HybridLinked" => Ok(Transport::HybridLinked), - "HybridQr" => Ok(Transport::HybridQr), - "Internal" => Ok(Transport::Internal), - "NFC" => Ok(Transport::Nfc), - "USB" => Ok(Transport::Usb), - _ => Err(format!("Unrecognized transport: {}", self.to_owned())), - } - } -} - -impl From for String { - fn from(val: Transport) -> Self { - val.as_str().to_string() - } -} - -impl Transport { - fn as_str(&self) -> &'static str { - match self { - Transport::Ble => "BLE", - Transport::HybridLinked => "HybridLinked", - Transport::HybridQr => "HybridQr", - Transport::Internal => "Internal", - Transport::Nfc => "NFC", - Transport::Usb => "USB", - } - } -} - -#[derive(Debug, Default)] -pub struct UserVerificationMethod; diff --git a/creds-ui/src/main.rs b/creds-ui/src/main.rs new file mode 100644 index 00000000..6d7cea20 --- /dev/null +++ b/creds-ui/src/main.rs @@ -0,0 +1,48 @@ +mod client; +#[rustfmt::skip] +mod config; +mod dbus; +mod gui; + +use std::error::Error; + +use crate::{client::DbusCredentialClient, dbus::UiControlService}; + +fn main() -> Result<(), Box> { + tracing_subscriber::fmt::init(); + tracing::debug!("Starting credentials UI service"); + async_std::task::block_on(run()) +} + +async fn run() -> Result<(), Box> { + print!("Starting GUI thread...\t"); + let (request_tx, request_rx) = async_std::channel::bounded(2); + // this allows the D-Bus service to signal to the GUI to draw a window for + // executing the credential flow. + let conn = zbus::connection::Builder::session()? + .name("xyz.iinuwa.credentials.UiControl")? + .build() + .await?; + let cred_client = DbusCredentialClient::new(conn); + let _handle = gui::start_gui_thread(request_rx, cred_client)?; + println!(" ✅"); + + print!("Starting UI Control listener...\t"); + let interface = UiControlService { request_tx }; + let path = "/xyz/iinuwa/credentials/UiControl"; + let service = "xyz.iinuwa.credentials.UiControl"; + let _server_conn = zbus::connection::Builder::session()? + .name(service)? + .serve_at(path, interface)? + .build() + .await?; + println!(" ✅"); + loop { + std::future::pending::<()>().await; + } + #[allow(unreachable_code)] + { + _ = _handle.join(); + Ok(()) + } +} diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/meson.build b/creds-ui/src/meson.build similarity index 55% rename from xyz-iinuwa-credential-manager-portal-gtk/src/meson.build rename to creds-ui/src/meson.build index b9bb0a5c..c2cc648d 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/src/meson.build +++ b/creds-ui/src/meson.build @@ -1,7 +1,10 @@ global_conf = configuration_data() global_conf.set_quoted('APP_ID', application_id) if (get_option('profile') == 'development') - global_conf.set_quoted('PKGDATADIR', meson.project_build_root() / backend_executable_name / 'data' / 'resources') + global_conf.set_quoted( + 'PKGDATADIR', + meson.project_build_root() / gui_build_dir / 'data' / 'resources', + ) else global_conf.set_quoted('PKGDATADIR', pkgdatadir) endif @@ -9,25 +12,23 @@ global_conf.set_quoted('PROFILE', profile) global_conf.set_quoted('VERSION', version + version_suffix) global_conf.set_quoted('GETTEXT_PACKAGE', gettext_package) global_conf.set_quoted('LOCALEDIR', localedir) -configure_file( - input: 'config.rs.in', - output: 'config.rs', - configuration: global_conf -) +configure_file(input: 'config.rs.in', output: 'config.rs', configuration: global_conf) # Copy the config.rs output to the source directory. run_command( 'cp', - meson.project_build_root() / backend_executable_name / 'src' / 'config.rs', - meson.project_source_root() / backend_executable_name / 'src' / 'config.rs', - check: true + meson.project_build_root() / meson.current_build_dir() / 'config.rs', + meson.project_source_root() / meson.current_source_dir() / 'config.rs', + check: true, ) -cargo_options = [ '--manifest-path', meson.project_source_root() / backend_executable_name / 'Cargo.toml' ] -cargo_options += [ '--target-dir', meson.project_build_root() / backend_executable_name / 'src' ] +cargo_options = [ + '--manifest-path', meson.project_source_root() / gui_source_dir / 'Cargo.toml', +] +cargo_options += ['--target-dir', meson.project_build_root() / gui_build_dir / 'target'] if get_option('profile') == 'default' - cargo_options += [ '--release' ] + cargo_options += ['--release'] rust_target = 'release' message('Building in release mode') else @@ -35,13 +36,13 @@ else message('Building in debug mode') endif -cargo_env = [ 'CARGO_HOME=' + meson.project_build_root() / 'cargo-home' ] +cargo_env = ['CARGO_HOME=' + meson.project_build_root() / 'cargo-home'] custom_target( 'cargo-build', build_by_default: true, build_always_stale: true, - output: backend_executable_name, + output: gui_executable_name, console: true, install: true, install_dir: bindir, @@ -49,11 +50,14 @@ custom_target( command: [ 'env', cargo_env, - cargo, 'build', + cargo, + 'build', cargo_options, '&&', - 'cp', backend_executable_name / 'src' / rust_target / backend_executable_name, '@OUTPUT@', - ] + 'cp', + gui_build_dir / 'target' / rust_target / gui_executable_name, + '@OUTPUT@', + ], ) test( diff --git a/credsd/.gitignore b/credsd/.gitignore new file mode 100644 index 00000000..6e5196c1 --- /dev/null +++ b/credsd/.gitignore @@ -0,0 +1 @@ +tests/config/mod.rs diff --git a/credsd/Cargo.lock b/credsd/Cargo.lock new file mode 100644 index 00000000..b16ab393 --- /dev/null +++ b/credsd/Cargo.lock @@ -0,0 +1,3720 @@ +# This file is automatically @generated by Cargo. +# 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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "asn1-rs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 2.0.12", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.0.8", + "slab", + "windows-sys 0.60.2", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 1.0.8", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-signal" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.0.8", + "signal-hook-registry", + "slab", + "windows-sys 0.60.2", +] + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "aws-lc-rs" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64-url" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2b6c78c06f7288d5e3c3d683bde35a79531127c83b087e5d0d77c974b4b28" +dependencies = [ + "base64", +] + +[[package]] +name = "base64ct" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.9.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bluez-async" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353dc0fbd494ab1d066ffdff16f07acbea46ca63f507e093c07fdf2408d84300" +dependencies = [ + "bitflags 2.9.0", + "bluez-generated", + "dbus", + "dbus-tokio", + "futures", + "itertools 0.13.0", + "log", + "serde", + "serde-xml-rs", + "thiserror 2.0.12", + "tokio", + "uuid", +] + +[[package]] +name = "bluez-generated" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9676783265eadd6f11829982792c6f303f3854d014edfba384685dcf237dd062" +dependencies = [ + "dbus", +] + +[[package]] +name = "btleplug" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b668804e0728a09c83cd9b94c9e1176717ea5522e8a3cb3688c2ac9a5f6e137c" +dependencies = [ + "async-trait", + "bitflags 2.9.0", + "bluez-async", + "dashmap 6.1.0", + "dbus", + "futures", + "jni", + "jni-utils", + "log", + "objc2", + "objc2-core-bluetooth", + "objc2-foundation", + "once_cell", + "static_assertions", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "uuid", + "windows", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cbor-smol" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087b31faa4ad4ba21c9bd0209204eef424dae6424195aafc7242006b69fc8d" +dependencies = [ + "delog", + "heapless", + "heapless-bytes", + "serde", +] + +[[package]] +name = "cc" +version = "1.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-expr" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d0390889d58f934f01cd49736275b4c2da15bcfc328c78ff2349907e6cabf22" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cosey" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75494895fa1a9713ca725ddf2db084ee84fb0c20938fdd7c89293febe732d30a" +dependencies = [ + "heapless-bytes", + "serde", + "serde_repr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "creds-lib" +version = "0.1.0" +dependencies = [ + "futures-lite", + "libwebauthn", + "serde", + "zbus", +] + +[[package]] +name = "credsd" +version = "0.1.0" +dependencies = [ + "async-stream", + "async-trait", + "base64", + "cosey", + "creds-lib", + "futures-lite", + "gio", + "libwebauthn", + "openssl", + "ring", + "rustls", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", + "zbus", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctap-types" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb8b105c5e728afd373e99874f0c1911c170b3a56848456cc16feb4506321606" +dependencies = [ + "bitflags 1.3.2", + "cbor-smol", + "cosey", + "delog", + "heapless", + "heapless-bytes", + "iso7816", + "serde", + "serde-indexed 0.1.1", + "serde_bytes", + "serde_repr", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "futures-channel", + "futures-util", + "libc", + "libdbus-sys", + "winapi", +] + +[[package]] +name = "dbus-tokio" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "007688d459bc677131c063a3a77fb899526e17b7980f390b69644bdbc41fad13" +dependencies = [ + "dbus", + "libc", + "tokio", +] + +[[package]] +name = "delog" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af2b93368262340c9d4441251b824500d1b641a50957ecf4219a2cc41b9eac8f" +dependencies = [ + "log", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "serdect", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "fragile" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "gio" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "273d64c833fbbf7cd86c4cdced893c5d3f2f5d6aeb30fd0c30d172456ce8be2e" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "pin-project-lite", + "smallvec", +] + +[[package]] +name = "gio-sys" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8130f5810a839d74afc3a929c34a700bf194972bb034f2ecfe639682dd13cc" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "windows-sys 0.59.0", +] + +[[package]] +name = "glib" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "690e8bcf8a819b5911d6ae79879226191d01253a4f602748072603defd5b9553" +dependencies = [ + "bitflags 2.9.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "smallvec", +] + +[[package]] +name = "glib-macros" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e772291ebea14c28eb11bb75741f62f4a4894f25e60ce80100797b6b010ef0f9" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2be4c74454fb4a6bd3328320737d0fa3d6939e2d570f5d846da00cb222f6a0" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "gobject-sys" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab318a786f9abd49d388013b9161fa0ef8218ea6118ee7111c95e62186f7d31f" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "heapless-bytes" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7285eba272c6af3e9f15fb9e1c1b6e7d35aa70580ffe0d47af017e97dfb6f48b" +dependencies = [ + "heapless", + "serde", + "typenum", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hidapi" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b876ecf37e86b359573c16c8366bc3eba52b689884a0fc42ba3f67203d2a8b" +dependencies = [ + "cc", + "cfg-if", + "libc", + "pkg-config", + "windows-sys 0.48.0", +] + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "iso7816" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3c7e91da489667bb054f9cd2f1c60cc2ac4478a899f403d11dbc62189215b0" +dependencies = [ + "heapless", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jni-utils" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "259e9f2c3ead61de911f147000660511f07ab00adeed1d84f5ac4d0386e7a6c4" +dependencies = [ + "dashmap 5.5.3", + "futures", + "jni", + "log", + "once_cell", + "static_assertions", + "uuid", +] + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.2", + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] +name = "libwebauthn" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56ed4eb5e9a63098f7eeaf0fe0fc7b63c84977e8b8253ebe3707b124b6d8036" +dependencies = [ + "aes", + "async-trait", + "base64-url", + "bitflags 2.9.0", + "btleplug", + "byteorder", + "cbc", + "cosey", + "ctap-types", + "curve25519-dalek", + "dbus", + "futures", + "heapless", + "hex", + "hidapi", + "hkdf", + "hmac", + "maplit", + "mockall", + "num-derive", + "num-traits", + "num_enum", + "p256", + "rand 0.8.5", + "rustls", + "serde", + "serde-indexed 0.2.0", + "serde_bytes", + "serde_cbor_2", + "serde_derive", + "serde_repr", + "sha2", + "snow", + "text_io", + "thiserror 2.0.12", + "time", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tracing", + "tungstenite", + "uuid", + "x509-parser", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "mockall" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.9.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-core-bluetooth" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a644b62ffb826a5277f536cf0f701493de420b13d40e700c452c36567771111" +dependencies = [ + "bitflags 2.9.0", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.9.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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +dependencies = [ + "bitflags 2.9.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "serdect", + "sha2", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "polling" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.0.8", + "windows-sys 0.60.2", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettyplease" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", + "serdect", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.9.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-indexed" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca2da10b1f1623f47130256065e05e94fd7a98dbd26a780a4c5de831b21e5c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde-indexed" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68cf7478db8b81abcf71b6d195a34a4891bd3d39868731c4d73194d74ec7a3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde-xml-rs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782" +dependencies = [ + "log", + "serde", + "thiserror 1.0.69", + "xml-rs", +] + +[[package]] +name = "serde_bytes" +version = "0.11.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_cbor_2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aec2709de9078e077090abd848e967abab63c9fb3fdb5d4799ad359d8d482c" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "snow" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "599b506ccc4aff8cf7844bc42cf783009a434c1e26c964432560fb6d6ad02d82" +dependencies = [ + "aes-gcm", + "blake2", + "chacha20poly1305", + "curve25519-dalek", + "getrandom 0.3.2", + "p256", + "ring", + "rustc_version", + "sha2", + "subtle", +] + +[[package]] +name = "socket2" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-deps" +version = "7.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" + +[[package]] +name = "tempfile" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +dependencies = [ + "fastrand", + "getrandom 0.3.2", + "once_cell", + "rustix 1.0.8", + "windows-sys 0.59.0", +] + +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + +[[package]] +name = "text_io" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d8d3ca3b06292094e03841d8995e910712d2a10b5869c8f9725385b29761115" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tokio" +version = "1.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.1", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror 2.0.12", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +dependencies = [ + "getrandom 0.3.2", + "serde", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "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", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "x509-parser" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror 2.0.12", + "time", +] + +[[package]] +name = "xml-rs" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" + +[[package]] +name = "zbus" +version = "5.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb4f9a464286d42851d18a605f7193b8febaf5b0919d71c6399b7b26e5b0aad" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "nix", + "ordered-stream", + "serde", + "serde_repr", + "tokio", + "tracing", + "uds_windows", + "windows-sys 0.59.0", + "winnow", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef9859f68ee0c4ee2e8cde84737c78e3f4c54f946f2a38645d0d4c7a95327659" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +dependencies = [ + "serde", + "static_assertions", + "winnow", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zvariant" +version = "5.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91b3680bb339216abd84714172b5138a4edac677e641ef17e1d8cb1b3ca6e6f" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8c68501be459a8dbfffbe5d792acdd23b4959940fc87785fb013b32edbc208" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "static_assertions", + "syn", + "winnow", +] diff --git a/xyz-iinuwa-credential-manager-portal-gtk/Cargo.toml b/credsd/Cargo.toml similarity index 60% rename from xyz-iinuwa-credential-manager-portal-gtk/Cargo.toml rename to credsd/Cargo.toml index c9645335..39962f03 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/Cargo.toml +++ b/credsd/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "xyz-iinuwa-credential-manager-portal-gtk" +name = "credsd" version = "0.1.0" authors = ["Isaiah Inuwa "] edition = "2021" @@ -8,10 +8,8 @@ edition = "2021" lto = true [dependencies] -async-std = { version = "1.13.1", features = ["unstable"] } base64 = "0.22.1" -gettext-rs = { version = "0.7", features = ["gettext-system"] } -gtk = { version = "0.9.6", package = "gtk4", features = ["v4_6"] } +creds-lib = { path = "../creds-lib" } openssl = "0.10.72" ring = "0.17.14" serde = { version = "1.0.219", features = ["derive"] } @@ -19,14 +17,16 @@ serde_json = "1.0.140" # serde_cbor = "0.11.1" tracing = "0.1.41" tracing-subscriber = "0.3" -zbus = { version = "5.5.0", default-features = false, features = ["blocking-api", "tokio"] } -libwebauthn = { git = "https://github.com/linux-credentials/libwebauthn", rev = "34f8a59cb1634175b8baf866e6d30d1869f5a221" } +zbus = { version = "5.9.0", default-features = false, features = ["tokio"] } +libwebauthn = "~0.2.2" async-trait = "0.1.88" tokio = { version = "1.45.0", features = ["rt-multi-thread"] } futures-lite = "2.6.0" -qrcode = "0.14.1" # this is temporary until we move COSE -> Vec serialization methods into libwebauthn cosey = "0.3.2" rustls = { version = "0.23.27", default-features = false, features = ["std", "tls12", "ring", "log", "logging", "prefer-post-quantum"] } async-stream = "0.3.6" + +[dev-dependencies] +gio = "0.21.0" diff --git a/credsd/meson.build b/credsd/meson.build new file mode 100644 index 00000000..7e2b9329 --- /dev/null +++ b/credsd/meson.build @@ -0,0 +1,53 @@ +backend_executable_name = 'credsd' +backend_build_dir = meson.current_build_dir() +base_id = 'xyz.iinuwa.CredentialManagerUi' + +dependency('dbus-1', version: '>= 1.6') +dependency('glib-2.0', version: '>= 2.66') +dependency('gio-2.0', version: '>= 2.66') +dependency('gtk4', version: '>= 4.6.2') +dependency('ssl', 'openssl', version: '>= 3.0') +dependency('udev', version: '>= 249') + +cargo = find_program('cargo', required: true) + +version = meson.project_version() + +if get_option('profile') == 'development' + profile = 'Devel' + vcs_tag = run_command('git', 'rev-parse', '--short', 'HEAD', check: false).stdout().strip() + if vcs_tag == '' + version_suffix = '-devel' + else + version_suffix = '-@0@'.format(vcs_tag) + endif + application_id = '@0@.@1@'.format(base_id, profile) +else + profile = '' + version_suffix = '' + application_id = base_id +endif + +meson.add_dist_script( + meson.project_source_root() / 'build-aux/dist-vendor.sh', + meson.project_build_root() / 'meson-dist' / backend_executable_name + + '-' + + version, + meson.project_source_root(), +) + +if get_option('profile') == 'development' + # Setup pre-commit hook for ensuring coding style is always consistent + message('Setting up git pre-commit hook..') + run_command('cp', '-f', 'hooks/pre-commit.hook', '.git/hooks/pre-commit', check: false) +endif + +cargo_options = [ + '--manifest-path', meson.project_source_root() / meson.current_source_dir() / 'Cargo.toml', +] +cargo_options += [ + '--target-dir', meson.project_build_root() / meson.current_build_dir() / 'target', +] + +subdir('src') +subdir('tests') diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/cbor.rs b/credsd/src/cbor.rs similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/src/cbor.rs rename to credsd/src/cbor.rs diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/cose.rs b/credsd/src/cose.rs similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/src/cose.rs rename to credsd/src/cose.rs diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/credential_service/hybrid.rs b/credsd/src/credential_service/hybrid.rs similarity index 94% rename from xyz-iinuwa-credential-manager-portal-gtk/src/credential_service/hybrid.rs rename to credsd/src/credential_service/hybrid.rs index 8c8a24a2..e239424b 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/src/credential_service/hybrid.rs +++ b/credsd/src/credential_service/hybrid.rs @@ -1,11 +1,10 @@ use core::panic; use std::fmt::Debug; -use crate::dbus::CredentialRequest; use async_stream::stream; use futures_lite::Stream; use tokio::sync::broadcast; -use tokio::sync::mpsc::{self, Receiver, Sender}; +use tokio::sync::mpsc::{self, Sender}; use tracing::{debug, error}; use libwebauthn::transport::cable::channel::{CableUpdate, CableUxUpdate}; @@ -13,7 +12,9 @@ use libwebauthn::transport::cable::qr_code_device::{CableQrCodeDevice, QrCodeOpe use libwebauthn::transport::{Channel, Device}; use libwebauthn::webauthn::{Error as WebAuthnError, WebAuthn}; -use super::{AuthenticatorResponse, Error}; +use creds_lib::model::{CredentialRequest, Error}; + +use super::AuthenticatorResponse; pub(crate) trait HybridHandler { fn start( @@ -204,6 +205,19 @@ impl From for HybridState { } } +impl From for creds_lib::model::HybridState { + fn from(value: HybridState) -> Self { + match value { + HybridState::Init(qr_code) => creds_lib::model::HybridState::Started(qr_code), + HybridState::Connecting => creds_lib::model::HybridState::Connecting, + HybridState::Connected => creds_lib::model::HybridState::Connected, + HybridState::Completed => creds_lib::model::HybridState::Completed, + HybridState::UserCancelled => creds_lib::model::HybridState::UserCancelled, + HybridState::Failed => creds_lib::model::HybridState::Failed, + } + } +} + async fn handle_hybrid_updates( state_sender: &Sender, mut ux_update_receiver: broadcast::Receiver, @@ -248,7 +262,7 @@ pub(super) mod test { proto::ctap2::{Ctap2PublicKeyCredentialDescriptor, Ctap2Transport}, }; - use crate::dbus::CredentialRequest; + use creds_lib::model::CredentialRequest; use super::{HybridEvent, HybridHandler, HybridStateInternal}; #[derive(Debug)] diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/credential_service/mod.rs b/credsd/src/credential_service/mod.rs similarity index 59% rename from xyz-iinuwa-credential-manager-portal-gtk/src/credential_service/mod.rs rename to credsd/src/credential_service/mod.rs index 1ae7ffe6..fbb3cc9b 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/src/credential_service/mod.rs +++ b/credsd/src/credential_service/mod.rs @@ -14,38 +14,47 @@ use libwebauthn::{ self, ops::webauthn::{GetAssertionResponse, MakeCredentialResponse}, }; - -use crate::{ - credential_service::{hybrid::HybridEvent, usb::UsbEvent}, - dbus::{CredentialRequest, CredentialResponse}, - gui::view_model::{Device, Transport}, +use tokio::sync::oneshot::Sender; + +use creds_lib::{ + model::{ + CredentialRequest, CredentialResponse, Device, Error as CredentialServiceError, Operation, + Transport, + }, + server::ViewRequest, }; +use crate::credential_service::{hybrid::HybridEvent, usb::UsbEvent}; + use hybrid::{HybridHandler, HybridState, HybridStateInternal}; use usb::{UsbHandler, UsbStateInternal}; pub use { - server::{CredentialManagementClient, CredentialServiceClient, InProcessServer}, + server::{CredentialManagementClient, UiController}, usb::UsbState, }; +type RequestContext = ( + CredentialRequest, + Sender>, +); + #[derive(Debug)] -pub struct CredentialService { +pub struct CredentialService { devices: Vec, - cred_request: Mutex>, - // Place to store data to be returned to the caller - cred_response: Arc>>, + /// Current request and channel to respond to caller. + ctx: Arc>>, hybrid_handler: H, usb_handler: U, + + ui_control_client: UC, } -impl CredentialService -where -// ::Stream: Unpin + Send + Sized + 'static, -// ::Stream: Unpin + Send + Sized + 'static, +impl + CredentialService { - pub fn new(hybrid_handler: H, usb_handler: U) -> Self { + pub fn new(hybrid_handler: H, usb_handler: U, ui_control_client: UC) -> Self { let devices = vec![ Device { id: String::from("0"), @@ -59,59 +68,103 @@ where Self { devices, - cred_request: Mutex::new(None), - cred_response: Arc::new(Mutex::new(None)), + ctx: Arc::new(Mutex::new(None)), hybrid_handler, usb_handler, + + ui_control_client, } } - pub fn init_request(&self, request: &CredentialRequest) -> Result<(), String> { - let mut cred_request = self.cred_request.lock().unwrap(); - if cred_request.is_some() { - Err("Already a request in progress.".to_string()) - } else { - _ = cred_request.insert(request.clone()); - Ok(()) + pub async fn init_request( + &self, + request: &CredentialRequest, + tx: Sender>, + ) { + { + let mut cred_request = self.ctx.lock().unwrap(); + if cred_request.is_some() { + tx.send(Err(CredentialServiceError::Internal( + "Already a request in progress.".to_string(), + ))) + .expect("Send to local receiver to succeed"); + return; + } else { + _ = cred_request.insert((request.clone(), tx)); + } + }; + let operation = match &request { + CredentialRequest::CreatePublicKeyCredentialRequest(_) => Operation::Create, + CredentialRequest::GetPublicKeyCredentialRequest(_) => Operation::Get, + }; + let view_request = ViewRequest { operation }; + + let launch_ui_response = self + .ui_control_client + .launch_ui(view_request) + .await + .map_err(|err| err.to_string()); + if let Err(err) = launch_ui_response { + tracing::error!("Failed to launch UI for credentials: {err}. Cancelling request."); + _ = self.ctx.lock().unwrap().take(); + let err = Err(CredentialServiceError::Internal(err)); + let (_, tx) = self.ctx.lock().unwrap().take().unwrap(); + tx.send(err); } } - async fn get_available_public_key_devices(&self) -> Result, ()> { + pub async fn get_available_public_key_devices(&self) -> Result, ()> { Ok(self.devices.to_owned()) } - fn get_hybrid_credential(&self) -> Pin + Send + 'static>> { - let guard = self.cred_request.lock().unwrap(); - let cred_request = guard.clone().unwrap(); - let stream = self.hybrid_handler.start(&cred_request); - let cred_response = self.cred_response.clone(); - Box::pin(HybridStateStream { - inner: stream, - cred_response, - }) + pub fn get_hybrid_credential( + &self, + ) -> Pin + Send + 'static>> { + let guard = self.ctx.lock().unwrap(); + if let Some((ref cred_request, _)) = *guard { + let stream = self.hybrid_handler.start(&cred_request); + let ctx = self.ctx.clone(); + Box::pin(HybridStateStream { inner: stream, ctx }) + } else { + tracing::error!( + "Attempted to start hybrid credential flow, but no request context was found." + ); + todo!("Handle error when context is not set up.") + } } - fn get_usb_credential(&self) -> Pin + Send + 'static>> { - let guard = self.cred_request.lock().unwrap(); - let cred_request = guard.clone().unwrap(); - let stream = self.usb_handler.start(&cred_request); - Box::pin(UsbStateStream { - inner: stream, - cred_response: self.cred_response.clone(), - }) + pub fn get_usb_credential(&self) -> Pin + Send + 'static>> { + let guard = self.ctx.lock().unwrap(); + if let Some((ref cred_request, _)) = *guard { + let stream = self.usb_handler.start(&cred_request); + let ctx = self.ctx.clone(); + Box::pin(UsbStateStream { inner: stream, ctx }) + } else { + tracing::error!( + "Attempted to start hybrid credential flow, but no request context was found." + ); + todo!("Handle error when context is not set up.") + } } - pub fn complete_auth(&self) -> Option { - self.cred_request.lock().unwrap().take(); - let mut cred_response = self.cred_response.lock().unwrap(); - cred_response.take() + pub async fn complete_auth( + &self, + response: Result, + ) -> () { + if let Some((_request, responder)) = self.ctx.lock().unwrap().take() { + if responder.send(response).is_err() { + tracing::error!("Failed to send response to back to caller"); + }; + } else { + tracing::error!("No corresponding request for this"); + } } } pub struct HybridStateStream { inner: H, - cred_response: Arc>>, + ctx: Arc>>, } impl Stream for HybridStateStream @@ -124,7 +177,7 @@ where self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> Poll> { - let cred_response = &self.cred_response.clone(); + let ctx = &self.ctx.clone(); match Box::pin(Box::pin(self).as_mut().inner.next()).poll(cx) { Poll::Pending => Poll::Pending, Poll::Ready(Some(HybridEvent { state })) => { @@ -148,8 +201,7 @@ where ) } }; - let mut cred_response = cred_response.lock().unwrap(); - cred_response.replace(response); + complete_request(ctx, response.clone()); } Poll::Ready(Some(state.into())) } @@ -160,7 +212,7 @@ where struct UsbStateStream { inner: H, - cred_response: Arc>>, + ctx: Arc>>, } impl Stream for UsbStateStream @@ -173,13 +225,12 @@ where self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> Poll> { - let cred_response = &self.cred_response.clone(); + let ctx = &self.ctx.clone(); match Box::pin(Box::pin(self).as_mut().inner.next()).poll(cx) { Poll::Pending => Poll::Pending, Poll::Ready(Some(UsbEvent { state })) => { if let UsbStateInternal::Completed(response) = &state { - let mut cred_response = cred_response.lock().unwrap(); - cred_response.replace(response.clone()); + complete_request(ctx, response.clone()); } Poll::Ready(Some(state.into())) } @@ -188,29 +239,24 @@ where } } +fn complete_request(ctx: &Mutex>, response: CredentialResponse) { + if let Some((_, responder)) = ctx.lock().unwrap().take() { + if responder.send(Ok(response)).is_err() { + tracing::error!( + "Attempted to send credential response to caller, but channel was closed." + ); + } + } else { + tracing::error!("Tried to consume context to respond to caller, but none was found.") + } +} + #[derive(Debug, Clone)] enum AuthenticatorResponse { CredentialCreated(MakeCredentialResponse), CredentialsAsserted(GetAssertionResponse), } -#[derive(Debug, Clone)] -pub enum Error { - /// Some unknown error with the authenticator occurred. - AuthenticatorError, - /// No matching credentials were found on the device. - NoCredentials, - /// Too many incorrect PIN attempts, and authenticator must be removed and - /// reinserted to continue any more PIN attempts. - /// - /// Note that this is different than exhausting the PIN count that fully - /// locks out the device. - PinAttemptsExhausted, - // TODO: We may want to hide the details on this variant from the public API. - /// Something went wrong with the credential service itself, not the authenticator. - Internal(String), -} - impl From for AuthenticatorResponse { fn from(value: MakeCredentialResponse) -> Self { Self::CredentialCreated(value) @@ -227,11 +273,12 @@ impl From for AuthenticatorResponse { mod test { use std::sync::Arc; - use async_std::stream::StreamExt; + use futures_lite::stream::StreamExt; - use crate::{ - credential_service::usb::InProcessUsbHandler, - dbus::{CreateCredentialRequest, CreatePublicKeyCredentialRequest, CredentialRequest}, + use crate::credential_service::usb::InProcessUsbHandler; + use creds_lib::{ + model::CredentialRequest, + server::{CreateCredentialRequest, CreatePublicKeyCredentialRequest}, }; use super::{ @@ -239,6 +286,7 @@ mod test { AuthenticatorResponse, CredentialService, }; + /* #[test] fn test_hybrid_sets_credential() { let request = create_credential_request(); @@ -254,7 +302,10 @@ mod test { let cred_service = Arc::new(CredentialService::new(hybrid_handler, usb_handler)); cred_service.init_request(&request).unwrap(); let mut stream = cred_service.get_hybrid_credential(); - async_std::task::block_on(async move { while let Some(_) = stream.next().await {} }); + tokio::runtime::Builder::new_current_thread() + .build() + .unwrap() + .block_on(async move { while let Some(_) = stream.next().await {} }); let cred_service = Arc::try_unwrap(cred_service).unwrap(); assert!(cred_service.complete_auth().is_some()); } @@ -299,16 +350,19 @@ mod test { "credProps": true } }"#.to_string(); - let (req, _) = CreateCredentialRequest { - origin: Some("webauthn.io".to_string()), - is_same_origin: Some(true), - r#type: "public-key".to_string(), - public_key: Some(CreatePublicKeyCredentialRequest { request_json }), - } - .try_into_ctap2_request() + + let (req, _) = crate::dbus::model::create_credential_request_try_into_ctap2( + &CreateCredentialRequest { + origin: Some("webauthn.io".to_string()), + is_same_origin: Some(true), + r#type: "public-key".to_string(), + public_key: Some(CreatePublicKeyCredentialRequest { request_json }), + }, + ) .unwrap(); CredentialRequest::CreatePublicKeyCredentialRequest(req) } + */ fn create_authenticator_response() -> AuthenticatorResponse { use libwebauthn::{ diff --git a/credsd/src/credential_service/server.rs b/credsd/src/credential_service/server.rs new file mode 100644 index 00000000..0623b34a --- /dev/null +++ b/credsd/src/credential_service/server.rs @@ -0,0 +1,561 @@ +use std::error::Error; +use std::fmt::Debug; +use std::future::Future; +use std::pin::Pin; +use std::sync::{Arc, Mutex}; + +use creds_lib::client::CredentialServiceClient; +use creds_lib::server::ViewRequest; +use futures_lite::{Stream, StreamExt}; +use tokio::sync::mpsc::Receiver; +use tokio::sync::{mpsc, oneshot, Mutex as AsyncMutex}; + +use creds_lib::model::{BackgroundEvent, CredentialRequest, CredentialResponse, Device}; + +use super::hybrid::{HybridHandler, HybridState}; +use super::usb::{UsbHandler, UsbState}; +use super::CredentialService; + +enum ManagementRequest { + InitRequest(Box), + CompleteAuth, + GetDevices, + GetHybridCredential, + GetUsbCredential, +} + +enum ManagementResponse { + EnterClientPin, + InitRequest(Receiver>), + CompleteAuth(Result), + GetDevices(Vec), + GetHybridCredential, + GetUsbCredential, + InitStream(Result + Send + 'static>>, ()>), +} + +impl Debug for ManagementResponse { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::InitRequest(arg0) => f.debug_tuple("InitRequest").field(arg0).finish(), + Self::CompleteAuth(arg0) => f.debug_tuple("CompleteAuth").field(arg0).finish(), + Self::EnterClientPin => f.debug_tuple("EnterClientPin").finish(), + Self::GetDevices(arg0) => f.debug_tuple("GetDevices").field(arg0).finish(), + Self::GetHybridCredential => f.debug_tuple("GetHybridCredential").finish(), + Self::GetUsbCredential => f.debug_tuple("GetUsbCredential").finish(), + Self::InitStream(_) => f + .debug_tuple("InitStream") + .field(&String::from("")) + .finish(), + } + } +} + +#[allow(clippy::enum_variant_names)] +pub enum ServiceRequest { + EnterClientPin(String), + GetDevices, + GetHybridCredential, + GetUsbCredential, + InitStream, +} + +// Clippy complains that these variant names have the same prefix, but that's +// intentional for now. +#[allow(clippy::enum_variant_names)] +pub enum ServiceResponse { + EnterClientPin, + GetDevices(Vec), + GetHybridCredential, + GetUsbCredential, + InitStream(Result + Send + 'static>>, ()>), +} + +impl Debug for ServiceResponse { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::EnterClientPin => f.debug_tuple("EnterClientPin").finish(), + Self::GetDevices(arg0) => f.debug_tuple("GetDevices").field(arg0).finish(), + Self::GetHybridCredential => f.debug_tuple("GetHybridCredential").finish(), + Self::GetUsbCredential => f.debug_tuple("GetUsbCredential").finish(), + Self::InitStream(_) => f + .debug_tuple("InitStream") + .field(&String::from("")) + .finish(), + } + } +} + +enum InProcessServerRequest { + Client(ServiceRequest), + Management(ManagementRequest), +} + +#[derive(Debug)] +enum InProcessServerResponse { + Client(ServiceResponse), + Management(ManagementResponse), +} + +/// Used for communication from privileged broker to credential service +pub trait CredentialManagementClient { + fn init_request( + &self, + cred_request: CredentialRequest, + ) -> impl Future>> + Send; + fn complete_auth(&self) -> impl Future> + Send; + + fn get_available_public_key_devices( + &self, + ) -> impl Future, Box>> + Send; + + fn get_hybrid_credential(&mut self) -> impl Future> + Send; + fn get_usb_credential(&mut self) -> impl Future> + Send; + fn initiate_event_stream( + &mut self, + ) -> impl Future< + Output = Result + Send + 'static>>, ()>, + > + Send; + fn enter_client_pin(&mut self, pin: String) -> impl Future> + Send; + fn select_credential( + &self, + credential_id: String, + ) -> impl Future> + Send; +} + +#[derive(Debug)] +pub struct InProcessManager +where + H: HybridHandler + Debug + Send + Sync, + U: UsbHandler + Debug + Send + Sync, + UC: UiController + Debug + Send + Sync, +{ + tx: mpsc::Sender<( + InProcessServerRequest, + oneshot::Sender, + )>, + svc: Arc>>, + bg_event_tx: Option>, + usb_pin_tx: Arc>>>, + usb_event_forwarder_task: Arc>>, + hybrid_event_forwarder_task: Arc>>, +} + +impl< + H: HybridHandler + Debug + Send + Sync, + U: UsbHandler + Debug + Send + Sync, + UC: UiController + Debug + Send + Sync, + > Clone for InProcessManager +{ + fn clone(&self) -> Self { + Self { + tx: self.tx.clone(), + svc: self.svc.clone(), + bg_event_tx: self.bg_event_tx.clone(), + usb_pin_tx: self.usb_pin_tx.clone(), + usb_event_forwarder_task: self.usb_event_forwarder_task.clone(), + hybrid_event_forwarder_task: self.hybrid_event_forwarder_task.clone(), + } + } +} + +impl< + H: HybridHandler + Debug + Send + Sync, + U: UsbHandler + Debug + Send + Sync, + UC: UiController + Debug + Send + Sync, + > InProcessManager +{ + async fn send(&self, request: ManagementRequest) -> Result { + let (response_tx, response_rx) = oneshot::channel(); + self.tx + .send((InProcessServerRequest::Management(request), response_tx)) + .await + .unwrap(); + match response_rx.await { + Ok(InProcessServerResponse::Management(response)) => Ok(response), + Ok(_) => { + tracing::error!("invalid response received from server"); + Err(()) + } + Err(err) => { + tracing::error!("Failed to retrieve response from server: {:?}", err); + Err(()) + } + } + } +} + +impl< + H: HybridHandler + Debug + Send + Sync, + U: UsbHandler + Debug + Send + Sync, + UC: UiController + Debug + Send + Sync, + > CredentialManagementClient for InProcessManager +{ + async fn init_request( + &self, + cred_request: CredentialRequest, + ) -> Receiver> { + // self.svc.lock().await.init_request(&cred_request).await + todo!() + } + + async fn complete_auth(&self) -> Result { + /* + self.svc + .lock() + .await + .complete_auth() + .await + .ok_or("No credentials in credential service".to_string()) + */ + + todo!() + } + + async fn get_available_public_key_devices(&self) -> Result, Box> { + let devices = self + .svc + .lock() + .await + .get_available_public_key_devices() + .await + .map_err(|_| "Failed to get public key devices".to_string())?; + Ok(devices) + } + + async fn get_hybrid_credential(&mut self) -> Result<(), ()> { + let svc = self.svc.lock().await; + let mut stream = svc.get_hybrid_credential(); + if let Some(tx_weak) = self.bg_event_tx.as_ref().map(|t| t.clone().downgrade()) { + let task = tokio::spawn(async move { + while let Some(hybrid_state) = stream.next().await { + if let Some(tx) = tx_weak.upgrade() { + match hybrid_state { + HybridState::Completed | HybridState::Failed => { + tx.send(BackgroundEvent::HybridQrStateChanged(hybrid_state.into())) + .await + .unwrap(); + break; + } + _ => tx + .send(BackgroundEvent::HybridQrStateChanged(hybrid_state.into())) + .await + .unwrap(), + }; + } + } + }) + .abort_handle(); + if let Some(prev_task) = self + .hybrid_event_forwarder_task + .lock() + .unwrap() + .replace(task) + { + prev_task.abort(); + } + } + Ok(()) + } + + async fn get_usb_credential(&mut self) -> Result<(), ()> { + let mut stream = self.svc.lock().await.get_usb_credential(); + if let Some(tx_weak) = self.bg_event_tx.as_ref().map(|t| t.clone().downgrade()) { + let usb_pin_tx = self.usb_pin_tx.clone(); + let task = tokio::spawn(async move { + while let Some(state) = stream.next().await { + if let Some(tx) = tx_weak.upgrade() { + if tx + .send(BackgroundEvent::UsbStateChanged(state.clone().into())) + .await + .is_err() + { + tracing::debug!("Closing USB background event forwarder"); + break; + } + match state { + UsbState::NeedsPin { pin_tx, .. } => { + let mut usb_pin_tx = usb_pin_tx.lock().await; + let _ = usb_pin_tx.insert(pin_tx); + } + UsbState::Completed | UsbState::Failed(_) => { + break; + } + _ => {} + }; + } + } + }) + .abort_handle(); + if let Some(prev_task) = self.usb_event_forwarder_task.lock().unwrap().replace(task) { + prev_task.abort(); + } + } + Ok(()) + } + + async fn initiate_event_stream( + &mut self, + ) -> Result + Send + 'static>>, ()> { + let (tx, mut rx) = mpsc::channel(32); + self.bg_event_tx = Some(tx); + Ok(Box::pin(async_stream::stream! { + // TODO: we need to add a shutdown event that tells this stream + // to shut down when completed, failed or cancelled + while let Some(bg_event) = rx.recv().await { + yield bg_event + } + tracing::debug!("event stream ended"); + })) + } + + async fn enter_client_pin(&mut self, pin: String) -> Result<(), ()> { + if let Some(pin_tx) = self.usb_pin_tx.lock().await.take() { + pin_tx.send(pin).await.unwrap(); + } + Ok(()) + } + + async fn select_credential(&self, credential_id: String) -> Result<(), ()> { + todo!(); + } +} + +impl< + H: HybridHandler + Debug + Send + Sync, + U: UsbHandler + Debug + Send + Sync, + UC: UiController + Debug + Send + Sync, + > Drop for InProcessManager +{ + fn drop(&mut self) { + if let Some(task) = self.usb_event_forwarder_task.lock().unwrap().take() { + task.abort(); + } + + if let Some(task) = self.hybrid_event_forwarder_task.lock().unwrap().take() { + task.abort(); + } + } +} + +/// Represents a client for the UI to call methods on the credential service. +pub struct InProcessClient { + tx: mpsc::Sender<( + InProcessServerRequest, + oneshot::Sender, + )>, +} + +impl InProcessClient { + async fn send(&self, request: ServiceRequest) -> Result { + let (response_tx, response_rx) = oneshot::channel(); + self.tx + .send((InProcessServerRequest::Client(request), response_tx)) + .await + .unwrap(); + match response_rx.await { + Ok(InProcessServerResponse::Client(response)) => Ok(response), + Ok(_) => { + tracing::error!("invalid response received from server"); + Err(()) + } + Err(err) => { + tracing::error!("Failed to retrieve response from server: {:?}", err); + Err(()) + } + } + } +} + +impl CredentialServiceClient for InProcessClient { + async fn get_available_public_key_devices(&self) -> Result, ()> { + let response = self.send(ServiceRequest::GetDevices).await.unwrap(); + if let ServiceResponse::GetDevices(devices) = response { + Ok(devices) + } else { + Err(()) + } + } + + async fn get_hybrid_credential(&mut self) -> Result<(), ()> { + if let Ok(ServiceResponse::GetHybridCredential) = + self.send(ServiceRequest::GetHybridCredential).await + { + Ok(()) + } else { + Err(()) + } + } + + async fn get_usb_credential(&mut self) -> Result<(), ()> { + let response = self.send(ServiceRequest::GetUsbCredential).await.unwrap(); + if let ServiceResponse::GetUsbCredential = response { + Ok(()) + } else { + Err(()) + } + } + + async fn initiate_event_stream( + &mut self, + ) -> Result + Send + 'static>>, ()> { + if let Ok(ServiceResponse::InitStream(Ok(stream))) = + self.send(ServiceRequest::InitStream).await + { + Ok(stream) + } else { + Err(()) + } + } + + async fn enter_client_pin(&mut self, pin: String) -> Result<(), ()> { + if let Ok(ServiceResponse::EnterClientPin) = + self.send(ServiceRequest::EnterClientPin(pin)).await + { + Ok(()) + } else { + Err(()) + } + } + + async fn select_credential(&self, credential_id: String) -> Result<(), ()> { + todo!(); + } +} + +struct ArcInProcessClient(Arc); +impl CredentialServiceClient for ArcInProcessClient { + fn get_available_public_key_devices(&self) -> impl Future, ()>> { + InProcessClient::get_available_public_key_devices(&self.0) + } + + async fn get_hybrid_credential(&mut self) -> Result<(), ()> { + let client = Arc::get_mut(&mut self.0).ok_or(())?; + InProcessClient::get_hybrid_credential(client).await + } + + async fn get_usb_credential(&mut self) -> Result<(), ()> { + let client = Arc::get_mut(&mut self.0).ok_or(())?; + InProcessClient::get_usb_credential(client).await + } + + async fn initiate_event_stream( + &mut self, + ) -> Result + Send + 'static>>, ()> { + let client = Arc::get_mut(&mut self.0).ok_or(())?; + InProcessClient::initiate_event_stream(client).await + } + + async fn enter_client_pin(&mut self, pin: String) -> Result<(), ()> { + let client = Arc::get_mut(&mut self.0).ok_or(())?; + InProcessClient::enter_client_pin(client, pin).await + } + + fn select_credential(&self, credential_id: String) -> impl Future> { + InProcessClient::select_credential(&self.0, credential_id) + } +} + +#[derive(Debug)] +pub struct InProcessServer +where + H: HybridHandler + Debug + Send + Sync, + U: UsbHandler + Debug + Send + Sync, + UC: UiController + Debug + Send + Sync, +{ + rx: mpsc::Receiver<( + InProcessServerRequest, + oneshot::Sender, + )>, + mgr: InProcessManager, + responder_rx: Option>>, +} + +impl InProcessServer +where + H: HybridHandler + Debug + Send + Sync, + U: UsbHandler + Debug + Send + Sync, + UC: UiController + Debug + Send + Sync, +{ + pub fn new( + svc: CredentialService, + ) -> (Self, InProcessManager, InProcessClient) { + let (tx, rx) = mpsc::channel(256); + + let svc_arc = Arc::new(AsyncMutex::new(svc)); + let mgr_tx = tx.clone(); + let mgr = InProcessManager { + tx: mgr_tx.clone(), + svc: svc_arc, + bg_event_tx: None, + usb_pin_tx: Arc::new(AsyncMutex::new(None)), + usb_event_forwarder_task: Arc::new(Mutex::new(None)), + hybrid_event_forwarder_task: Arc::new(Mutex::new(None)), + }; + let client_tx = tx.clone(); + let client = InProcessClient { tx: client_tx }; + let server = Self { + rx, + mgr: mgr.clone(), + responder_rx: None, + }; + (server, mgr, client) + } + + pub async fn run(&mut self) { + while let Some((request, tx)) = self.rx.recv().await { + let response = match request { + InProcessServerRequest::Client(ServiceRequest::EnterClientPin(pin)) => { + let rsp = self.mgr.enter_client_pin(pin).await; + InProcessServerResponse::Client(ServiceResponse::EnterClientPin) + } + InProcessServerRequest::Client(ServiceRequest::GetDevices) => { + let rsp = self.mgr.get_available_public_key_devices().await.unwrap(); + InProcessServerResponse::Client(ServiceResponse::GetDevices(rsp)) + } + InProcessServerRequest::Client(ServiceRequest::GetHybridCredential) => { + let rsp = self.mgr.get_hybrid_credential().await; + InProcessServerResponse::Client(ServiceResponse::GetHybridCredential) + } + + InProcessServerRequest::Client(ServiceRequest::GetUsbCredential) => { + let rsp = self.mgr.get_usb_credential().await; + InProcessServerResponse::Client(ServiceResponse::GetUsbCredential) + } + InProcessServerRequest::Client(ServiceRequest::InitStream) => { + let rsp = self.mgr.initiate_event_stream().await; + InProcessServerResponse::Client(ServiceResponse::InitStream(rsp)) + } + InProcessServerRequest::Management(ManagementRequest::InitRequest(request)) => { + let rsp = self.mgr.init_request(*request).await; + InProcessServerResponse::Management(ManagementResponse::InitRequest(rsp)) + } + InProcessServerRequest::Management(ManagementRequest::CompleteAuth) => { + let rsp = self.mgr.complete_auth().await; + InProcessServerResponse::Management(ManagementResponse::CompleteAuth(rsp)) + } + InProcessServerRequest::Management(ManagementRequest::GetDevices) => { + let rsp = self.mgr.get_available_public_key_devices().await.unwrap(); + InProcessServerResponse::Management(ManagementResponse::GetDevices(rsp)) + } + InProcessServerRequest::Management(ManagementRequest::GetHybridCredential) => { + let rsp = self.mgr.get_hybrid_credential().await; + InProcessServerResponse::Management(ManagementResponse::GetHybridCredential) + } + InProcessServerRequest::Management(ManagementRequest::GetUsbCredential) => { + let rsp = self.mgr.get_usb_credential().await; + InProcessServerResponse::Client(ServiceResponse::GetUsbCredential) + } + }; + tx.send(response).unwrap() + } + } +} + +/// Used by the credential service to control the UI. +pub trait UiController { + fn launch_ui( + &self, + request: ViewRequest, + ) -> impl Future>> + Send; +} diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/credential_service/usb.rs b/credsd/src/credential_service/usb.rs similarity index 93% rename from xyz-iinuwa-credential-manager-portal-gtk/src/credential_service/usb.rs rename to credsd/src/credential_service/usb.rs index 31c20dab..1e5d9496 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/src/credential_service/usb.rs +++ b/credsd/src/credential_service/usb.rs @@ -17,12 +17,9 @@ use tokio::sync::broadcast; use tokio::sync::mpsc::{self, Receiver, Sender, WeakSender}; use tracing::{debug, warn}; -use crate::{ - dbus::{CredentialRequest, GetAssertionResponseInternal}, - gui::view_model::Credential, -}; +use creds_lib::model::{Credential, CredentialRequest, Error, GetAssertionResponseInternal}; -use super::{AuthenticatorResponse, CredentialResponse, Error}; +use super::{AuthenticatorResponse, CredentialResponse}; pub(crate) trait UsbHandler { fn start( @@ -527,6 +524,38 @@ impl From for UsbState { } } +impl From for creds_lib::model::UsbState { + fn from(value: UsbState) -> Self { + Self::from(&value) + } +} +impl From<&UsbState> for creds_lib::model::UsbState { + fn from(value: &UsbState) -> Self { + match value { + UsbState::Idle => creds_lib::model::UsbState::Idle, + UsbState::Waiting => creds_lib::model::UsbState::Waiting, + UsbState::SelectingDevice => creds_lib::model::UsbState::SelectingDevice, + UsbState::Connected => creds_lib::model::UsbState::Connected, + UsbState::NeedsPin { attempts_left, .. } => creds_lib::model::UsbState::NeedsPin { + attempts_left: *attempts_left, + }, + UsbState::NeedsUserVerification { attempts_left } => { + creds_lib::model::UsbState::NeedsUserVerification { + attempts_left: *attempts_left, + } + } + UsbState::NeedsUserPresence => creds_lib::model::UsbState::NeedsUserPresence, + UsbState::SelectCredential { creds, .. } => { + creds_lib::model::UsbState::SelectCredential { + creds: creds.to_owned(), + } + } + UsbState::Completed => creds_lib::model::UsbState::Completed, + UsbState::Failed(err) => creds_lib::model::UsbState::Failed(err.to_owned()), + } + } +} + async fn handle_usb_updates( signal_tx: &WeakSender>, mut state_rx: broadcast::Receiver, diff --git a/credsd/src/dbus/broker.rs b/credsd/src/dbus/broker.rs new file mode 100644 index 00000000..ff4c287a --- /dev/null +++ b/credsd/src/dbus/broker.rs @@ -0,0 +1,154 @@ +pub(crate) async fn start_service( + service_name: &str, + path: &str, + manager_client: C, +) -> zbus::Result { + let lock = Arc::new(AsyncMutex::new(())); + connection::Builder::session()? + .name(service_name)? + .serve_at( + path, + CredentialManager { + app_lock: lock, + manager_client, + }, + )? + .build() + .await +} + +struct CredentialRequestController { + svc: Arc>>, +} + +#[interface(name = "xyz.iinuwa.credentials.impl.Credentials")] +impl CredentialRequestController +where + H: HybridHandler + Debug + Send + Sync + 'static, + U: UsbHandler + Debug + Send + Sync + 'static, + UC: UiController + Debug + Send + Sync + 'static, +{ + async fn create_credential( + &self, + request: CreateCredentialRequest, + ) -> fdo::Result { + match create_credential_request_try_into_ctap2(&request) { + Ok((make_request, client_data_json)) => { + let mut rx = { + let rx: Receiver> = self + .svc + .lock() + .await + .init_request(&CredentialRequest::CreatePublicKeyCredentialRequest( + make_request, + )) + .await; + rx + }; + let msg = rx.recv().await.ok_or_else(|| { + tracing::error!("Credential service shutdown response channel prematurely"); + fdo::Error::Failed("Credential service shutdown".to_string()) + })?; + match msg { + Ok(CredentialResponse::CreatePublicKeyCredentialResponse(cred_response)) => { + let public_key_response = create_credential_response_try_from_ctap2( + &cred_response, + client_data_json, + )?; + Ok(public_key_response.into()) + } + // We should be returning the correct kind of response, so this shouldn't happen. + Ok(_) => Err(fdo::Error::Failed("Internal error occurred".to_string())), + Err(_) => Err(fdo::Error::Failed( + "Failed to create credential".to_string(), + )), + } + } + Err(_) => Err(fdo::Error::InvalidArgs( + "Unable to parse create credential request".to_string(), + )), + } + } + + async fn get_credential( + &self, + request: GetCredentialRequest, + ) -> fdo::Result { + match get_credential_request_try_into_ctap2(&request) { + Ok((get_request, client_data_json)) => { + let mut rx = { + let rx: Receiver> = self + .svc + .lock() + .await + .init_request(&CredentialRequest::GetPublicKeyCredentialRequest( + get_request, + )) + .await; + rx + }; + let msg = rx.recv().await.ok_or_else(|| { + tracing::error!("Credential service shutdown response channel prematurely"); + fdo::Error::Failed("Credential service shutdown".to_string()) + })?; + match msg { + Ok(CredentialResponse::GetPublicKeyCredentialResponse(cred_response)) => { + let public_key_response = get_credential_response_try_from_ctap2( + &cred_response, + client_data_json, + )?; + Ok(public_key_response.into()) + } + // We should be returning the correct kind of response, so this shouldn't happen. + Ok(_) => Err(fdo::Error::Failed("Internal error occurred".to_string())), + Err(_) => Err(fdo::Error::Failed("Failed to get credential".to_string())), + } + } + Err(_) => Err(fdo::Error::InvalidArgs( + "Unable to parse get credential request".to_string(), + )), + } + } +} + +async fn execute_flow( + // TODO: Replace this with UiControlClient + // gui_tx: &async_std::channel::Sender, + manager_client: &C, + cred_request: &CredentialRequest, +) -> zbus::Result { + let mut signal_rx = manager_client.init_request(cred_request.clone()).await; + let rsp = signal_rx + .recv() + .await + .ok_or(fdo::Error::Failed( + "Credential service unexpectedly interrupted".to_string(), + ))? + .map_err(|err| fdo::Error::Failed(err.to_string()))?; + Ok(rsp) + + /* + // start GUI + let operation = match &cred_request { + CredentialRequest::CreatePublicKeyCredentialRequest(_) => Operation::Create, + CredentialRequest::GetPublicKeyCredentialRequest(_) => Operation::Get, + }; + let (signal_tx, signal_rx) = tokio::sync::oneshot::channel(); + let view_request = ViewRequest { + operation, + signal: signal_tx, + }; + // TODO: Replace this with a UiControlClient + // gui_tx.send(view_request).await.unwrap(); + // wait for gui to complete + signal_rx.await.map_err(|_| { + zbus::Error::Failure("GUI channel closed before completing request.".to_string()) + })?; + + // finish up + manager_client.complete_auth().await.map_err(|err| { + tracing::error!("Error retrieving credential: {:?}", err); + zbus::Error::Failure("Error retrieving credential".to_string()) + }) + */ +} diff --git a/credsd/src/dbus/flow_control.rs b/credsd/src/dbus/flow_control.rs new file mode 100644 index 00000000..28708b7d --- /dev/null +++ b/credsd/src/dbus/flow_control.rs @@ -0,0 +1,395 @@ +use std::future::Future; +use std::{collections::VecDeque, error::Error, fmt::Debug, pin::Pin, sync::Arc}; + +use creds_lib::model::{ + CredentialRequest, CredentialResponse, Error as CredentialServiceError, WebAuthnError, +}; +use creds_lib::server::{BackgroundEvent, Device}; +use futures_lite::{Stream, StreamExt}; +use tokio::sync::oneshot; +use tokio::{ + sync::{ + mpsc::{self, Receiver, Sender}, + Mutex as AsyncMutex, + }, + task::AbortHandle, +}; +use zbus::{ + connection::{Builder, Connection}, + fdo, interface, + object_server::{InterfaceRef, SignalEmitter}, + ObjectServer, +}; + +use crate::credential_service::{ + hybrid::{HybridHandler, HybridState}, + usb::UsbHandler, + CredentialManagementClient, CredentialService, UiController, UsbState, +}; +pub const INTERFACE_NAME: &'static str = "xyz.iinuwa.credentials.FlowControl1"; +pub const SERVICE_PATH: &'static str = "/xyz/iinuwa/credentials/FlowControl"; +pub const SERVICE_NAME: &'static str = "xyz.iinuwa.credentials.FlowControl"; + +pub async fn start_flow_control_service< + H: HybridHandler + Debug + Send + Sync + 'static, + U: UsbHandler + Debug + Send + Sync + 'static, + UC: UiController + Debug + Send + Sync + 'static, +>( + credential_service: CredentialService, +) -> zbus::Result<( + Connection, + Sender<( + CredentialRequest, + oneshot::Sender>, + )>, +)> { + let svc = Arc::new(AsyncMutex::new(credential_service)); + let svc2 = svc.clone(); + let conn = Builder::session()? + .name(SERVICE_NAME)? + .serve_at( + SERVICE_PATH, + FlowControlService { + signal_state: Arc::new(AsyncMutex::new(SignalState::Idle)), + svc, + usb_pin_tx: Arc::new(AsyncMutex::new(None)), + usb_cred_tx: Arc::new(AsyncMutex::new(None)), + usb_event_forwarder_task: Arc::new(AsyncMutex::new(None)), + hybrid_event_forwarder_task: Arc::new(AsyncMutex::new(None)), + }, + )? + .build() + .await?; + let (initiator_tx, mut initiator_rx) = mpsc::channel(2); + tokio::spawn(async move { + let svc = svc2; + while let Some((msg, tx)) = initiator_rx.recv().await { + svc.lock().await.init_request(&msg, tx).await; + } + }); + Ok((conn, initiator_tx)) +} + +struct FlowControlService { + signal_state: Arc>, + svc: Arc>>, + usb_pin_tx: Arc>>>, + usb_cred_tx: Arc>>>, + usb_event_forwarder_task: Arc>>, + hybrid_event_forwarder_task: Arc>>, +} + +/// The following methods are for communication between the [trusted] +/// UI and the credential service, and should not be called by arbitrary +/// clients. +#[interface( + name = "xyz.iinuwa.credentials.FlowControl1", + proxy( + gen_blocking = false, + default_path = "/xyz/iinuwa/credentials/FlowControl", + default_service = "xyz.iinuwa.credentials.FlowControl", + ) +)] +impl FlowControlService +where + H: HybridHandler + Debug + Send + Sync + 'static, + U: UsbHandler + Debug + Send + Sync + 'static, + UC: UiController + Debug + Send + Sync + 'static, +{ + async fn initiate_event_stream( + &self, + #[zbus(signal_emitter)] emitter: SignalEmitter<'_>, + ) -> fdo::Result<()> { + let mut signal_state = self.signal_state.lock().await; + match *signal_state { + SignalState::Idle => {} + SignalState::Pending(ref mut pending) => { + for msg in pending.iter_mut() { + emitter.state_changed(msg.clone()).await?; + } + } + SignalState::Active => {} + }; + *signal_state = SignalState::Active; + Ok(()) + } + + async fn get_available_public_key_devices(&self) -> fdo::Result> { + let devices = self + .svc + .lock() + .await + .get_available_public_key_devices() + .await + .map_err(|_| fdo::Error::Failed("Failed to retrieve available devices".to_string()))?; + let dbus_devices: Vec = devices.into_iter().map(Device::from).collect(); + + Ok(dbus_devices) + } + + async fn get_hybrid_credential( + &self, + #[zbus(object_server)] object_server: &ObjectServer, + ) -> fdo::Result<()> { + let svc = self.svc.lock().await; + let mut stream = svc.get_hybrid_credential(); + let signal_state = self.signal_state.clone(); + let object_server = object_server.clone(); + let task = tokio::spawn(async move { + let interface: zbus::Result>> = + object_server.interface(SERVICE_PATH).await; + + let emitter = match interface { + Ok(ref i) => i.signal_emitter(), + Err(err) => { + tracing::error!("Failed to get connection to D-Bus to send signals: {err}"); + return; + } + }; + while let Some(state) = stream.next().await { + let event = + creds_lib::model::BackgroundEvent::HybridQrStateChanged(state.clone().into()) + .try_into(); + match event { + Err(err) => { + tracing::error!("Failed to serialize state update: {err}"); + break; + } + Ok(event) => match send_state_update(&emitter, &signal_state, event).await { + Ok(_) => {} + Err(err) => { + tracing::error!("Failed to send state update to UI: {err}"); + break; + } + }, + } + match state { + HybridState::Completed | HybridState::Failed => { + break; + } + _ => {} + }; + } + }) + .abort_handle(); + if let Some(prev_task) = self.hybrid_event_forwarder_task.lock().await.replace(task) { + prev_task.abort(); + } + Ok(()) + } + + async fn get_usb_credential( + &self, + #[zbus(object_server)] object_server: &ObjectServer, + ) -> fdo::Result<()> { + let mut stream = self.svc.lock().await.get_usb_credential(); + let usb_pin_tx = self.usb_pin_tx.clone(); + let usb_cred_tx = self.usb_cred_tx.clone(); + let signal_state = self.signal_state.clone(); + let object_server = object_server.clone(); + let task = tokio::spawn(async move { + let interface: zbus::Result>> = + object_server.interface(SERVICE_PATH).await; + + let emitter = match interface { + Ok(ref i) => i.signal_emitter(), + Err(err) => { + tracing::error!("Failed to get connection to D-Bus to send signals: {err}"); + return; + } + }; + while let Some(state) = stream.next().await { + match creds_lib::model::BackgroundEvent::UsbStateChanged((&state).into()).try_into() + { + Err(err) => { + tracing::error!("Failed to serialize state update: {err}"); + break; + } + Ok(event) => match send_state_update(&emitter, &signal_state, event).await { + Ok(_) => {} + Err(err) => { + tracing::error!("Failed to send state update to UI: {err}"); + break; + } + }, + }; + match state { + UsbState::NeedsPin { pin_tx, .. } => { + let mut usb_pin_tx = usb_pin_tx.lock().await; + let _ = usb_pin_tx.insert(pin_tx); + } + UsbState::SelectCredential { cred_tx, .. } => { + let mut usb_cred_tx = usb_cred_tx.lock().await; + let _ = usb_cred_tx.insert(cred_tx); + } + UsbState::Completed | UsbState::Failed(_) => { + break; + } + _ => {} + }; + } + }) + .abort_handle(); + if let Some(prev_task) = self.usb_event_forwarder_task.lock().await.replace(task) { + prev_task.abort(); + } + Ok(()) + } + + async fn select_device(&self, device_id: String) -> fdo::Result<()> { + todo!() + } + + async fn enter_client_pin(&self, pin: String) -> fdo::Result<()> { + if let Some(pin_tx) = self.usb_pin_tx.lock().await.take() { + pin_tx.send(pin).await.unwrap(); + } + Ok(()) + } + + async fn select_credential(&self, credential_id: String) -> fdo::Result<()> { + if let Some(cred_tx) = self.usb_cred_tx.lock().await.take() { + cred_tx.send(credential_id).await.unwrap(); + } + Ok(()) + } + + #[zbus(signal)] + async fn state_changed( + emitter: &SignalEmitter<'_>, + update: BackgroundEvent, + ) -> zbus::Result<()>; +} +async fn send_state_update( + emitter: &SignalEmitter<'_>, + signal_state: &Arc>, + update: BackgroundEvent, +) -> fdo::Result<()> { + let mut signal_state = signal_state.lock().await; + match *signal_state { + SignalState::Idle => { + let pending = VecDeque::from([update]); + *signal_state = SignalState::Pending(pending); + } + SignalState::Pending(ref mut pending) => { + pending.push_back(update); + } + SignalState::Active => { + emitter.state_changed(update).await?; + } + }; + Ok(()) +} + +enum SignalState { + /// No state + Idle, + /// Waiting for client to signal that it's ready to receive events. + /// Holds a cache of events to send once the client connects. + Pending(VecDeque), + /// Client is actively receiving messages. + Active, +} + +pub struct CredentialControlServiceClient { + conn: Connection, +} + +impl CredentialControlServiceClient { + pub fn new(conn: Connection) -> Self { + Self { conn } + } + + async fn proxy(&self) -> zbus::Result { + FlowControlServiceProxy::new(&self.conn).await + } +} + +/* +impl CredentialManagementClient for CredentialControlServiceClient { + async fn init_request( + &self, + cred_request: CredentialRequest, + ) -> Receiver> { + // TODO: Start here + // self.proxy().await.unwrap(). + todo!() + } + + async fn complete_auth(&self) -> Result { + todo!() + } + + async fn get_available_public_key_devices( + &self, + ) -> Result, Box> { + let devices: Result, String> = self + .proxy() + .await? + .get_available_public_key_devices() + .await? + .into_iter() + .map(|d| d.try_into().map_err(|_| "Failed".to_string())) + .collect(); + Ok(devices?) + } + + async fn get_hybrid_credential(&mut self) -> Result<(), ()> { + todo!() + } + + async fn get_usb_credential(&mut self) -> Result<(), ()> { + todo!() + } + + async fn initiate_event_stream( + &mut self, + ) -> Result + Send + 'static>>, ()> + { + todo!() + } + + async fn enter_client_pin(&mut self, pin: String) -> Result<(), ()> { + if let Err(err) = self.proxy().await.unwrap().enter_client_pin(pin).await { + tracing::error!("Failed to send client pin: {err}"); + return Err(()); + } + Ok(()) + } + + async fn select_credential(&self, credential_id: String) -> Result<(), ()> { + todo!() + } +} + */ + +pub trait CredentialRequestController { + fn request_credential( + &self, + request: CredentialRequest, + ) -> impl Future> + Send; +} + +pub struct CredentialRequestControllerClient { + pub initiator: Sender<( + CredentialRequest, + oneshot::Sender>, + )>, +} + +impl CredentialRequestController for CredentialRequestControllerClient { + async fn request_credential( + &self, + request: CredentialRequest, + ) -> Result { + let (tx, rx) = oneshot::channel(); + // TODO: We need a PlatformError variant. + self.initiator.send((request, tx)).await.unwrap(); + rx.await + .map_err(|_| { + tracing::error!("Credential response channel closed prematurely"); + WebAuthnError::NotAllowedError + }) + .and_then(|msg| msg.map_err(|_| WebAuthnError::NotAllowedError)) + } +} diff --git a/credsd/src/dbus/gateway.rs b/credsd/src/dbus/gateway.rs new file mode 100644 index 00000000..7721fe3e --- /dev/null +++ b/credsd/src/dbus/gateway.rs @@ -0,0 +1,263 @@ +//! Implements the service that public clients can connect to. Responsible for +//! authorizing clients for origins and validating request parameters. + +use std::sync::Arc; + +use creds_lib::{ + model::{CredentialRequest, CredentialResponse, GetClientCapabilitiesResponse, WebAuthnError}, + server::{ + CreateCredentialRequest, CreateCredentialResponse, GetCredentialRequest, + GetCredentialResponse, + }, +}; +use serde::Deserialize; +use tokio::sync::Mutex as AsyncMutex; +use zbus::{fdo, interface, Connection, DBusError}; + +use crate::dbus::{ + create_credential_request_try_into_ctap2, create_credential_response_try_from_ctap2, + get_credential_request_try_into_ctap2, get_credential_response_try_from_ctap2, + CredentialRequestController, +}; + +pub const INTERFACE_NAME: &'static str = "xyz.iinuwa.credentials.Credentials1"; +pub const SERVICE_NAME: &'static str = "xyz.iinuwa.credentials.Credentials"; +pub const SERVICE_PATH: &'static str = "/xyz/iinuwa/credentials/Credentials"; + +pub async fn start_gateway( + controller: C, +) -> Result { + zbus::connection::Builder::session() + .inspect_err(|err| { + tracing::error!("Failed to connect to D-Bus session: {err}"); + })? + .name(SERVICE_NAME)? + .serve_at( + SERVICE_PATH, + CredentialGateway { + controller: Arc::new(AsyncMutex::new(controller)), + }, + )? + .build() + .await +} + +struct CredentialGateway { + controller: Arc>, +} + +/// These are public methods that can be called by arbitrary clients to begin a credential flow. +#[interface(name = "xyz.iinuwa.credentials.Credentials1")] +impl CredentialGateway { + async fn create_credential( + &self, + request: CreateCredentialRequest, + ) -> Result { + let (origin, is_same_origin, _top_origin) = check_origin( + request.origin.as_ref().map(|s| s.as_str()), + request.is_same_origin, + ) + .await + .map_err(Error::from)?; + if let ("publicKey", Some(_)) = (request.r#type.as_ref(), &request.public_key) { + if !is_same_origin { + // TODO: Once we modify the models to convey the top-origin in cross origin requests to the UI, we can remove this error message. + // We should still reject cross-origin requests for conditionally-mediated requests. + tracing::warn!("Client attempted to issue cross-origin request for credentials, which are not supported by this platform."); + return Err(WebAuthnError::NotAllowedError.into()); + } + let (make_cred_request, client_data_json) = + create_credential_request_try_into_ctap2(&request).map_err(|e| { + tracing::error!("Could not parse passkey creation request: {e:?}"); + WebAuthnError::TypeError + })?; + let cred_request = + CredentialRequest::CreatePublicKeyCredentialRequest(make_cred_request); + + let response = self + .controller + .lock() + .await + .request_credential(cred_request) + .await?; + + if let CredentialResponse::CreatePublicKeyCredentialResponse(cred_response) = response { + let public_key_response = + create_credential_response_try_from_ctap2(&cred_response, client_data_json) + .map_err(|err| { + tracing::error!( + "Failed to parse credential response from authenticator: {err}" + ); + // Using NotAllowedError as a catch-all error. + WebAuthnError::NotAllowedError + })?; + Ok(public_key_response.into()) + } else { + // TODO: is response safe to log here? + // tracing::error!("Expected create public key credential response, received {response:?}"); + tracing::error!("Did not receive expected create public key credential response."); + // Using NotAllowedError as a catch-all error. + Err(WebAuthnError::NotAllowedError.into()) + } + } else { + tracing::error!("Unknown credential type request: {}", request.r#type); + Err(WebAuthnError::TypeError.into()) + } + } + + async fn get_credential( + &self, + request: GetCredentialRequest, + ) -> Result { + let (origin, is_same_origin, _top_origin) = check_origin( + request.origin.as_ref().map(|s| s.as_str()), + request.is_same_origin, + ) + .await + .map_err(Error::from)?; + if let ("publicKey", Some(_)) = (request.r#type.as_ref(), &request.public_key) { + if !is_same_origin { + // TODO: Once we modify the models to convey the top-origin in cross origin requests to the UI, we can remove this error message. + tracing::warn!("Client attempted to issue cross-origin request for credentials, which are not supported by this platform."); + return Err(WebAuthnError::NotAllowedError.into()); + } + // Setup request + + // TODO: assert that RP ID is bound to origin: + // - if RP ID is not set, set the RP ID to the origin's effective domain + // - if RP ID is set, assert that it matches origin's effective domain + // - if RP ID is set, but origin's effective domain doesn't match + // - query for related origins, if supported + // - fail if not supported, or if RP ID doesn't match any related origins. + let (get_cred_request, client_data_json) = + get_credential_request_try_into_ctap2(&request).map_err(|e| { + tracing::error!("Could not parse passkey assertion request: {e:?}"); + WebAuthnError::TypeError + })?; + let cred_request = CredentialRequest::GetPublicKeyCredentialRequest(get_cred_request); + + let response = self + .controller + .lock() + .await + .request_credential(cred_request) + .await?; + + if let CredentialResponse::GetPublicKeyCredentialResponse(cred_response) = response { + let public_key_response = + get_credential_response_try_from_ctap2(&cred_response, client_data_json) + .map_err(|err| { + tracing::error!( + "Failed to parse credential response from authenticator: {err}" + ); + // Using NotAllowedError as a catch-all error. + WebAuthnError::NotAllowedError + })?; + Ok(public_key_response.into()) + } else { + // TODO: is response safe to log here? + // tracing::error!("Expected get public key credential response, received {response:?}"); + tracing::error!("Did not receive expected get public key credential response."); + // Using NotAllowedError as a catch-all error. + Err(WebAuthnError::NotAllowedError.into()) + } + } else { + tracing::error!("Unknown credential type request: {}", request.r#type); + Err(WebAuthnError::TypeError.into()) + } + } + + async fn get_client_capabilities(&self) -> fdo::Result { + Ok(GetClientCapabilitiesResponse { + conditional_create: false, + conditional_get: false, + hybrid_transport: true, + passkey_platform_authenticator: false, + user_verifying_platform_authenticator: false, + related_origins: false, + signal_all_accepted_credentials: false, + signal_current_user_details: false, + signal_unknown_credential: false, + }) + } +} + +async fn check_origin( + origin: Option<&str>, + is_same_origin: Option, + // TODO: Replace is_same_origin with explicit top_origin + // top_origin: Option<&str>, +) -> Result<(String, bool, String), WebAuthnError> { + let origin = if let Some(origin) = origin { + origin.to_string() + } else { + tracing::warn!( + "Caller requested implicit origin, which is not yet implemented. Rejecting request." + ); + return Err(WebAuthnError::SecurityError); + }; + let is_same_origin = is_same_origin.unwrap_or(false); + let top_origin = if is_same_origin { + origin.clone() + } else { + tracing::warn!("Client attempted to issue cross-origin request for credentials, which are not supported by this platform."); + return Err(WebAuthnError::NotAllowedError.into()); + }; + Ok((origin, true, top_origin)) +} + +#[derive(DBusError, Debug)] +#[zbus(prefix = "xyz.iinuwa.credentials")] +enum Error { + #[zbus(error)] + ZBus(zbus::Error), + + /// The ceremony was cancelled by an AbortController. See § 5.6 Abort + /// Operations with AbortSignal and § 1.3.4 Aborting Authentication + /// Operations. + AbortError, + + /// Either `residentKey` was set to required and no available authenticator + /// supported resident keys, or `userVerification` was set to required and no + /// available authenticator could perform user verification. + ConstraintError, + + /// The authenticator used in the ceremony recognized an entry in + /// `excludeCredentials` after the user consented to registering a credential. + InvalidStateError, + + /// No entry in `pubKeyCredParams` had a type property of `public-key`, or the + /// authenticator did not support any of the signature algorithms specified + /// in `pubKeyCredParams`. + NotSupportedError, + + /// The effective domain was not a valid domain, or `rp.id` was not equal to + /// or a registrable domain suffix of the effective domain. In the latter + /// case, the client does not support related origin requests or the related + /// origins validation procedure failed. + SecurityError, + + /// A catch-all error covering a wide range of possible reasons, including + /// common ones like the user canceling out of the ceremony. Some of these + /// causes are documented throughout this spec, while others are + /// client-specific. + NotAllowedError, + + /// The options argument was not a valid `CredentialCreationOptions` value, or + /// the value of `user.id` was empty or was longer than 64 bytes. + TypeError, +} + +impl From for Error { + fn from(value: WebAuthnError) -> Self { + match value { + WebAuthnError::AbortError => Self::AbortError, + WebAuthnError::ConstraintError => Self::ConstraintError, + WebAuthnError::InvalidStateError => Self::InvalidStateError, + WebAuthnError::NotSupportedError => Self::NotSupportedError, + WebAuthnError::SecurityError => Self::SecurityError, + WebAuthnError::NotAllowedError => Self::NotAllowedError, + WebAuthnError::TypeError => Self::TypeError, + } + } +} diff --git a/credsd/src/dbus/mod.rs b/credsd/src/dbus/mod.rs new file mode 100644 index 00000000..e9912531 --- /dev/null +++ b/credsd/src/dbus/mod.rs @@ -0,0 +1,83 @@ +//! This module hosts the D-Bus endpoints needed for this service. +//! +//! The D-Bus endpoints are structured to allow sandboxing with small component processes connected with a central broker. +//! # Broker: +//! The broker's main responsibility is to enforce permissions between the various components. +//! To do that, the broker has a bunch of seemingly redundant methods that forwards to the actual +//! implementations. +//! +//! The internal components should sandboxed only to have access to resources needed to fulfill the request. +//! +//! ## Client -> pub service -> broker -> Cred Service: +//! These methods are called by the pub service on behalf of a client requesting credentials. +//! The pub service must pass appropriate context for the broker to determine the client's permissions. +//! - get_cred(options) +//! - create_cred(options) +//! - get_client_capabilities() +//! +//! ## UI -> broker -> Cred service: +//! These methods are called by the trusted UI to interact with the credential service. +//! - initialize_event_stream() +//! - get_hybrid_credential() +//! - get_usb_credential() +//! - get_available_devices() # a device is a discrete authenticator or a group of potential authenticators accessible via a particular transport, or a credential? +//! - send_pin() +//! - select_credential() +//! - cancel_request() +//! +//! ## Cred Service -> broker -> UI: +//! - launch UI +//! - send_state_changed() + +// mod broker; +mod flow_control; +mod gateway; +mod model; +mod ui_control; + +use std::pin::Pin; +use std::{collections::VecDeque, error::Error, fmt::Debug, sync::Arc}; + +use creds_lib::model::MakeCredentialRequest; +use creds_lib::server::{CreateCredentialRequest, CreatePublicKeyCredentialRequest, ViewRequest}; +use futures_lite::{Stream, StreamExt}; +use tokio::sync::mpsc::{Receiver, Sender}; +use tokio::sync::Mutex as AsyncMutex; +use tokio::task::AbortHandle; +use zbus::object_server::{InterfaceRef, SignalEmitter}; +use zbus::{ + connection::{self, Connection}, + fdo, interface, +}; +use zbus::{proxy, ObjectServer}; + +use creds_lib::{ + client::CredentialServiceClient, + model::{ + CredentialRequest, CredentialResponse, CredentialType, GetClientCapabilitiesResponse, + Operation, + }, + server::{ + BackgroundEvent, CreateCredentialResponse, CreatePublicKeyCredentialResponse, Device, + GetCredentialRequest, GetCredentialResponse, GetPublicKeyCredentialResponse, + }, +}; + +use self::model::{ + create_credential_request_try_into_ctap2, create_credential_response_try_from_ctap2, + get_credential_request_try_into_ctap2, get_credential_response_try_from_ctap2, +}; +use crate::credential_service::hybrid::{HybridHandler, HybridState}; +use crate::credential_service::usb::UsbHandler; +use crate::credential_service::{ + CredentialManagementClient, CredentialService, UiController, UsbState, +}; + +pub use self::{ + flow_control::{ + start_flow_control_service, CredentialRequestController, CredentialRequestControllerClient, + SERVICE_NAME as FLOW_CONTROL_SERVICE_NAME, SERVICE_PATH as FLOW_CONTROL_SERVICE_PATH, + }, + gateway::start_gateway, + ui_control::UiControlServiceClient, +}; diff --git a/credsd/src/dbus/model.rs b/credsd/src/dbus/model.rs new file mode 100644 index 00000000..dae18f6f --- /dev/null +++ b/credsd/src/dbus/model.rs @@ -0,0 +1,400 @@ +//! This module contains types used for serializing data to and from D-Bus method calls. +//! +//! Types shared between components within this service belong in creds_lib::model. + +use std::{collections::HashMap, time::Duration}; + +use base64::{self, engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; +use serde::{Deserialize, Serialize}; +use tokio::sync::oneshot; +use zbus::{ + fdo, + zvariant::{self, DeserializeDict, OwnedValue, SerializeDict, Type, Value, LE}, +}; + +use creds_lib::{ + model::{ + CredentialType, GetAssertionResponseInternal, MakeCredentialResponseInternal, Operation, + ViewUpdate, + }, + server::{ + CreateCredentialRequest, CreatePublicKeyCredentialResponse, GetCredentialRequest, + GetPublicKeyCredentialResponse, + }, +}; + +pub(super) struct ViewRequest { + pub(super) operation: Operation, + pub(super) signal: oneshot::Sender<()>, +} + +use crate::webauthn::{ + self, CredentialProtectionExtension, Ctap2PublicKeyCredentialDescriptor, + Ctap2PublicKeyCredentialRpEntity, Ctap2PublicKeyCredentialUserEntity, + GetAssertionHmacOrPrfInput, GetAssertionLargeBlobExtension, GetAssertionRequest, + GetAssertionRequestExtensions, GetPublicKeyCredentialUnsignedExtensionsResponse, + MakeCredentialHmacOrPrfInput, MakeCredentialRequest, MakeCredentialsRequestExtensions, + PublicKeyCredentialParameters, ResidentKeyRequirement, UserVerificationRequirement, +}; + +// Helper functions for translating D-Bus types into internal types +pub(super) fn create_credential_request_try_into_ctap2( + request: &CreateCredentialRequest, +) -> std::result::Result<(MakeCredentialRequest, String), webauthn::Error> { + if request.public_key.is_none() { + return Err(webauthn::Error::NotSupported); + } + let options = request.public_key.as_ref().unwrap(); + + let request_value = serde_json::from_str::(&options.request_json) + .map_err(|_| webauthn::Error::Internal("Invalid request JSON".to_string()))?; + let json = request_value + .as_object() + .ok_or_else(|| webauthn::Error::Internal("Invalid request JSON".to_string()))?; + let challenge = json + .get("challenge") + .and_then(|c| c.as_str()) + .ok_or_else(|| webauthn::Error::Internal("JSON missing `challenge` field".to_string()))? + .to_owned(); + let rp = json + .get("rp") + .and_then(|val| { + serde_json::from_str::(&val.to_string()).ok() + }) + .ok_or_else(|| webauthn::Error::Internal("JSON missing `rp` field".to_string()))?; + let user = + json.get("user") + .ok_or(webauthn::Error::Internal( + "JSON missing `user` field".to_string(), + )) + .and_then(|val| { + serde_json::from_str::(&val.to_string()) + .map_err(|e| { + let msg = format!("JSON missing `user` field: {e}"); + webauthn::Error::Internal(msg) + }) + })?; + let other_options = + serde_json::from_str::(&request_value.to_string()) + .map_err(|_| webauthn::Error::Internal("Invalid request JSON".to_string()))?; + 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); + + (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 = request_value + .clone() + .get("pubKeyCredParams") + .ok_or_else(|| { + webauthn::Error::Internal( + "Request JSON missing or invalid `pubKeyCredParams` key".to_string(), + ) + }) + .and_then(|val| -> std::result::Result, webauthn::Error> { + serde_json::from_str::>(&val.to_string()).map_err( + |e| { + webauthn::Error::Internal(format!( + "Request JSON missing or invalid `pubKeyCredParams` key: {e}" + )) + }, + ) + })?; + 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, _) => { + return Err(webauthn::Error::Internal( + "Error reading origin from request".to_string(), + )); + } + }; + 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, + + relying_party: rp, + user, + resident_key, + user_verification, + algorithms, + exclude, + extensions, + timeout: other_options.timeout.unwrap_or(Duration::from_secs(300)), + }, + client_data_json, + )) +} + +pub(super) fn create_credential_response_try_from_ctap2( + response: &MakeCredentialResponseInternal, + client_data_json: String, +) -> std::result::Result { + let auth_data = &response.ctap.authenticator_data; + let attested_credential = auth_data.attested_credential.as_ref().ok_or_else(|| { + fdo::Error::Failed("Invalid credential received from authenticator".to_string()) + })?; + + let unsigned_extensions = + serde_json::to_string(&response.ctap.unsigned_extensions_output).unwrap(); + let authenticator_data_blob = auth_data.to_response_bytes().unwrap(); + let attestation_statement = (&response.ctap.attestation_statement) + .try_into() + .map_err(|_| fdo::Error::Failed("Could not serialize attestation statement".to_string()))?; + let attestation_object = webauthn::create_attestation_object( + &authenticator_data_blob, + &attestation_statement, + response.ctap.enterprise_attestation.unwrap_or(false), + ) + .map_err(|_| zbus::Error::Failure("Failed to create attestation object".to_string()))?; + // 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, + client_data_json, + Some(response.transport.clone()), + unsigned_extensions, + response.attachment_modality.clone(), + ) + .to_json(); + let response = CreatePublicKeyCredentialResponse { + registration_response_json, + }; + Ok(response) +} + +pub(super) fn get_credential_request_try_into_ctap2( + request: &GetCredentialRequest, +) -> std::result::Result<(GetAssertionRequest, String), webauthn::Error> { + if request.public_key.is_none() { + return Err(webauthn::Error::NotSupported); + } + let options: webauthn::GetCredentialOptions = request + .public_key + .as_ref() + .ok_or(webauthn::Error::Internal( + ("Invalid request: no \"public-key\" options specified").to_string(), + )) + .and_then(|o| { + serde_json::from_str(&o.request_json) + .map_err(|e| webauthn::Error::Internal(format!("Invalid request JSON: {:?}", e))) + }) + .unwrap(); + 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, _) => { + return Err(webauthn::Error::Internal( + "Error reading origin from request".to_string(), + )); + } + }; + 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, + _ => { + return Err(webauthn::Error::Internal( + "Invalid user verification requirement specified".to_string(), + )); + } + }; + let relying_party_id = options.rp_id.unwrap_or_else(|| { + let (_, effective_domain) = origin.rsplit_once('/').unwrap(); + effective_domain.to_string() + }); + + 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 + }; + + 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, + )) +} + +pub(super) fn get_credential_response_try_from_ctap2( + response: &GetAssertionResponseInternal, + client_data_json: String, +) -> std::result::Result { + let authenticator_data_blob = response + .ctap + .authenticator_data + .to_response_bytes() + .unwrap(); + + // 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 + .as_ref() + .map(GetPublicKeyCredentialUnsignedExtensionsResponse::from); + + let authentication_response_json = webauthn::GetPublicKeyCredentialResponse::new( + client_data_json, + response + .ctap + .credential_id + .as_ref() + .map(|c| c.id.clone().into_vec()), + authenticator_data_blob, + response.ctap.signature.clone(), + response.ctap.user.as_ref().map(|u| u.id.clone().into_vec()), + response.attachment_modality.clone(), + unsigned_extensions, + ) + .to_json(); + + let response = GetPublicKeyCredentialResponse { + authentication_response_json, + }; + Ok(response) +} diff --git a/credsd/src/dbus/ui_control.rs b/credsd/src/dbus/ui_control.rs new file mode 100644 index 00000000..ccbfd8ad --- /dev/null +++ b/credsd/src/dbus/ui_control.rs @@ -0,0 +1,42 @@ +//! These methods are called by the credential service to control the UI. + +use std::error::Error; + +use zbus::{fdo, proxy, Connection}; + +use crate::credential_service::UiController; +use creds_lib::server::ViewRequest; + +#[proxy( + gen_blocking = false, + interface = "xyz.iinuwa.credentials.UiControl1", + default_service = "xyz.iinuwa.credentials.UiControl", + default_path = "/xyz/iinuwa/credentials/UiControl" +)] +trait UiControlService { + fn launch_ui(&self, request: ViewRequest) -> fdo::Result<()>; +} + +#[derive(Debug)] +pub struct UiControlServiceClient { + conn: Connection, +} + +impl UiControlServiceClient { + pub fn new(conn: Connection) -> Self { + Self { conn } + } + + async fn proxy(&self) -> Result { + UiControlServiceProxy::new(&self.conn).await + } +} +impl UiController for UiControlServiceClient { + async fn launch_ui(&self, request: ViewRequest) -> Result<(), Box> { + self.proxy() + .await? + .launch_ui(request) + .await + .map_err(|err| err.into()) + } +} diff --git a/credsd/src/main.rs b/credsd/src/main.rs new file mode 100644 index 00000000..c6f4b956 --- /dev/null +++ b/credsd/src/main.rs @@ -0,0 +1,59 @@ +mod cbor; +mod cose; +mod credential_service; +mod dbus; +// mod model; +mod serde; +mod webauthn; + +use std::error::Error; + +use crate::{ + credential_service::{ + hybrid::InternalHybridHandler, usb::InProcessUsbHandler, CredentialService, + }, + dbus::{CredentialRequestControllerClient, UiControlServiceClient}, +}; + +#[tokio::main] +async fn main() { + // Initialize logger + tracing_subscriber::fmt::init(); + rustls::crypto::ring::default_provider() + .install_default() + .expect("Failed to install rustls crypto provider"); + + println!("Starting..."); + run().await.unwrap(); +} + +async fn run() -> Result<(), Box> { + print!("Connecting to D-Bus as client...\t"); + let dbus_client_conn = zbus::connection::Builder::session()? + .name("xyz.iinuwa.credentials.Credsd")? + .build() + .await?; + println!(" ✅"); + + print!("Starting D-Bus UI -> Credential control service..."); + let ui_controller = UiControlServiceClient::new(dbus_client_conn); + let credential_service = CredentialService::new( + InternalHybridHandler::new(), + InProcessUsbHandler {}, + ui_controller, + ); + let (_flow_control_conn, initiator) = + dbus::start_flow_control_service(credential_service).await?; + println!(" ✅"); + + print!("Starting D-Bus public client service..."); + let initiator = CredentialRequestControllerClient { initiator }; + let _gateway_conn = dbus::start_gateway(initiator).await?; + println!(" ✅"); + + println!("Waiting for messages..."); + loop { + // wait forever, handle D-Bus in the background + std::future::pending::<()>().await; + } +} diff --git a/credsd/src/meson.build b/credsd/src/meson.build new file mode 100644 index 00000000..434fee12 --- /dev/null +++ b/credsd/src/meson.build @@ -0,0 +1,46 @@ +if get_option('profile') == 'default' + cargo_options += ['--release'] + rust_target = 'release' + message('Building in release mode') +else + rust_target = 'debug' + message('Building in debug mode') +endif + +cargo_env = ['CARGO_HOME=' + meson.project_build_root() / 'cargo-home'] +message('@0@'.format(cargo_options)) + +custom_target( + 'cargo-build', + build_by_default: true, + build_always_stale: true, + output: backend_executable_name, + console: true, + install: true, + install_dir: bindir, + command: [ + 'env', + cargo_env, + cargo, + 'build', + cargo_options, + '&&', + 'cp', + backend_build_dir / 'target' / rust_target / backend_executable_name, + '@OUTPUT@', + ], +) + +test( + 'cargo-unit-tests', + cargo, + env: [cargo_env], + args: [ + 'test', + '--bins', + '--no-fail-fast', cargo_options, + '--', + '--nocapture', + ], + protocol: 'exitcode', +) diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/serde/mod.rs b/credsd/src/serde/mod.rs similarity index 100% rename from xyz-iinuwa-credential-manager-portal-gtk/src/serde/mod.rs rename to credsd/src/serde/mod.rs diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/webauthn.rs b/credsd/src/webauthn.rs similarity index 95% rename from xyz-iinuwa-credential-manager-portal-gtk/src/webauthn.rs rename to credsd/src/webauthn.rs index 89d568df..ca5052a5 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/src/webauthn.rs +++ b/credsd/src/webauthn.rs @@ -4,16 +4,30 @@ use base64::{self, engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use libwebauthn::{ ops::webauthn::{CredentialProtectionPolicy, MakeCredentialLargeBlobExtension}, proto::ctap2::{ - Ctap2AttestationStatement, Ctap2CredentialType, Ctap2PublicKeyCredentialDescriptor, - Ctap2PublicKeyCredentialType, Ctap2Transport, + Ctap2AttestationStatement, Ctap2CredentialType, Ctap2PublicKeyCredentialType, + Ctap2Transport, }, }; +use ring::digest; use serde::{Deserialize, Serialize}; use serde_json::json; use tracing::debug; +use creds_lib::model::Operation; + use crate::cose::{CoseKeyAlgorithmIdentifier, CoseKeyType}; +pub use libwebauthn::ops::webauthn::{ + Assertion, CredentialProtectionExtension, GetAssertionHmacOrPrfInput, + GetAssertionLargeBlobExtension, GetAssertionRequest, GetAssertionRequestExtensions, + MakeCredentialHmacOrPrfInput, MakeCredentialRequest, MakeCredentialResponse, + MakeCredentialsRequestExtensions, ResidentKeyRequirement, UserVerificationRequirement, +}; +pub use libwebauthn::proto::ctap2::{ + Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialRpEntity, + Ctap2PublicKeyCredentialUserEntity, +}; + #[derive(Debug)] pub enum Error { Unknown, @@ -655,3 +669,23 @@ 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}}}") +} diff --git a/xyz-iinuwa-credential-manager-portal-gtk/tests/config/mod.rs.in b/credsd/tests/config/mod.rs.in similarity index 81% rename from xyz-iinuwa-credential-manager-portal-gtk/tests/config/mod.rs.in rename to credsd/tests/config/mod.rs.in index b4e7d961..aab84060 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/tests/config/mod.rs.in +++ b/credsd/tests/config/mod.rs.in @@ -1,4 +1,4 @@ pub const SERVICE_DIR: &'static str = @SERVICE_DIR@; -pub const SERVICE_NAME: &'static str = "xyz.iinuwa.credentials.CredentialManagerUi"; -pub const PATH: &'static str = "/xyz/iinuwa/credentials/CredentialManagerUi"; -pub const INTERFACE: &'static str = "xyz.iinuwa.credentials.CredentialManagerUi1"; +pub const SERVICE_NAME: &'static str = "xyz.iinuwa.credentials.Credentials"; +pub const PATH: &'static str = "/xyz/iinuwa/credentials/Credentials"; +pub const INTERFACE: &'static str = "xyz.iinuwa.credentials.Credentials1"; diff --git a/xyz-iinuwa-credential-manager-portal-gtk/tests/dbus.rs b/credsd/tests/dbus.rs similarity index 96% rename from xyz-iinuwa-credential-manager-portal-gtk/tests/dbus.rs rename to credsd/tests/dbus.rs index 2e7fe811..7dfc63e9 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/tests/dbus.rs +++ b/credsd/tests/dbus.rs @@ -20,7 +20,7 @@ fn test_client_capabilities() { let capabilities = HashMap::from([ ("conditionalCreate", false), ("conditionalGet", false), - ("hybridTransport", false), + ("hybridTransport", true), ("passkeyPlatformAuthenticator", false), ("userVerifyingPlatformAuthenticator", false), ("relatedOrigins", false), @@ -36,7 +36,7 @@ fn test_client_capabilities() { mod client { use crate::config::{INTERFACE, PATH, SERVICE_DIR, SERVICE_NAME}; - use gtk::gio::{TestDBus, TestDBusFlags}; + use gio::{TestDBus, TestDBusFlags}; use serde::Serialize; use zbus::{blocking::Connection, zvariant::DynamicType, Message}; diff --git a/xyz-iinuwa-credential-manager-portal-gtk/tests/meson.build b/credsd/tests/meson.build similarity index 55% rename from xyz-iinuwa-credential-manager-portal-gtk/tests/meson.build rename to credsd/tests/meson.build index cf289d90..48aeafda 100644 --- a/xyz-iinuwa-credential-manager-portal-gtk/tests/meson.build +++ b/credsd/tests/meson.build @@ -1,11 +1,11 @@ test_config = configuration_data() test_config.set_quoted( 'SERVICE_DIR', - meson.project_build_root() / backend_executable_name / 'tests', + meson.project_build_root() / meson.current_build_dir(), ) test_config.set( 'DBUS_EXECUTABLE', - meson.project_build_root() / backend_executable_name / 'src' / backend_executable_name, + meson.project_build_root() / backend_build_dir / 'target' / rust_target / backend_executable_name, ) configure_file( input: 'config' / 'mod.rs.in', @@ -16,14 +16,14 @@ configure_file( # Copy the config output to the source directory. run_command( 'cp', - meson.project_build_root() / backend_executable_name / 'tests' / 'config.rs', - meson.project_source_root() / backend_executable_name / 'tests' / 'config' / 'mod.rs', + meson.project_build_root() / meson.current_build_dir() / 'config.rs', + meson.project_source_root() / meson.current_source_dir() / 'config' / 'mod.rs', check: true, ) configure_file( - input: 'services' / 'xyz.iinuwa.CredentialManagerUi.service.in', - output: 'xyz.iinuwa.CredentialManagerUi.service', + input: 'services' / 'xyz.iinuwa.credentials.Credentials.service.in', + output: 'xyz.iinuwa.credentials.Credentials.service', configuration: test_config, ) @@ -40,4 +40,4 @@ test( ], protocol: 'exitcode', verbose: true, -) +) \ No newline at end of file diff --git a/credsd/tests/services/xyz.iinuwa.credentials.Credentials.service.in b/credsd/tests/services/xyz.iinuwa.credentials.Credentials.service.in new file mode 100644 index 00000000..a32a18aa --- /dev/null +++ b/credsd/tests/services/xyz.iinuwa.credentials.Credentials.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=xyz.iinuwa.credentials.Credentials +Exec=@DBUS_EXECUTABLE@ diff --git a/demo_client/main.py b/demo_client/main.py index aabda2ec..bfdad37c 100755 --- a/demo_client/main.py +++ b/demo_client/main.py @@ -14,18 +14,20 @@ import util import webauthn + async def run(cmd): bus = await MessageBus().connect() - with open('../contrib/xyz.iinuwa.credentials.CredentialManager.xml', 'r') as f: + with open("../contrib/xyz.iinuwa.credentials.CredentialManager.xml", "r") as f: introspection = f.read() - proxy_object = bus.get_proxy_object('xyz.iinuwa.credentials.CredentialManagerUi', - '/xyz/iinuwa/credentials/CredentialManagerUi', - introspection) + proxy_object = bus.get_proxy_object( + "xyz.iinuwa.credentials.Credentials", + "/xyz/iinuwa/credentials/Credentials", + introspection, + ) - interface = proxy_object.get_interface( - 'xyz.iinuwa.credentials.CredentialManagerUi1') + interface = proxy_object.get_interface("xyz.iinuwa.credentials.Credentials1") rp_id = "example.com" origin = "https://example.com" @@ -33,8 +35,10 @@ async def run(cmd): user_handle = b"123abdsacddw" username = "user@example.com" - if cmd == 'create': - auth_data = await create_passkey(interface, origin, top_origin, rp_id, user_handle, username) + if cmd == "create": + auth_data = await create_passkey( + interface, origin, top_origin, rp_id, user_handle, username + ) user_data = { "id": 1, "name": username, @@ -42,17 +46,19 @@ async def run(cmd): "cred_id": util.b64_encode(auth_data.cred_id), "pub_key": util.b64_encode(auth_data.pub_key_bytes), "sign_count": auth_data.sign_count, - "backup_eligible": auth_data.has_flag('BE'), - "backup_state": auth_data.has_flag('BS'), - "uv_initialized": auth_data.has_flag('UV'), + "backup_eligible": auth_data.has_flag("BE"), + "backup_state": auth_data.has_flag("BS"), + "uv_initialized": auth_data.has_flag("UV"), } print("New credential data:") print(json.dumps(user_data)) - json.dump(user_data, open('./user.json', 'w')) - elif cmd == 'get': - user_data = json.load(open('./user.json', 'r')) - cred_id = util.b64_decode(user_data['cred_id']) - auth_data = await get_passkey(interface, origin, top_origin, rp_id, cred_id, user_data) + json.dump(user_data, open("./user.json", "w")) + elif cmd == "get": + user_data = json.load(open("./user.json", "r")) + cred_id = util.b64_decode(user_data["cred_id"]) + auth_data = await get_passkey( + interface, origin, top_origin, rp_id, cred_id, user_data + ) print(auth_data) else: print(f"unknown cmd: {cmd}") @@ -66,12 +72,17 @@ async def run(cmd): async def create_password(interface): password_req = { - "type": Variant('s', "password"), - "password": Variant("a{sv}", { - "origin": Variant('s', "xyz.iinuwa.credentials.CredentialManager:local"), - "id": Variant('s', "test@example.com"), - "password": Variant('s', "abc123"), - }) + "type": Variant("s", "password"), + "password": Variant( + "a{sv}", + { + "origin": Variant( + "s", "xyz.iinuwa.credentials.CredentialManager:local" + ), + "id": Variant("s", "test@example.com"), + "password": Variant("s", "abc123"), + }, + ), } rsp = await interface.call_create_credential(password_req) return rsp @@ -80,18 +91,21 @@ async def create_password(interface): async def get_password(interface): password_req = { "origin": Variant("s", "xyz.iinuwa.credentials.CredentialManager:local"), - "options": Variant("aa{sv}", [ - { - "type": Variant("s", "password"), - "password": Variant("a{sv}", {}), - } - ]) + "options": Variant( + "aa{sv}", + [ + { + "type": Variant("s", "password"), + "password": Variant("a{sv}", {}), + } + ], + ), } rsp = await interface.call_get_credential(password_req) - if rsp['type'].value == 'password': - cred = rsp['password'].value - id = cred['id'].value - password = cred['password'].value + if rsp["type"].value == "password": + cred = rsp["password"].value + id = cred["id"].value + password = cred["password"].value return (id, password) return None @@ -116,31 +130,37 @@ async def create_passkey(interface, origin, top_origin, rp_id, user_handle, user ], } - print(f"Sending {'same' if is_same_origin else 'cross'}-origin request for {origin} using options:") + print( + f"Sending {'same' if is_same_origin else 'cross'}-origin request for {origin} using options:" + ) pprint(options) print() req_json = json.dumps(options) req = { - "type": Variant('s', "publicKey"), - "origin": Variant('s', origin), - "is_same_origin": Variant('b', is_same_origin), - "publicKey": Variant('a{sv}', { - "request_json": Variant('s', req_json) - }) + "type": Variant("s", "publicKey"), + "origin": Variant("s", origin), + "is_same_origin": Variant("b", is_same_origin), + "publicKey": Variant("a{sv}", {"request_json": Variant("s", req_json)}), } rsp = await interface.call_create_credential(req) print("Received response") pprint(rsp) - if rsp['type'].value != 'public-key': - raise Exception(f"Invalid credential type received: expected 'public-key', received {rsp['type'.value]}") - - response_json = json.loads(rsp['public_key'].value['registration_response_json'].value) + if rsp["type"].value != "public-key": + raise Exception( + f"Invalid credential type received: expected 'public-key', received {rsp['type'.value]}" + ) + + response_json = json.loads( + rsp["public_key"].value["registration_response_json"].value + ) return webauthn.verify_create_response(response_json, options, origin) -async def get_passkey(interface, origin, top_origin, rp_id, cred_id, user: Optional[dict]): +async def get_passkey( + interface, origin, top_origin, rp_id, cred_id, user: Optional[dict] +): is_same_origin = origin == top_origin options = { "challenge": util.b64_encode(secrets.token_bytes(16)), @@ -150,27 +170,31 @@ async def get_passkey(interface, origin, top_origin, rp_id, cred_id, user: Optio ], } - print(f"Sending {'same' if is_same_origin else 'cross'}-origin request for {origin} using options:") + print( + f"Sending {'same' if is_same_origin else 'cross'}-origin request for {origin} using options:" + ) pprint(options) print() req_json = json.dumps(options) req = { - "type": Variant('s', "publicKey"), - "origin": Variant('s', origin), - "is_same_origin": Variant('b', is_same_origin), - "publicKey": Variant('a{sv}', { - "request_json": Variant('s', req_json) - }) + "type": Variant("s", "publicKey"), + "origin": Variant("s", origin), + "is_same_origin": Variant("b", is_same_origin), + "publicKey": Variant("a{sv}", {"request_json": Variant("s", req_json)}), } rsp = await interface.call_get_credential(req) print("Received response") pprint(rsp) - if rsp['type'].value != 'public-key': - raise Exception(f"Invalid credential type received: expected 'public-key', received {rsp['type'.value]}") - - response_json = json.loads(rsp['public_key'].value['authentication_response_json'].value) + if rsp["type"].value != "public-key": + raise Exception( + f"Invalid credential type received: expected 'public-key', received {rsp['type'.value]}" + ) + + response_json = json.loads( + rsp["public_key"].value["authentication_response_json"].value + ) print(user) return webauthn.verify_get_response(response_json, options, origin, user, None) @@ -188,57 +212,45 @@ def main(): if __name__ == "__main__": main() + class VerificationTests(unittest.TestCase): def test_create_credential(self): response = { - 'id': 'owBYoufBWYDUOeNB9dZs9x6GlEPiS8ziKnI_9YVq9RpkwwYsxelm66HOP2usfy-SaV8NE5nJVWDIMvS0W-x9BYtN4AmHZVY33GW2rdfLpeBruuh4jDXgYdnHtZC0IyDIKZiOTzSzyoQih8F-VLcTmqQl7SVHgf-xAh-6TxAJMccROZyIsili1OOnv3WSE7374c2Sw9At0ILaSiTmvC7MtZfnj9hhnAFMFobCJvainepVBn3HAlDo22486wkPqW2D5N00XYXK', - 'rawId': 'owBYoufBWYDUOeNB9dZs9x6GlEPiS8ziKnI_9YVq9RpkwwYsxelm66HOP2usfy-SaV8NE5nJVWDIMvS0W-x9BYtN4AmHZVY33GW2rdfLpeBruuh4jDXgYdnHtZC0IyDIKZiOTzSzyoQih8F-VLcTmqQl7SVHgf-xAh-6TxAJMccROZyIsili1OOnv3WSE7374c2Sw9At0ILaSiTmvC7MtZfnj9hhnAFMFobCJvainepVBn3HAlDo22486wkPqW2D5N00XYXK', - 'response': { - 'attestationObject': 'o2NmbXRmcGFja2VkZ2F0dFN0bXSjY2FsZyZjc2lnWEcwRQIgIQ1ReuY8bt2QPrmsZGqphT3hwTJ4Ar2zd3RevRXelHYCIQDiSKGGo5mUqsWP43B6TgxcWby0M1ucBkwOQTS4E6Dt-mN4NWOBWQKqMIICpjCCAkygAwIBAgIUfWe3F4mJfmOVopPF8mmAKxBb0igwCgYIKoZIzj0EAwIwLTERMA8GA1UECgwIU29sb0tleXMxCzAJBgNVBAYTAkNIMQswCQYDVQQDDAJGMTAgFw0yMTA1MjMwMDUyMDBaGA8yMDcxMDUxMTAwNTIwMFowgYMxCzAJBgNVBAYTAlVTMREwDwYDVQQKDAhTb2xvS2V5czEiMCAGA1UECwwZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjE9MDsGA1UEAww0U29sbyAyIE5GQytVU0ItQSA4NjUyQUJFOUZCRDg0ODEwQTg0MEQ2RkM0NDJBOEMyQyBCMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABArSyTVT7sDxX0rom6XoIcg8qwMStGV3SjoGRNMqHBSAh2sr4EllUzA1F8yEX5XvUPN_M6DQlqEFGw18UodOjBqjgfAwge0wHQYDVR0OBBYEFBiTdxTWyNCRuzSieBflmHPSJbS1MB8GA1UdIwQYMBaAFEFrtkvvohkN5GJf_SkElrmCKbT4MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgTwMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL2kuczJwa2kubmV0L2YxLzAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8vYy5zMnBraS5uZXQvcjEvMCEGCysGAQQBguUcAQEEBBIEEIZSq-n72EgQqEDW_EQqjCwwEwYLKwYBBAGC5RwCAQEEBAMCBDAwCgYIKoZIzj0EAwIDSAAwRQIgMsLnUg5Px2FehxIUNiaey8qeT1FGtlJ1s3LEUGOks-8CIQDNEv5aupDvYxn2iqWSNysv4qpdoqSMytRQ7ctfuJDWN2hhdXRoRGF0YVkBJ6N5pvbur7mlXjeMEYA04nUeaC-rny0wqxPSElWGzhlHRQAAADmGUqvp-9hIEKhA1vxEKowsAMajAFii58FZgNQ540H11mz3HoaUQ-JLzOIqcj_1hWr1GmTDBizF6Wbroc4_a6x_L5JpXw0TmclVYMgy9LRb7H0Fi03gCYdlVjfcZbat18ul4Gu66HiMNeBh2ce1kLQjIMgpmI5PNLPKhCKHwX5UtxOapCXtJUeB_7ECH7pPEAkxxxE5nIiyKWLU46e_dZITvfvhzZLD0C3QgtpKJOa8Lsy1l-eP2GGcAUwWhsIm9qKd6lUGfccCUOjbbjzrCQ-pbYPk3TRdhcqkAQEDJyAGIVggzFQIxv1GYCb7CZXbKR8VRTWiRCbceHYcsBNx-lOg9Xk', - 'clientDataJSON': 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiai1keUY4WGN3NWxZMllGSjI2MHl3ZyIsIm9yaWdpbiI6Inh5ei5paW51d2EuY3JlZGVudGlhbHMuQ3JlZGVudGlhbE1hbmFnZXI6bG9jYWwiLCJjcm9zc09yaWdpbiI6dHJ1ZX0', - 'transports': ['usb'] - } + "id": "owBYoufBWYDUOeNB9dZs9x6GlEPiS8ziKnI_9YVq9RpkwwYsxelm66HOP2usfy-SaV8NE5nJVWDIMvS0W-x9BYtN4AmHZVY33GW2rdfLpeBruuh4jDXgYdnHtZC0IyDIKZiOTzSzyoQih8F-VLcTmqQl7SVHgf-xAh-6TxAJMccROZyIsili1OOnv3WSE7374c2Sw9At0ILaSiTmvC7MtZfnj9hhnAFMFobCJvainepVBn3HAlDo22486wkPqW2D5N00XYXK", + "rawId": "owBYoufBWYDUOeNB9dZs9x6GlEPiS8ziKnI_9YVq9RpkwwYsxelm66HOP2usfy-SaV8NE5nJVWDIMvS0W-x9BYtN4AmHZVY33GW2rdfLpeBruuh4jDXgYdnHtZC0IyDIKZiOTzSzyoQih8F-VLcTmqQl7SVHgf-xAh-6TxAJMccROZyIsili1OOnv3WSE7374c2Sw9At0ILaSiTmvC7MtZfnj9hhnAFMFobCJvainepVBn3HAlDo22486wkPqW2D5N00XYXK", + "response": { + "attestationObject": "o2NmbXRmcGFja2VkZ2F0dFN0bXSjY2FsZyZjc2lnWEcwRQIgIQ1ReuY8bt2QPrmsZGqphT3hwTJ4Ar2zd3RevRXelHYCIQDiSKGGo5mUqsWP43B6TgxcWby0M1ucBkwOQTS4E6Dt-mN4NWOBWQKqMIICpjCCAkygAwIBAgIUfWe3F4mJfmOVopPF8mmAKxBb0igwCgYIKoZIzj0EAwIwLTERMA8GA1UECgwIU29sb0tleXMxCzAJBgNVBAYTAkNIMQswCQYDVQQDDAJGMTAgFw0yMTA1MjMwMDUyMDBaGA8yMDcxMDUxMTAwNTIwMFowgYMxCzAJBgNVBAYTAlVTMREwDwYDVQQKDAhTb2xvS2V5czEiMCAGA1UECwwZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjE9MDsGA1UEAww0U29sbyAyIE5GQytVU0ItQSA4NjUyQUJFOUZCRDg0ODEwQTg0MEQ2RkM0NDJBOEMyQyBCMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABArSyTVT7sDxX0rom6XoIcg8qwMStGV3SjoGRNMqHBSAh2sr4EllUzA1F8yEX5XvUPN_M6DQlqEFGw18UodOjBqjgfAwge0wHQYDVR0OBBYEFBiTdxTWyNCRuzSieBflmHPSJbS1MB8GA1UdIwQYMBaAFEFrtkvvohkN5GJf_SkElrmCKbT4MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgTwMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL2kuczJwa2kubmV0L2YxLzAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8vYy5zMnBraS5uZXQvcjEvMCEGCysGAQQBguUcAQEEBBIEEIZSq-n72EgQqEDW_EQqjCwwEwYLKwYBBAGC5RwCAQEEBAMCBDAwCgYIKoZIzj0EAwIDSAAwRQIgMsLnUg5Px2FehxIUNiaey8qeT1FGtlJ1s3LEUGOks-8CIQDNEv5aupDvYxn2iqWSNysv4qpdoqSMytRQ7ctfuJDWN2hhdXRoRGF0YVkBJ6N5pvbur7mlXjeMEYA04nUeaC-rny0wqxPSElWGzhlHRQAAADmGUqvp-9hIEKhA1vxEKowsAMajAFii58FZgNQ540H11mz3HoaUQ-JLzOIqcj_1hWr1GmTDBizF6Wbroc4_a6x_L5JpXw0TmclVYMgy9LRb7H0Fi03gCYdlVjfcZbat18ul4Gu66HiMNeBh2ce1kLQjIMgpmI5PNLPKhCKHwX5UtxOapCXtJUeB_7ECH7pPEAkxxxE5nIiyKWLU46e_dZITvfvhzZLD0C3QgtpKJOa8Lsy1l-eP2GGcAUwWhsIm9qKd6lUGfccCUOjbbjzrCQ-pbYPk3TRdhcqkAQEDJyAGIVggzFQIxv1GYCb7CZXbKR8VRTWiRCbceHYcsBNx-lOg9Xk", + "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiai1keUY4WGN3NWxZMllGSjI2MHl3ZyIsIm9yaWdpbiI6Inh5ei5paW51d2EuY3JlZGVudGlhbHMuQ3JlZGVudGlhbE1hbmFnZXI6bG9jYWwiLCJjcm9zc09yaWdpbiI6dHJ1ZX0", + "transports": ["usb"], + }, } - challenge = 'j-dyF8Xcw5lY2YFJ260ywg' + challenge = "j-dyF8Xcw5lY2YFJ260ywg" create_options = { - 'challenge': challenge, - 'rp': { - 'id': 'example.com' - }, - 'authenticatorSelection': { - 'userVerification': 'required' - }, - 'pubKeyCredParams': [ - { - "type": "public-key", - "alg": -8 - }, - { - "type": "public-key", - "alg": -7 - }, - { - "type": "public-key", - "alg": -257 - } - ] + "challenge": challenge, + "rp": {"id": "example.com"}, + "authenticatorSelection": {"userVerification": "required"}, + "pubKeyCredParams": [ + {"type": "public-key", "alg": -8}, + {"type": "public-key", "alg": -7}, + {"type": "public-key", "alg": -257}, + ], } - origin = "xyz.iinuwa.credentials.CredentialManager:local" + origin = "xyz.iinuwa.credentials.CredentialManager:local" auth_data = webauthn.verify_create_response(response, create_options, origin) - self.assertEqual(response['id'], util.b64_encode(auth_data.cred_id)) + self.assertEqual(response["id"], util.b64_encode(auth_data.cred_id)) def test_get_credential(self): response = { - "authenticatorAttachment":"cross-platform", - "id":"owBYojOVzZU-pjscj82gQAHvhUDTMgzQtTcQjyBpzHT-bqLwtLF2OOJDoskE18lOn2-1-SV-b7nCvn5s5Uq2KhBt1Q9kFVBUsb8jBl959BY3KWTg2rgjpN9nB5uIWTEFXfAWo0qIYGGVhXLyEbvu72Lq_W0wlccoKlxWrP349qN9OG2RTaGrgNjxTo1LqnSVc9S6D1zD7mop5KQ_9FZEjA5jABAquwFMAuO4ongyujnpoAfyAlB6UZ_JDmDFCkuN598q_LAu", - "rawId":"owBYojOVzZU-pjscj82gQAHvhUDTMgzQtTcQjyBpzHT-bqLwtLF2OOJDoskE18lOn2-1-SV-b7nCvn5s5Uq2KhBt1Q9kFVBUsb8jBl959BY3KWTg2rgjpN9nB5uIWTEFXfAWo0qIYGGVhXLyEbvu72Lq_W0wlccoKlxWrP349qN9OG2RTaGrgNjxTo1LqnSVc9S6D1zD7mop5KQ_9FZEjA5jABAquwFMAuO4ongyujnpoAfyAlB6UZ_JDmDFCkuN598q_LAu", + "authenticatorAttachment": "cross-platform", + "id": "owBYojOVzZU-pjscj82gQAHvhUDTMgzQtTcQjyBpzHT-bqLwtLF2OOJDoskE18lOn2-1-SV-b7nCvn5s5Uq2KhBt1Q9kFVBUsb8jBl959BY3KWTg2rgjpN9nB5uIWTEFXfAWo0qIYGGVhXLyEbvu72Lq_W0wlccoKlxWrP349qN9OG2RTaGrgNjxTo1LqnSVc9S6D1zD7mop5KQ_9FZEjA5jABAquwFMAuO4ongyujnpoAfyAlB6UZ_JDmDFCkuN598q_LAu", + "rawId": "owBYojOVzZU-pjscj82gQAHvhUDTMgzQtTcQjyBpzHT-bqLwtLF2OOJDoskE18lOn2-1-SV-b7nCvn5s5Uq2KhBt1Q9kFVBUsb8jBl959BY3KWTg2rgjpN9nB5uIWTEFXfAWo0qIYGGVhXLyEbvu72Lq_W0wlccoKlxWrP349qN9OG2RTaGrgNjxTo1LqnSVc9S6D1zD7mop5KQ_9FZEjA5jABAquwFMAuO4ongyujnpoAfyAlB6UZ_JDmDFCkuN598q_LAu", "response": { - "authenticatorData":"o3mm9u6vuaVeN4wRgDTidR5oL6ufLTCrE9ISVYbOGUcFAAAAXA", - "clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiWjE2T2hrVlB5d245Mjc2SjZ3dEdmZyIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJjcm9zc09yaWdpbiI6dHJ1ZX0", - "signature":"9frQigpe0p8NGwWc9Ikve9RlOZbcmz6S-JVDaPde-dxS-sPRFLGDA3ekh0j294MqaejRudzTw5uggh1IU2lJCQ", - "userHandle": None - } + "authenticatorData": "o3mm9u6vuaVeN4wRgDTidR5oL6ufLTCrE9ISVYbOGUcFAAAAXA", + "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiWjE2T2hrVlB5d245Mjc2SjZ3dEdmZyIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJjcm9zc09yaWdpbiI6dHJ1ZX0", + "signature": "9frQigpe0p8NGwWc9Ikve9RlOZbcmz6S-JVDaPde-dxS-sPRFLGDA3ekh0j294MqaejRudzTw5uggh1IU2lJCQ", + "userHandle": None, + }, } user = { @@ -253,25 +265,29 @@ def test_get_credential(self): "uv_initialized": True, } options = { - 'challenge': "Z16OhkVPywn9276J6wtGfg", - 'rpId': 'example.com', + "challenge": "Z16OhkVPywn9276J6wtGfg", + "rpId": "example.com", "allowCredentials": [ { "type": "public-key", - "id": ("owBYojOVzZU-pjscj82gQAHvhUDTMgzQtTcQjyBpzHT-bqLwtLF2OOJDoskE18lO" - "n2-1-SV-b7nCvn5s5Uq2KhBt1Q9kFVBUsb8jBl959BY3KWTg2rgjpN9nB5uIWTEF" - "XfAWo0qIYGGVhXLyEbvu72Lq_W0wlccoKlxWrP349qN9OG2RTaGrgNjxTo1LqnSV" - "c9S6D1zD7mop5KQ_9FZEjA5jABAquwFMAuO4ongyujnpoAfyAlB6UZ_JDmDFCkuN" - "598q_LAu") + "id": ( + "owBYojOVzZU-pjscj82gQAHvhUDTMgzQtTcQjyBpzHT-bqLwtLF2OOJDoskE18lO" + "n2-1-SV-b7nCvn5s5Uq2KhBt1Q9kFVBUsb8jBl959BY3KWTg2rgjpN9nB5uIWTEF" + "XfAWo0qIYGGVhXLyEbvu72Lq_W0wlccoKlxWrP349qN9OG2RTaGrgNjxTo1LqnSV" + "c9S6D1zD7mop5KQ_9FZEjA5jABAquwFMAuO4ongyujnpoAfyAlB6UZ_JDmDFCkuN" + "598q_LAu" + ), }, ], } - expected_origin = 'https://example.com' + expected_origin = "https://example.com" - auth_data = webauthn.verify_get_response(response, options, "https://example.com", user, None) - self.assertTrue(auth_data.has_flag('UV')) - self.assertFalse(auth_data.has_flag('BS')) - self.assertTrue(auth_data.sign_count > user['sign_count']) + auth_data = webauthn.verify_get_response( + response, options, "https://example.com", user, None + ) + self.assertTrue(auth_data.has_flag("UV")) + self.assertFalse(auth_data.has_flag("BS")) + self.assertTrue(auth_data.sign_count > user["sign_count"]) def test_create_u2f_credential(self): response = { @@ -285,42 +301,33 @@ def test_create_u2f_credential(self): "transports": ["usb"], "authenticatorData": "xGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v-ppaZJdA7dBAAAAAAAAAAAAAAAAAAAAAAAAAAAAYPGdjuzMVoVnLKvs08iEfTsqDQNaH5yP84sva5WKMzF3AErD10K6lVKV49Z6ZM1jiJOsjoII4n06ppkfWQONJvujW0vN_XAsBqE9nFZpOKdzlj5SYoX95lJ0psxRe6RtoqUBAgMmIAEhWCBCRSlLPOaY_h1eAuKoX-k_bX9Vxmnrt_08jiGRbws9hiJYIJG3xgod6abzVyI9E6QioArtDscVBH56lABGF8ojHxov", "publicKey": "pQECAyYgASFYIEJFKUs85pj-HV4C4qhf6T9tf1XGaeu3_TyOIZFvCz2GIlggkbfGCh3ppvNXIj0TpCKgCu0OxxUEfnqUAEYXyiMfGi8", - "publicKeyAlgorithm": -7 - } + "publicKeyAlgorithm": -7, + }, } - challenge = 'LSwXTIRzLFsL3iSrg7gj0rolzVxlUJ1GwCqD0tu6rxk' + challenge = "LSwXTIRzLFsL3iSrg7gj0rolzVxlUJ1GwCqD0tu6rxk" create_options = { "attestation": "direct", "authenticatorSelection": { "authenticatorAttachment": "cross-platform", "requireResidentKey": False, "residentKey": "discouraged", - "userVerification": "discouraged" + "userVerification": "discouraged", }, "challenge": challenge, "excludeCredentials": [], "pubKeyCredParams": [ - { - "alg": -7, - "type": "public-key" - }, - { - "alg": -257, - "type": "public-key" - } + {"alg": -7, "type": "public-key"}, + {"alg": -257, "type": "public-key"}, ], - "rp": { - "id": "demo.yubico.com", - "name": "Yubico Demo" - }, + "rp": {"id": "demo.yubico.com", "name": "Yubico Demo"}, "timeout": 600000, "user": { "displayName": "qwelvy", "id": "NfF0j0oEdzdAkGD1kxrQCzw-X6ryVIpcAISt8RoToxU", - "name": "qwelvy" - } + "name": "qwelvy", + }, } origin = "https://demo.yubico.com" auth_data = webauthn.verify_create_response(response, create_options, origin) - self.assertEqual(response['id'], util.b64_encode(auth_data.cred_id)) + self.assertEqual(response["id"], util.b64_encode(auth_data.cred_id)) diff --git a/meson.build b/meson.build index b6f9f0ac..83848874 100644 --- a/meson.build +++ b/meson.build @@ -14,8 +14,12 @@ localedir = prefix / get_option('localedir') meson.add_dist_script( 'build-aux/dist-vendor.sh', - meson.project_build_root() / 'meson-dist' / meson.project_name() + '-' + version, - meson.project_source_root() + meson.project_build_root() / 'meson-dist' / meson.project_name() + + '-' + + version, + meson.project_source_root(), ) -subdir('xyz-iinuwa-credential-manager-portal-gtk') +subdir('creds-lib') +subdir('credsd') +subdir('creds-ui') diff --git a/webext/README.md b/webext/README.md index 2c9e57d1..a705239f 100644 --- a/webext/README.md +++ b/webext/README.md @@ -5,10 +5,49 @@ Currently, this is written only for Firefox; there will be some slight API tweak This requires some setup to make it work: -1. Copy `app/credential_manager_shim.json` to `~/.mozilla/native-messaging-hosts/credential_manager_shim.json`. -2. In the copied file, replace the `path` key with the absolute path to `app/credential_manager_shim.py` +# Prerequisites + +Currently, this web extension relies on the `dbus-next` to interact with D-Bus +services. If you have that package installed in your system Python, this +should work. You can test using the following: + +```shell +python3 -c 'import dbus_next; print("dbus-next is installed")' +``` + +If that completes without error, then you're good to go. Otherwise, you have a +couple of options: + +- Install the system package for your operating system, for example: + ```shell + # Fedora + dnf install python3-dbus-next + # Debian/Ubuntu + apt install python3-dbus-next + # Arch + pacman -S python-dbus-next + ``` +- Modify the shebang to point to a Python instance that does have the package installed. + ```shell + cd webext/ + python3 -m venv env + source ./env/bin/activate + pip3 install dbus-next + echo "Change the first line in webext/app/credential_manager_shim.py to:" + echo "#!$(readlink -f ./env/bin/python3)" + # Update the shebang to point to the absolute path to webext/env/bin/python3 + ``` + +# Setup Instructions + +(Note: Paths are relative to root of this repository) + +1. Copy `webext/app/credential_manager_shim.json` to `~/.mozilla/native-messaging-hosts/credential_manager_shim.json`. +2. In the copied file, replace the `path` key with the absolute path to `webext/app/credential_manager_shim.py` 3. Open Firefox and go to `about:debugging` -4. Click "This Firefox" > Load Temporary Extension. Select `add-on/manifest.json` -6. Build and run the `xyz-iinuwa-credential-manager-portal-gtk` binary to start the D-Bus service. -5. Navigate to [https://webauthn.io](). -6. Run through the registration and creation process. +4. Click "This Firefox" > Load Temporary Extension. Select `webext/add-on/manifest.json` +5. Build with `ninja -C ./build` and run the following binaries binary to start the D-Bus services. + - `GSCHEMA_SCHEMA_DIR=build/creds-ui/data ./build/creds-ui/target/debug/creds-ui` + - `./build/credsd/target/debug/credsd` +6. Navigate to [https://webauthn.io](). +7. Run through the registration and creation process. diff --git a/webext/app/credential_manager_shim.py b/webext/app/credential_manager_shim.py index 109cc552..c20b6235 100755 --- a/webext/app/credential_manager_shim.py +++ b/webext/app/credential_manager_shim.py @@ -337,12 +337,13 @@ async def run(cmd, options, origin, top_origin): with open('../../contrib/xyz.iinuwa.credentials.CredentialManager.xml', 'r') as f: introspection = f.read() - proxy_object = bus.get_proxy_object('xyz.iinuwa.credentials.CredentialManagerUi', - '/xyz/iinuwa/credentials/CredentialManagerUi', - introspection) + proxy_object = bus.get_proxy_object( + "xyz.iinuwa.credentials.Credentials", + "/xyz/iinuwa/credentials/Credentials", + introspection, + ) - interface = proxy_object.get_interface( - 'xyz.iinuwa.credentials.CredentialManagerUi1') + interface = proxy_object.get_interface("xyz.iinuwa.credentials.Credentials1") logging.debug(f"Connected to interface at {interface.path}") if cmd == 'create': diff --git a/xyz-iinuwa-credential-manager-portal-gtk/.gitignore b/xyz-iinuwa-credential-manager-portal-gtk/.gitignore deleted file mode 100644 index 95d99eff..00000000 --- a/xyz-iinuwa-credential-manager-portal-gtk/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -src/config.rs -tests/config/mod.rs \ No newline at end of file diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/credential_service/server.rs b/xyz-iinuwa-credential-manager-portal-gtk/src/credential_service/server.rs deleted file mode 100644 index ceab9ea1..00000000 --- a/xyz-iinuwa-credential-manager-portal-gtk/src/credential_service/server.rs +++ /dev/null @@ -1,275 +0,0 @@ -use std::fmt::Debug; -use std::future::Future; -use std::pin::Pin; -use std::sync::Arc; - -use futures_lite::Stream; -use tokio::sync::{mpsc, oneshot}; - -use crate::dbus::{CredentialRequest, CredentialResponse}; -use crate::gui::view_model::Device; - -use super::hybrid::{HybridHandler, HybridState}; -use super::usb::{UsbHandler, UsbState}; -use super::CredentialService; - -#[allow(clippy::enum_variant_names)] -pub enum ServiceRequest { - GetDevices, - GetHybridCredential, - GetUsbCredential, -} - -enum ManagementRequest { - InitRequest(Box), - CompleteAuth, -} - -#[derive(Debug)] -enum ManagementResponse { - InitRequest(Result<(), String>), - CompleteAuth(Option), -} - -// Clippy complains that these variant names have the same prefix, but that's -// intentional for now. -#[allow(clippy::enum_variant_names)] -pub enum ServiceResponse { - GetDevices(Vec), - GetHybridCredential(Pin + Send>>), - GetUsbCredential(Pin + Send>>), -} - -impl Debug for ServiceResponse { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::GetDevices(arg0) => f.debug_tuple("GetDevices").field(arg0).finish(), - Self::GetHybridCredential(_) => f - .debug_tuple("GetHybridCredential") - .field(&String::from("")) - .finish(), - Self::GetUsbCredential(_) => f - .debug_tuple("GetUsbCredential") - .field(&String::from("")) - .finish(), - } - } -} - -enum InProcessServerRequest { - Client(ServiceRequest), - Management(ManagementRequest), -} - -#[derive(Debug)] -enum InProcessServerResponse { - Client(ServiceResponse), - Management(ManagementResponse), -} - -pub trait CredentialServiceClient { - fn get_available_public_key_devices( - &self, - ) -> impl Future, ()>> + Send; - - fn get_hybrid_credential( - &self, - ) -> impl Future + Send>>> + Send; - fn get_usb_credential( - &self, - ) -> impl Future + Send>>> + Send; -} - -pub trait CredentialManagementClient { - fn init_request( - &self, - cred_request: CredentialRequest, - ) -> impl Future> + Send; - fn complete_auth(&self) -> impl Future> + Send; -} - -pub struct InProcessManager { - tx: mpsc::Sender<( - InProcessServerRequest, - oneshot::Sender, - )>, -} - -impl InProcessManager { - async fn send(&self, request: ManagementRequest) -> Result { - let (response_tx, response_rx) = oneshot::channel(); - self.tx - .send((InProcessServerRequest::Management(request), response_tx)) - .await - .unwrap(); - match response_rx.await { - Ok(InProcessServerResponse::Management(response)) => Ok(response), - Ok(_) => { - tracing::error!("invalid response received from server"); - Err(()) - } - Err(err) => { - tracing::error!("Failed to retrieve response from server: {:?}", err); - Err(()) - } - } - } -} - -impl CredentialManagementClient for InProcessManager { - async fn init_request(&self, cred_request: CredentialRequest) -> Result<(), String> { - let response = self - .send(ManagementRequest::InitRequest(Box::new(cred_request))) - .await - .unwrap(); - if let ManagementResponse::InitRequest(result) = response { - result - } else { - Err("No credentials in credential service".to_string()) - } - } - - async fn complete_auth(&self) -> Result { - let response = self.send(ManagementRequest::CompleteAuth).await.unwrap(); - if let ManagementResponse::CompleteAuth(Some(cred_response)) = response { - Ok(cred_response) - } else { - Err("No credentials in credential service".to_string()) - } - } -} - -pub struct InProcessClient { - tx: mpsc::Sender<( - InProcessServerRequest, - oneshot::Sender, - )>, -} - -impl InProcessClient { - async fn send(&self, request: ServiceRequest) -> Result { - let (response_tx, response_rx) = oneshot::channel(); - self.tx - .send((InProcessServerRequest::Client(request), response_tx)) - .await - .unwrap(); - match response_rx.await { - Ok(InProcessServerResponse::Client(response)) => Ok(response), - Ok(_) => { - tracing::error!("invalid response received from server"); - Err(()) - } - Err(err) => { - tracing::error!("Failed to retrieve response from server: {:?}", err); - Err(()) - } - } - } -} - -impl CredentialServiceClient for InProcessClient { - async fn get_available_public_key_devices(&self) -> Result, ()> { - let response = self.send(ServiceRequest::GetDevices).await.unwrap(); - if let ServiceResponse::GetDevices(devices) = response { - Ok(devices) - } else { - Err(()) - } - } - - async fn get_hybrid_credential(&self) -> Pin + Send>> { - let response = self - .send(ServiceRequest::GetHybridCredential) - .await - .unwrap(); - if let ServiceResponse::GetHybridCredential(stream) = response { - stream - } else { - panic!("Unable to get hybrid credential"); - } - } - - async fn get_usb_credential(&self) -> Pin + Send>> { - let response = self.send(ServiceRequest::GetUsbCredential).await.unwrap(); - if let ServiceResponse::GetUsbCredential(stream) = response { - stream - } else { - panic!("Unable to get usb credential"); - } - } -} - -impl CredentialServiceClient for Arc { - fn get_available_public_key_devices(&self) -> impl Future, ()>> { - InProcessClient::get_available_public_key_devices(self) - } - - fn get_hybrid_credential( - &self, - ) -> impl Future + Send>>> { - InProcessClient::get_hybrid_credential(self) - } - - fn get_usb_credential( - &self, - ) -> impl Future + Send>>> { - InProcessClient::get_usb_credential(self) - } -} - -#[derive(Debug)] -pub struct InProcessServer -where - H: HybridHandler + Debug, - U: UsbHandler + Debug, -{ - svc: CredentialService, - rx: mpsc::Receiver<( - InProcessServerRequest, - oneshot::Sender, - )>, -} - -impl InProcessServer -where - H: HybridHandler + Debug, - U: UsbHandler + Debug, -{ - pub fn new(svc: CredentialService) -> (Self, InProcessManager, InProcessClient) { - let (tx, rx) = mpsc::channel(256); - - let mgr_tx = tx.clone(); - let mgr = InProcessManager { tx: mgr_tx }; - let client_tx = tx.clone(); - let client = InProcessClient { tx: client_tx }; - (Self { svc, rx }, mgr, client) - } - - pub async fn run(&mut self) { - while let Some((request, tx)) = self.rx.recv().await { - let response = match request { - InProcessServerRequest::Client(ServiceRequest::GetDevices) => { - let rsp = self.svc.get_available_public_key_devices().await.unwrap(); - InProcessServerResponse::Client(ServiceResponse::GetDevices(rsp)) - } - InProcessServerRequest::Client(ServiceRequest::GetHybridCredential) => { - let rsp = self.svc.get_hybrid_credential(); - InProcessServerResponse::Client(ServiceResponse::GetHybridCredential(rsp)) - } - InProcessServerRequest::Client(ServiceRequest::GetUsbCredential) => { - let rsp = self.svc.get_usb_credential(); - InProcessServerResponse::Client(ServiceResponse::GetUsbCredential(rsp)) - } - InProcessServerRequest::Management(ManagementRequest::InitRequest(request)) => { - let rsp = self.svc.init_request(&request); - InProcessServerResponse::Management(ManagementResponse::InitRequest(rsp)) - } - InProcessServerRequest::Management(ManagementRequest::CompleteAuth) => { - let rsp = self.svc.complete_auth(); - InProcessServerResponse::Management(ManagementResponse::CompleteAuth(rsp)) - } - }; - tx.send(response).unwrap() - } - } -} diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/dbus.rs b/xyz-iinuwa-credential-manager-portal-gtk/src/dbus.rs deleted file mode 100644 index 539d2a6d..00000000 --- a/xyz-iinuwa-credential-manager-portal-gtk/src/dbus.rs +++ /dev/null @@ -1,785 +0,0 @@ -use std::collections::HashMap; -use std::sync::Arc; -use std::time::Duration; - -use base64::Engine; -use base64::{self, engine::general_purpose::URL_SAFE_NO_PAD}; - -use libwebauthn::ops::webauthn::{ - Assertion, CredentialProtectionExtension, GetAssertionHmacOrPrfInput, - GetAssertionLargeBlobExtension, GetAssertionRequest, GetAssertionRequestExtensions, - MakeCredentialHmacOrPrfInput, MakeCredentialRequest, MakeCredentialResponse, - MakeCredentialsRequestExtensions, ResidentKeyRequirement, UserVerificationRequirement, -}; -use libwebauthn::proto::ctap2::{ - Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialRpEntity, - Ctap2PublicKeyCredentialUserEntity, -}; -use ring::digest; -use tokio::sync::Mutex as AsyncMutex; -use zbus::{ - connection::{self, Connection}, - fdo, interface, - zvariant::{DeserializeDict, SerializeDict, Type}, - Result, -}; - -use crate::credential_service::CredentialManagementClient; -use crate::gui::{ - view_model::{CredentialType, Operation}, - ViewRequest, -}; -use crate::webauthn::{ - self, GetPublicKeyCredentialUnsignedExtensionsResponse, PublicKeyCredentialParameters, -}; - -pub(crate) async fn start_service( - service_name: &str, - path: &str, - gui_tx: async_std::channel::Sender, - manager_client: C, -) -> Result { - let lock: Arc>> = - Arc::new(AsyncMutex::new(gui_tx)); - connection::Builder::session()? - .name(service_name)? - .serve_at( - path, - CredentialManager { - app_lock: lock, - manager_client, - }, - )? - .build() - .await -} - -struct CredentialManager { - app_lock: Arc>>, - manager_client: C, -} - -#[interface(name = "xyz.iinuwa.credentials.CredentialManagerUi1")] -impl CredentialManager { - async fn create_credential( - &self, - request: CreateCredentialRequest, - ) -> fdo::Result { - if let Ok(tx) = self.app_lock.try_lock() { - if request.origin.is_none() { - todo!("Implicit caller-origin binding not yet implemented.") - }; - let is_same_origin = request.is_same_origin.unwrap_or(false); - let response = match (request.r#type.as_ref(), &request.public_key) { - ("publicKey", Some(_)) => { - if !is_same_origin { - return Err(fdo::Error::AccessDenied(String::from( - "Cross-origin public-key credentials are not allowed.", - ))); - } - let (make_cred_request, client_data_json) = - request.clone().try_into_ctap2_request().map_err(|e| { - fdo::Error::Failed(format!( - "Could not parse passkey creation request: {e:?}" - )) - })?; - let cred_request = - CredentialRequest::CreatePublicKeyCredentialRequest(make_cred_request); - - let response = execute_flow(&tx, &self.manager_client, &cred_request).await?; - - if let CredentialResponse::CreatePublicKeyCredentialResponse(cred_response) = - response - { - let public_key_response = - CreatePublicKeyCredentialResponse::try_from_ctap2_response( - &cred_response, - client_data_json, - )?; - Ok(public_key_response.into()) - } else { - Err(fdo::Error::Failed("Failed to create passkey".to_string())) - } - } - _ => Err(fdo::Error::Failed( - "Unknown credential request type".to_string(), - )), - }; - response - } else { - tracing::info!("Window already open"); - Err(fdo::Error::ObjectPathInUse( - "WebAuthn session already open.".into(), - )) - } - } - - async fn get_credential( - &self, - request: GetCredentialRequest, - ) -> fdo::Result { - if let Ok(tx) = self.app_lock.try_lock() { - if request.origin.is_none() { - todo!("Implicit caller-origin binding is not yet implemented."); - } - let is_same_origin = request.is_same_origin.unwrap_or(false); - let response = match (request.r#type.as_ref(), &request.public_key) { - ("publicKey", Some(_)) => { - if !is_same_origin { - return Err(fdo::Error::AccessDenied(String::from( - "Cross-origin public-key credentials are not allowed.", - ))); - } - // Setup request - - // TODO: assert that RP ID is bound to origin: - // - if RP ID is not set, set the RP ID to the origin's effective domain - // - if RP ID is set, assert that it matches origin's effective domain - // - if RP ID is set, but origin's effective domain doesn't match - // - query for related origins, if supported - // - fail if not supported, or if RP ID doesn't match any related origins. - let (get_cred_request, client_data_json) = - request.clone().try_into_ctap2_request().map_err(|_| { - fdo::Error::Failed( - "Could not parse passkey assertion request.".to_owned(), - ) - })?; - let cred_request = - CredentialRequest::GetPublicKeyCredentialRequest(get_cred_request); - - let response = execute_flow(&tx, &self.manager_client, &cred_request).await?; - - match response { - CredentialResponse::GetPublicKeyCredentialResponse(cred_response) => { - let public_key_response = - GetPublicKeyCredentialResponse::try_from_ctap2_response( - &cred_response, - client_data_json, - )?; - Ok(public_key_response.into()) - } - _ => Err(fdo::Error::Failed( - "Invalid credential response received from authenticator".to_string(), - )), - } - } - _ => Err(fdo::Error::Failed( - "Unknown credential request type".to_string(), - )), - }; - response - } else { - tracing::info!("Window already open"); - Err(fdo::Error::ObjectPathInUse( - "WebAuthn session already open.".into(), - )) - } - } - - async fn get_client_capabilities(&self) -> fdo::Result { - Ok(GetClientCapabilitiesResponse { - conditional_create: false, - conditional_get: false, - hybrid_transport: false, - passkey_platform_authenticator: false, - user_verifying_platform_authenticator: false, - related_origins: false, - signal_all_accepted_credentials: false, - signal_current_user_details: false, - signal_unknown_credential: false, - }) - } -} - -async fn execute_flow( - gui_tx: &async_std::channel::Sender, - manager_client: &C, - cred_request: &CredentialRequest, -) -> Result { - manager_client - .init_request(cred_request.clone()) - .await - .map_err(|_| fdo::Error::Failed("Request already running".to_string()))?; - - // start GUI - let operation = match &cred_request { - CredentialRequest::CreatePublicKeyCredentialRequest(_) => Operation::Create { - cred_type: CredentialType::Passkey, - }, - CredentialRequest::GetPublicKeyCredentialRequest(_) => Operation::Get { - cred_types: vec![CredentialType::Passkey], - }, - }; - let (signal_tx, signal_rx) = tokio::sync::oneshot::channel(); - let view_request = ViewRequest { - operation, - signal: signal_tx, - }; - gui_tx.send(view_request).await.unwrap(); - - // wait for gui to complete - signal_rx.await.map_err(|_| { - zbus::Error::Failure("GUI channel closed before completing request.".to_string()) - })?; - - // finish up - manager_client.complete_auth().await.map_err(|err| { - tracing::error!("Error retrieving credential: {:?}", err); - zbus::Error::Failure("Error retrieving credential".to_string()) - }) -} - -// D-Bus <-> internal types -#[derive(Clone, Debug)] -pub(crate) enum CredentialRequest { - CreatePublicKeyCredentialRequest(MakeCredentialRequest), - GetPublicKeyCredentialRequest(GetAssertionRequest), -} - -#[derive(Clone, Debug)] -pub(crate) enum CredentialResponse { - CreatePublicKeyCredentialResponse(MakeCredentialResponseInternal), - GetPublicKeyCredentialResponse(GetAssertionResponseInternal), -} - -impl CredentialResponse { - pub(crate) fn from_make_credential( - response: &MakeCredentialResponse, - transports: &[&str], - modality: &str, - ) -> CredentialResponse { - CredentialResponse::CreatePublicKeyCredentialResponse(MakeCredentialResponseInternal::new( - response.clone(), - transports.iter().map(|s| s.to_string()).collect(), - modality.to_string(), - )) - } - - pub(crate) fn from_get_assertion(assertion: &Assertion, modality: &str) -> CredentialResponse { - CredentialResponse::GetPublicKeyCredentialResponse(GetAssertionResponseInternal::new( - assertion.clone(), - modality.to_string(), - )) - } -} - -#[derive(Clone, Debug)] -pub(crate) struct MakeCredentialResponseInternal { - ctap: MakeCredentialResponse, - transport: Vec, - attachment_modality: String, -} - -impl MakeCredentialResponseInternal { - pub(crate) fn new( - response: MakeCredentialResponse, - transport: Vec, - attachment_modality: String, - ) -> Self { - Self { - ctap: response, - transport, - attachment_modality, - } - } -} - -#[derive(Clone, Debug)] -pub(crate) struct GetAssertionResponseInternal { - ctap: Assertion, - attachment_modality: String, -} - -impl GetAssertionResponseInternal { - pub(crate) fn new(ctap: Assertion, attachment_modality: String) -> Self { - Self { - ctap, - attachment_modality, - } - } -} - -// D-Bus <-> Client types -#[derive(Clone, Debug, DeserializeDict, Type)] -#[zvariant(signature = "dict")] -pub struct CreateCredentialRequest { - pub(crate) origin: Option, - pub(crate) is_same_origin: Option, - #[zvariant(rename = "type")] - pub(crate) r#type: String, - #[zvariant(rename = "publicKey")] - pub(crate) public_key: Option, -} - -impl CreateCredentialRequest { - pub(crate) fn try_into_ctap2_request( - &self, - ) -> std::result::Result<(MakeCredentialRequest, String), webauthn::Error> { - if self.public_key.is_none() { - return Err(webauthn::Error::NotSupported); - } - let options = self.public_key.as_ref().unwrap(); - - let request_value = serde_json::from_str::(&options.request_json) - .map_err(|_| webauthn::Error::Internal("Invalid request JSON".to_string()))?; - let json = request_value - .as_object() - .ok_or_else(|| webauthn::Error::Internal("Invalid request JSON".to_string()))?; - let challenge = json - .get("challenge") - .and_then(|c| c.as_str()) - .ok_or_else(|| webauthn::Error::Internal("JSON missing `challenge` field".to_string()))? - .to_owned(); - let rp = json - .get("rp") - .and_then(|val| { - serde_json::from_str::(&val.to_string()).ok() - }) - .ok_or_else(|| webauthn::Error::Internal("JSON missing `rp` field".to_string()))?; - let user = json - .get("user") - .ok_or(webauthn::Error::Internal( - "JSON missing `user` field".to_string(), - )) - .and_then(|val| { - serde_json::from_str::(&val.to_string()) - .map_err(|e| { - let msg = format!("JSON missing `user` field: {e}"); - webauthn::Error::Internal(msg) - }) - })?; - let other_options = - serde_json::from_str::(&request_value.to_string()) - .map_err(|_| webauthn::Error::Internal("Invalid request JSON".to_string()))?; - 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); - - (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 = request_value - .clone() - .get("pubKeyCredParams") - .ok_or_else(|| { - webauthn::Error::Internal( - "Request JSON missing or invalid `pubKeyCredParams` key".to_string(), - ) - }) - .and_then(|val| -> std::result::Result, webauthn::Error> { - serde_json::from_str::>(&val.to_string()) - .map_err(|e| { - webauthn::Error::Internal(format!( - "Request JSON missing or invalid `pubKeyCredParams` key: {e}" - )) - }) - })?; - 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 (self.origin.as_ref(), self.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, _) => { - return Err(webauthn::Error::Internal( - "Error reading origin from request".to_string(), - )); - } - }; - let client_data_json = format_client_data_json( - Operation::Create { - cred_type: CredentialType::Passkey, - }, - &challenge, - &origin, - is_cross_origin, - ); - let client_data_hash = digest::digest(&digest::SHA256, client_data_json.as_bytes()) - .as_ref() - .to_owned(); - Ok(( - MakeCredentialRequest { - hash: client_data_hash, - origin, - - relying_party: rp, - user, - resident_key, - user_verification, - algorithms, - exclude, - extensions, - timeout: other_options.timeout.unwrap_or(Duration::from_secs(300)), - }, - client_data_json, - )) - } -} - -#[derive(Clone, Debug, DeserializeDict, Type)] -#[zvariant(signature = "dict")] -pub struct CreatePublicKeyCredentialRequest { - pub(crate) request_json: String, -} - -impl CreatePublicKeyCredentialResponse { - fn try_from_ctap2_response( - response: &MakeCredentialResponseInternal, - client_data_json: String, - ) -> std::result::Result { - let auth_data = &response.ctap.authenticator_data; - let attested_credential = auth_data.attested_credential.as_ref().ok_or_else(|| { - fdo::Error::Failed("Invalid credential received from authenticator".to_string()) - })?; - - let unsigned_extensions = - serde_json::to_string(&response.ctap.unsigned_extensions_output).unwrap(); - let authenticator_data_blob = auth_data.to_response_bytes().unwrap(); - let attestation_statement = - (&response.ctap.attestation_statement) - .try_into() - .map_err(|_| { - fdo::Error::Failed("Could not serialize attestation statement".to_string()) - })?; - let attestation_object = webauthn::create_attestation_object( - &authenticator_data_blob, - &attestation_statement, - response.ctap.enterprise_attestation.unwrap_or(false), - ) - .map_err(|_| zbus::Error::Failure("Failed to create attestation object".to_string()))?; - // 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, - client_data_json, - Some(response.transport.clone()), - unsigned_extensions, - response.attachment_modality.clone(), - ) - .to_json(); - let response = CreatePublicKeyCredentialResponse { - registration_response_json, - }; - Ok(response) - } -} - -#[derive(SerializeDict, Type)] -#[zvariant(signature = "dict")] -pub struct CreateCredentialResponse { - #[zvariant(rename = "type")] - r#type: String, - public_key: Option, -} - -#[derive(SerializeDict, Type)] -#[zvariant(signature = "dict")] -pub struct CreatePublicKeyCredentialResponse { - registration_response_json: String, -} - -impl From for CreateCredentialResponse { - fn from(response: CreatePublicKeyCredentialResponse) -> Self { - CreateCredentialResponse { - // TODO: Decide on camelCase or kebab-case for cred types - r#type: "public-key".to_string(), - public_key: Some(response), - } - } -} - -#[derive(Clone, Debug, DeserializeDict, Type)] -#[zvariant(signature = "dict")] -pub struct GetCredentialRequest { - origin: Option, - is_same_origin: Option, - #[zvariant(rename = "type")] - r#type: String, - #[zvariant(rename = "publicKey")] - public_key: Option, -} - -impl GetCredentialRequest { - fn try_into_ctap2_request( - &self, - ) -> std::result::Result<(GetAssertionRequest, String), webauthn::Error> { - if self.public_key.is_none() { - return Err(webauthn::Error::NotSupported); - } - let options = self.public_key.as_ref().unwrap(); - let request: webauthn::GetCredentialOptions = - serde_json::from_str(&options.request_json) - .map_err(|e| webauthn::Error::Internal(format!("Invalid request JSON: {:?}", e)))?; - let mut allow: Vec = request - .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 (self.origin.as_ref(), self.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, _) => { - return Err(webauthn::Error::Internal( - "Error reading origin from request".to_string(), - )); - } - }; - let client_data_json = format_client_data_json( - Operation::Get { - cred_types: vec![CredentialType::Passkey], - }, - &request.challenge, - &origin, - is_cross_origin, - ); - let client_data_hash = digest::digest(&digest::SHA256, client_data_json.as_bytes()) - .as_ref() - .to_owned(); - // 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 request - .user_verification - .unwrap_or_else(|| String::from("preferred")) - .as_ref() - { - "required" => UserVerificationRequirement::Required, - "preferred" => UserVerificationRequirement::Preferred, - "discouraged" => UserVerificationRequirement::Discouraged, - _ => { - return Err(webauthn::Error::Internal( - "Invalid user verification requirement specified".to_string(), - )) - } - }; - let relying_party_id = request.rp_id.unwrap_or_else(|| { - let (_, effective_domain) = origin.rsplit_once('/').unwrap(); - effective_domain.to_string() - }); - - let extensions = if let Some(incoming_extensions) = request.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 - }; - - Ok(( - GetAssertionRequest { - hash: client_data_hash, - relying_party_id, - user_verification, - allow, - extensions, - timeout: request.timeout.unwrap_or(Duration::from_secs(300)), - }, - client_data_json, - )) - } -} - -#[derive(Clone, Debug, DeserializeDict, Type)] -#[zvariant(signature = "dict")] -pub struct GetPublicKeyCredentialRequest { - pub(crate) request_json: String, -} - -impl GetPublicKeyCredentialResponse { - fn try_from_ctap2_response( - response: &GetAssertionResponseInternal, - client_data_json: String, - ) -> std::result::Result { - let authenticator_data_blob = response - .ctap - .authenticator_data - .to_response_bytes() - .unwrap(); - - // 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 - .as_ref() - .map(GetPublicKeyCredentialUnsignedExtensionsResponse::from); - - let authentication_response_json = webauthn::GetPublicKeyCredentialResponse::new( - client_data_json, - response - .ctap - .credential_id - .as_ref() - .map(|c| c.id.clone().into_vec()), - authenticator_data_blob, - response.ctap.signature.clone(), - response.ctap.user.as_ref().map(|u| u.id.clone().into_vec()), - response.attachment_modality.clone(), - unsigned_extensions, - ) - .to_json(); - - let response = GetPublicKeyCredentialResponse { - authentication_response_json, - }; - Ok(response) - } -} - -#[derive(SerializeDict, Type)] -#[zvariant(signature = "dict")] -pub struct GetCredentialResponse { - #[zvariant(rename = "type")] - r#type: String, - public_key: Option, -} - -#[derive(SerializeDict, Type)] -#[zvariant(signature = "dict")] -pub struct GetPublicKeyCredentialResponse { - authentication_response_json: String, -} - -impl From for GetCredentialResponse { - fn from(response: GetPublicKeyCredentialResponse) -> Self { - GetCredentialResponse { - // TODO: Decide on camelCase or kebab-case for cred types - r#type: "public-key".to_string(), - public_key: Some(response), - } - } -} - -#[derive(SerializeDict, Type)] -#[zvariant(signature = "dict", rename_all = "camelCase")] -pub struct GetClientCapabilitiesResponse { - conditional_create: bool, - conditional_get: bool, - hybrid_transport: bool, - passkey_platform_authenticator: bool, - user_verifying_platform_authenticator: bool, - related_origins: bool, - signal_all_accepted_credentials: bool, - signal_current_user_details: bool, - signal_unknown_credential: bool, -} - -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}}}") -} diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/gui/mod.rs b/xyz-iinuwa-credential-manager-portal-gtk/src/gui/mod.rs deleted file mode 100644 index 69919b50..00000000 --- a/xyz-iinuwa-credential-manager-portal-gtk/src/gui/mod.rs +++ /dev/null @@ -1,49 +0,0 @@ -pub mod view_model; - -use std::thread; - -use async_std::channel::Receiver; -use tokio::sync::oneshot; - -use crate::credential_service::CredentialServiceClient; - -use view_model::{Operation, ViewEvent, ViewUpdate}; - -pub struct ViewRequest { - pub operation: Operation, - pub signal: oneshot::Sender<()>, -} - -pub(super) fn start_gui_thread( - rx: Receiver, - client: C, -) { - thread::Builder::new() - .name("gui".into()) - .spawn(move || { - // D-Bus received a request and needs a window open - while let Ok(view_request) = rx.recv_blocking() { - run_gui(client.clone(), view_request); - } - }) - .unwrap(); -} - -fn run_gui(client: C, request: ViewRequest) { - let ViewRequest { - operation, - signal: response_tx, - } = request; - let (tx_update, rx_update) = async_std::channel::unbounded::(); - let (tx_event, rx_event) = async_std::channel::unbounded::(); - let event_loop = async_std::task::spawn(async move { - let mut vm = view_model::ViewModel::new(operation, client, rx_event, tx_update); - vm.start_event_loop().await; - println!("event loop ended?"); - }); - - view_model::gtk::start_gtk_app(tx_event, rx_update); - - async_std::task::block_on(event_loop.cancel()); - response_tx.send(()).unwrap(); -} diff --git a/xyz-iinuwa-credential-manager-portal-gtk/src/main.rs b/xyz-iinuwa-credential-manager-portal-gtk/src/main.rs deleted file mode 100644 index de43e1c0..00000000 --- a/xyz-iinuwa-credential-manager-portal-gtk/src/main.rs +++ /dev/null @@ -1,56 +0,0 @@ -mod cbor; -#[rustfmt::skip] -mod config; -mod cose; -mod credential_service; -mod dbus; -mod gui; -mod serde; -mod webauthn; - -use std::{error::Error, sync::Arc}; - -use crate::credential_service::{ - hybrid::InternalHybridHandler, usb::InProcessUsbHandler, CredentialService, InProcessServer, -}; - -#[tokio::main] -async fn main() { - // Initialize logger - tracing_subscriber::fmt::init(); - rustls::crypto::ring::default_provider() - .install_default() - .expect("Failed to install rustls crypto provider"); - - println!("Starting..."); - run().await.unwrap(); -} - -async fn run() -> Result<(), Box> { - let credential_service = - CredentialService::new(InternalHybridHandler::new(), InProcessUsbHandler {}); - print!("Starting credential service...\t"); - let (mut cred_server, cred_mgr, cred_client) = InProcessServer::new(credential_service); - tokio::spawn(async move { - cred_server.run().await; - }); - println!(" ✅"); - - print!("Starting GUI thread...\t"); - // this allows the D-Bus service to signal to the GUI to draw a window for - // executing the credential flow. - let (dbus_to_gui_tx, dbus_to_gui_rx) = async_std::channel::unbounded(); - gui::start_gui_thread(dbus_to_gui_rx, Arc::new(cred_client)); - println!(" ✅"); - - print!("Starting D-Bus service..."); - let service_name = "xyz.iinuwa.credentials.CredentialManagerUi"; - let path = "/xyz/iinuwa/credentials/CredentialManagerUi"; - let _conn = dbus::start_service(service_name, path, dbus_to_gui_tx, cred_mgr).await?; - println!(" ✅"); - println!("Waiting for messages..."); - loop { - // wait forever, handle D-Bus in the background - std::future::pending::<()>().await; - } -} diff --git a/xyz-iinuwa-credential-manager-portal-gtk/tests/services/xyz.iinuwa.CredentialManagerUi.service.in b/xyz-iinuwa-credential-manager-portal-gtk/tests/services/xyz.iinuwa.CredentialManagerUi.service.in deleted file mode 100644 index 9fc84ead..00000000 --- a/xyz-iinuwa-credential-manager-portal-gtk/tests/services/xyz.iinuwa.CredentialManagerUi.service.in +++ /dev/null @@ -1,3 +0,0 @@ -[D-BUS Service] -Name=xyz.iinuwa.credentials.CredentialManagerUi -Exec=@DBUS_EXECUTABLE@