diff --git a/deno.lock b/deno.lock index d4489ae..ce9b4f0 100644 --- a/deno.lock +++ b/deno.lock @@ -21,6 +21,7 @@ "npm:tailwindcss@^4.1.18": "4.1.18", "npm:tw-animate-css@^1.4.0": "1.4.0", "npm:typescript@~5.9.3": "5.9.3", + "npm:vaul-svelte@^1.0.0-next.7": "1.0.0-next.7_svelte@5.46.1__acorn@8.15.0", "npm:vite@^6.0.3": "6.4.1_picomatch@4.0.3" }, "npm": { @@ -667,9 +668,9 @@ "@floating-ui/dom", "@internationalized/date", "esm-env", - "runed", + "runed@0.35.1_@sveltejs+kit@2.49.3__@sveltejs+vite-plugin-svelte@5.1.1___svelte@5.46.1____acorn@8.15.0___vite@6.4.1____picomatch@4.0.3__svelte@5.46.1___acorn@8.15.0__typescript@5.9.3__vite@6.4.1___picomatch@4.0.3__acorn@8.15.0_svelte@5.46.1__acorn@8.15.0_@sveltejs+vite-plugin-svelte@5.1.1__svelte@5.46.1___acorn@8.15.0__vite@6.4.1___picomatch@4.0.3_typescript@5.9.3_vite@6.4.1__picomatch@4.0.3", "svelte", - "svelte-toolbelt", + "svelte-toolbelt@0.10.6_svelte@5.46.1__acorn@8.15.0_@sveltejs+kit@2.49.3__@sveltejs+vite-plugin-svelte@5.1.1___svelte@5.46.1____acorn@8.15.0___vite@6.4.1____picomatch@4.0.3__svelte@5.46.1___acorn@8.15.0__typescript@5.9.3__vite@6.4.1___picomatch@4.0.3__acorn@8.15.0_@sveltejs+vite-plugin-svelte@5.1.1__svelte@5.46.1___acorn@8.15.0__vite@6.4.1___picomatch@4.0.3_typescript@5.9.3_vite@6.4.1__picomatch@4.0.3", "tabbable" ] }, @@ -937,6 +938,13 @@ ], "bin": true }, + "runed@0.23.4_svelte@5.46.1__acorn@8.15.0": { + "integrity": "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==", + "dependencies": [ + "esm-env", + "svelte" + ] + }, "runed@0.35.1_@sveltejs+kit@2.49.3__@sveltejs+vite-plugin-svelte@5.1.1___svelte@5.46.1____acorn@8.15.0___vite@6.4.1____picomatch@4.0.3__svelte@5.46.1___acorn@8.15.0__typescript@5.9.3__vite@6.4.1___picomatch@4.0.3__acorn@8.15.0_svelte@5.46.1__acorn@8.15.0_@sveltejs+vite-plugin-svelte@5.1.1__svelte@5.46.1___acorn@8.15.0__vite@6.4.1___picomatch@4.0.3_typescript@5.9.3_vite@6.4.1__picomatch@4.0.3": { "integrity": "sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q==", "dependencies": [ @@ -993,7 +1001,16 @@ "integrity": "sha512-YWuX+RE+CnWYx09yseAe4ZVMM7e7GRFZM6OYWpBKOb++s+SQ8RBIMMe+Bs/CznBMc0QPLjr+vDBxTAkozXsFXQ==", "dependencies": [ "clsx", - "runed", + "runed@0.35.1_@sveltejs+kit@2.49.3__@sveltejs+vite-plugin-svelte@5.1.1___svelte@5.46.1____acorn@8.15.0___vite@6.4.1____picomatch@4.0.3__svelte@5.46.1___acorn@8.15.0__typescript@5.9.3__vite@6.4.1___picomatch@4.0.3__acorn@8.15.0_svelte@5.46.1__acorn@8.15.0_@sveltejs+vite-plugin-svelte@5.1.1__svelte@5.46.1___acorn@8.15.0__vite@6.4.1___picomatch@4.0.3_typescript@5.9.3_vite@6.4.1__picomatch@4.0.3", + "style-to-object", + "svelte" + ] + }, + "svelte-toolbelt@0.7.1_svelte@5.46.1__acorn@8.15.0": { + "integrity": "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ==", + "dependencies": [ + "clsx", + "runed@0.23.4_svelte@5.46.1__acorn@8.15.0", "style-to-object", "svelte" ] @@ -1060,6 +1077,14 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "bin": true }, + "vaul-svelte@1.0.0-next.7_svelte@5.46.1__acorn@8.15.0": { + "integrity": "sha512-7zN7Bi3dFQixvvbUJY9uGDe7Ws/dGZeBQR2pXdXmzQiakjrxBvWo0QrmsX3HK+VH+SZOltz378cmgmCS9f9rSg==", + "dependencies": [ + "runed@0.23.4_svelte@5.46.1__acorn@8.15.0", + "svelte", + "svelte-toolbelt@0.7.1_svelte@5.46.1__acorn@8.15.0" + ] + }, "vite@6.4.1_picomatch@4.0.3": { "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dependencies": [ @@ -1111,6 +1136,7 @@ "npm:tailwindcss@^4.1.18", "npm:tw-animate-css@^1.4.0", "npm:typescript@~5.9.3", + "npm:vaul-svelte@^1.0.0-next.7", "npm:vite@^6.0.3" ] } diff --git a/package.json b/package.json index 786aa27..0f2363d 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "tailwind-variants": "^3.2.2", "tw-animate-css": "^1.4.0", "typescript": "~5.9.3", + "vaul-svelte": "^1.0.0-next.7", "vite": "^6.0.3", "@tauri-apps/cli": "^2" } diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index d35d0a0..a95679c 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -451,7 +451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" dependencies = [ "serde", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", ] [[package]] @@ -465,9 +465,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.51" +version = "1.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" dependencies = [ "find-msvc-tools", "shlex", @@ -506,12 +506,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - [[package]] name = "chrono" version = "0.4.42" @@ -782,9 +776,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "der-parser" @@ -986,7 +980,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", "vswhom", "winreg", ] @@ -1108,15 +1102,15 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" [[package]] name = "flate2" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" dependencies = [ "crc32fast", "miniz_oxide", @@ -1390,9 +1384,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "libc", @@ -2315,19 +2309,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags 2.10.0", - "cfg-if", - "cfg_aliases", - "libc", - "memoffset", -] - [[package]] name = "nodrop" version = "0.1.14" @@ -3195,7 +3176,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -3225,7 +3206,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -3243,14 +3224,14 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom 0.3.4", ] @@ -3294,7 +3275,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", "thiserror 2.0.17", ] @@ -3391,7 +3372,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -3890,12 +3871,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "string_cache" version = "0.8.9" @@ -4137,7 +4112,7 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", "walkdir", ] @@ -4195,7 +4170,7 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", "walkdir", ] @@ -4325,7 +4300,7 @@ dependencies = [ "serde_with", "swift-rs", "thiserror 2.0.17", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", "url", "urlpattern", "uuid", @@ -4340,7 +4315,7 @@ checksum = "1087b111fe2b005e42dbdc1990fc18593234238d47453b0c99b7de1c9ab2c1e0" dependencies = [ "dunce", "embed-resource", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", ] [[package]] @@ -4409,40 +4384,40 @@ dependencies = [ [[package]] name = "thread-id" -version = "5.0.0" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99043e46c5a15af379c06add30d9c93a6c0e8849de00d244c4a2c417da128d80" +checksum = "2010d27add3f3240c1fef7959f46c814487b216baee662af53be645ba7831c07" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "time" -version = "0.3.44" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" [[package]] name = "time-macros" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" dependencies = [ "num-conv", "time-core", @@ -4499,9 +4474,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.10+spec-1.1.0" +version = "0.9.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" dependencies = [ "indexmap 2.13.0", "serde_core", @@ -4583,9 +4558,9 @@ checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -5733,9 +5708,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.12.0" +version = "5.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b622b18155f7a93d1cd2dc8c01d2d6a44e08fb9ebb7b3f9e6ed101488bad6c91" +checksum = "17f79257df967b6779afa536788657777a0001f5b42524fcaf5038d4344df40b" dependencies = [ "async-broadcast", "async-executor", @@ -5751,8 +5726,9 @@ dependencies = [ "futures-core", "futures-lite", "hex", - "nix", + "libc", "ordered-stream", + "rustix", "serde", "serde_repr", "tracing", @@ -5767,9 +5743,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.12.0" +version = "5.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cdb94821ca8a87ca9c298b5d1cbd80e2a8b67115d99f6e4551ac49e42b6a314" +checksum = "aad23e2d2f91cae771c7af7a630a49e755f1eb74f8a46e9f6d5f7a146edf5a37" dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", @@ -5782,12 +5758,11 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.2.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" dependencies = [ "serde", - "static_assertions", "winnow 0.7.14", "zvariant", ] @@ -5868,15 +5843,15 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8" +checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" [[package]] name = "zvariant" -version = "5.8.0" +version = "5.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be61892e4f2b1772727be11630a62664a1826b62efa43a6fe7449521cb8744c" +checksum = "326aaed414f04fe839777b4c443d4e94c74e7b3621093bd9c5e649ac8aa96543" dependencies = [ "endi", "enumflags2", @@ -5888,9 +5863,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.8.0" +version = "5.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58575a1b2b20766513b1ec59d8e2e68db2745379f961f86650655e862d2006" +checksum = "ba44e1f8f4da9e6e2d25d2a60b116ef8b9d0be174a7685e55bb12a99866279a7" dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", @@ -5901,9 +5876,9 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" +checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" dependencies = [ "proc-macro2", "quote", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 372584c..e3bbbe1 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -42,7 +42,7 @@ log4rs = "1" # For logging to output (like stdout) directories = "6" # For Applcation config/data dir handling [profile.dev] -incremental = true # Compile your binary in smaller steps. +incremental = true # Compile your binary in smaller steps. codegen-units = 256 [profile.release] diff --git a/src-tauri/src/fido/mod.rs b/src-tauri/src/fido/mod.rs index 67cc2d3..4c062f0 100644 --- a/src-tauri/src/fido/mod.rs +++ b/src-tauri/src/fido/mod.rs @@ -5,10 +5,12 @@ pub mod hid; use crate::{ error::PFError, - types::{AppConfig, DeviceInfo, FidoDeviceInfo, FullDeviceStatus}, + types::{AppConfig, DeviceInfo, FidoDeviceInfo, FullDeviceStatus, StoredCredential}, }; use constants::*; -use ctap_hid_fido2::{Cfg, FidoKeyHidFactory}; +use ctap_hid_fido2::{ + Cfg, FidoKeyHidFactory, public_key_credential_descriptor::PublicKeyCredentialDescriptor, +}; use hid::*; use serde_cbor_2::{Value, from_slice, to_vec}; use std::collections::{BTreeMap, HashMap}; @@ -81,6 +83,62 @@ pub(crate) fn set_min_pin_length( )) } +pub(crate) fn get_credentials(pin: String) -> Result, String> { + let cfg = Cfg::init(); + let device = FidoKeyHidFactory::create(&cfg) + .map_err(|e| format!("Failed to connect to FIDO device: {:?}", e))?; + + let rps = device + .credential_management_enumerate_rps(Some(&pin)) + .map_err(|e| format!("Failed to enumerate Relying Parties: {:?}", e))?; + + let mut all_credentials = Vec::new(); + + for rp in rps { + let creds = device + .credential_management_enumerate_credentials(Some(&pin), &rp.rpid_hash) + .map_err(|e| { + format!( + "Failed to enumerate credentials for RP {}: {:?}", + rp.public_key_credential_rp_entity.id, e + ) + })?; + + for cred in creds { + all_credentials.push(StoredCredential { + credential_id: hex::encode(&cred.public_key_credential_descriptor.id), + rp_id: rp.public_key_credential_rp_entity.id.clone(), + rp_name: rp.public_key_credential_rp_entity.name.clone(), + user_name: cred.public_key_credential_user_entity.name.clone(), + user_display_name: cred.public_key_credential_user_entity.display_name.clone(), + user_id: hex::encode(&cred.public_key_credential_user_entity.id).clone(), + }); + } + } + + Ok(all_credentials) +} + +pub(crate) fn delete_credential(pin: String, credential_id_hex: String) -> Result { + let cfg = Cfg::init(); + let device = FidoKeyHidFactory::create(&cfg) + .map_err(|e| format!("Failed to connect to FIDO device: {:?}", e))?; + + let cred_id_bytes = hex::decode(&credential_id_hex) + .map_err(|_| "Invalid Credential ID Hex string".to_string())?; + + let descriptor = PublicKeyCredentialDescriptor { + ctype: "public-key".to_string(), + id: cred_id_bytes, + }; + + device + .credential_management_delete_credential(Some(&pin), descriptor) + .map_err(|e| format!("Failed to delete credential: {:?}", e))?; + + Ok("Credential deleted successfully".into()) +} + // Custom Fido functions ( works only with pico-fido firmware ) pub fn read_device_details() -> Result { diff --git a/src-tauri/src/io.rs b/src-tauri/src/io.rs index 91bafa2..b136115 100644 --- a/src-tauri/src/io.rs +++ b/src-tauri/src/io.rs @@ -43,3 +43,17 @@ pub(crate) fn set_min_pin_length( pub fn reboot(to_bootsel: bool) -> Result { rescue::reboot_device(to_bootsel) } + +#[tauri::command] +pub async fn get_credentials(pin: String) -> Result, String> { + tauri::async_runtime::spawn_blocking(move || { + fido::get_credentials(pin) + }).await.map_err(|e| e.to_string())? +} + +#[tauri::command] +pub async fn delete_credential(pin: String, credential_id: String) -> Result { + tauri::async_runtime::spawn_blocking(move || { + fido::delete_credential(pin, credential_id) + }).await.map_err(|e| e.to_string())? +} \ No newline at end of file diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 2ee15ae..732a27b 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,5 +1,5 @@ use serde::Serialize; -use tauri::{Emitter, Listener, Manager, WebviewWindow}; +use tauri::{Emitter, Manager, WebviewWindow}; mod error; mod fido; @@ -49,6 +49,8 @@ pub fn run() { io::write_config, io::get_fido_info, io::change_fido_pin, + io::get_credentials, + io::delete_credential, io::set_min_pin_length, io::enable_secure_boot, io::reboot diff --git a/src-tauri/src/types.rs b/src-tauri/src/types.rs index d0e9f8a..64dbf9c 100644 --- a/src-tauri/src/types.rs +++ b/src-tauri/src/types.rs @@ -72,3 +72,14 @@ pub struct FidoDeviceInfo { pub min_pin_length: u32, pub firmware_version: String, } + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct StoredCredential { + pub rp_id: String, + pub rp_name: String, + pub user_name: String, + pub user_display_name: String, + pub user_id: String, + pub credential_id: String, +} diff --git a/src/lib/components/ui/dialog/dialog-close.svelte b/src/lib/components/ui/dialog/dialog-close.svelte new file mode 100644 index 0000000..840b2f6 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-close.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-content.svelte b/src/lib/components/ui/dialog/dialog-content.svelte new file mode 100644 index 0000000..5c6ee6d --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-content.svelte @@ -0,0 +1,45 @@ + + + + + + {@render children?.()} + {#if showCloseButton} + + + Close + + {/if} + + diff --git a/src/lib/components/ui/dialog/dialog-description.svelte b/src/lib/components/ui/dialog/dialog-description.svelte new file mode 100644 index 0000000..3845023 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-footer.svelte b/src/lib/components/ui/dialog/dialog-footer.svelte new file mode 100644 index 0000000..e7ff446 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-footer.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/dialog/dialog-header.svelte b/src/lib/components/ui/dialog/dialog-header.svelte new file mode 100644 index 0000000..4e5c447 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-header.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/dialog/dialog-overlay.svelte b/src/lib/components/ui/dialog/dialog-overlay.svelte new file mode 100644 index 0000000..f81ad83 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-overlay.svelte @@ -0,0 +1,20 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-portal.svelte b/src/lib/components/ui/dialog/dialog-portal.svelte new file mode 100644 index 0000000..ccfa79c --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-title.svelte b/src/lib/components/ui/dialog/dialog-title.svelte new file mode 100644 index 0000000..e4d4b34 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-trigger.svelte b/src/lib/components/ui/dialog/dialog-trigger.svelte new file mode 100644 index 0000000..9d1e801 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog.svelte b/src/lib/components/ui/dialog/dialog.svelte new file mode 100644 index 0000000..211672c --- /dev/null +++ b/src/lib/components/ui/dialog/dialog.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dialog/index.ts b/src/lib/components/ui/dialog/index.ts new file mode 100644 index 0000000..076cef5 --- /dev/null +++ b/src/lib/components/ui/dialog/index.ts @@ -0,0 +1,34 @@ +import Root from "./dialog.svelte"; +import Portal from "./dialog-portal.svelte"; +import Title from "./dialog-title.svelte"; +import Footer from "./dialog-footer.svelte"; +import Header from "./dialog-header.svelte"; +import Overlay from "./dialog-overlay.svelte"; +import Content from "./dialog-content.svelte"; +import Description from "./dialog-description.svelte"; +import Trigger from "./dialog-trigger.svelte"; +import Close from "./dialog-close.svelte"; + +export { + Root, + Title, + Portal, + Footer, + Header, + Trigger, + Overlay, + Content, + Description, + Close, + // + Root as Dialog, + Title as DialogTitle, + Portal as DialogPortal, + Footer as DialogFooter, + Header as DialogHeader, + Trigger as DialogTrigger, + Overlay as DialogOverlay, + Content as DialogContent, + Description as DialogDescription, + Close as DialogClose, +}; diff --git a/src/lib/components/ui/drawer/drawer-close.svelte b/src/lib/components/ui/drawer/drawer-close.svelte new file mode 100644 index 0000000..95c2479 --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-close.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/drawer/drawer-content.svelte b/src/lib/components/ui/drawer/drawer-content.svelte new file mode 100644 index 0000000..6bb01db --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-content.svelte @@ -0,0 +1,40 @@ + + + + + + + {@render children?.()} + + diff --git a/src/lib/components/ui/drawer/drawer-description.svelte b/src/lib/components/ui/drawer/drawer-description.svelte new file mode 100644 index 0000000..2763a1a --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/drawer/drawer-footer.svelte b/src/lib/components/ui/drawer/drawer-footer.svelte new file mode 100644 index 0000000..1691f58 --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-footer.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/drawer/drawer-header.svelte b/src/lib/components/ui/drawer/drawer-header.svelte new file mode 100644 index 0000000..65d2de5 --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-header.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/drawer/drawer-nested.svelte b/src/lib/components/ui/drawer/drawer-nested.svelte new file mode 100644 index 0000000..834af94 --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-nested.svelte @@ -0,0 +1,12 @@ + + + diff --git a/src/lib/components/ui/drawer/drawer-overlay.svelte b/src/lib/components/ui/drawer/drawer-overlay.svelte new file mode 100644 index 0000000..53f78a2 --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-overlay.svelte @@ -0,0 +1,20 @@ + + + diff --git a/src/lib/components/ui/drawer/drawer-portal.svelte b/src/lib/components/ui/drawer/drawer-portal.svelte new file mode 100644 index 0000000..5a0dd74 --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/drawer/drawer-title.svelte b/src/lib/components/ui/drawer/drawer-title.svelte new file mode 100644 index 0000000..a2e5761 --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/drawer/drawer-trigger.svelte b/src/lib/components/ui/drawer/drawer-trigger.svelte new file mode 100644 index 0000000..f1877d8 --- /dev/null +++ b/src/lib/components/ui/drawer/drawer-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/drawer/drawer.svelte b/src/lib/components/ui/drawer/drawer.svelte new file mode 100644 index 0000000..0cb57ff --- /dev/null +++ b/src/lib/components/ui/drawer/drawer.svelte @@ -0,0 +1,12 @@ + + + diff --git a/src/lib/components/ui/drawer/index.ts b/src/lib/components/ui/drawer/index.ts new file mode 100644 index 0000000..1656cac --- /dev/null +++ b/src/lib/components/ui/drawer/index.ts @@ -0,0 +1,38 @@ +import Root from "./drawer.svelte"; +import Content from "./drawer-content.svelte"; +import Description from "./drawer-description.svelte"; +import Overlay from "./drawer-overlay.svelte"; +import Footer from "./drawer-footer.svelte"; +import Header from "./drawer-header.svelte"; +import Title from "./drawer-title.svelte"; +import NestedRoot from "./drawer-nested.svelte"; +import Close from "./drawer-close.svelte"; +import Trigger from "./drawer-trigger.svelte"; +import Portal from "./drawer-portal.svelte"; + +export { + Root, + NestedRoot, + Content, + Description, + Overlay, + Footer, + Header, + Title, + Trigger, + Portal, + Close, + + // + Root as Drawer, + NestedRoot as DrawerNestedRoot, + Content as DrawerContent, + Description as DrawerDescription, + Overlay as DrawerOverlay, + Footer as DrawerFooter, + Header as DrawerHeader, + Title as DrawerTitle, + Trigger as DrawerTrigger, + Portal as DrawerPortal, + Close as DrawerClose, +}; diff --git a/src/lib/device/manager.svelte.ts b/src/lib/device/manager.svelte.ts index 73574ce..4cd7010 100644 --- a/src/lib/device/manager.svelte.ts +++ b/src/lib/device/manager.svelte.ts @@ -8,6 +8,7 @@ import type { SecurityState, FullDeviceStatus, DeviceConfigInput, + StoredCredential, } from "$lib/device/types.svelte"; class DeviceManager { @@ -15,6 +16,9 @@ class DeviceManager { connected = $state(false); fidoInfo: FidoInfo | null = $state(null); + credentials: StoredCredential[] = $state([]); + unlocked = $state(false); + config: DeviceConfig = $state({ ...DEFAULT_CONFIG }); info: DeviceInfo = $state({ ...DEFAULT_DEVICE_INFO }); security: SecurityState = $state({ @@ -181,6 +185,39 @@ class DeviceManager { return { success: false, msg: err }; } } + + async getCredentials(pin: string): Promise<{ success: boolean; data?: StoredCredential[]; msg?: string }> { + try { + logger.add("Fetching credentials...", "info"); + const creds = await invoke("get_credentials", { pin }); + + this.credentials = creds; + this.unlocked = true; + + logger.add(`Retrieved ${creds.length} credentials.`, "success"); + return { success: true, data: creds }; + } catch (err: any) { + logger.add(`Failed to fetch credentials: ${err}`, "error"); + return { success: false, msg: err }; + } + } + + lock() { + this.credentials = []; + this.unlocked = false; + } + + async deleteCredential(pin: string, credentialId: string): Promise<{ success: boolean; msg?: string }> { + try { + logger.add("Deleting credential...", "info"); + const res = await invoke("delete_credential", { pin, credentialId }); + logger.add(res, "success"); + return { success: true, msg: res }; + } catch (err: any) { + logger.add(`Failed to delete credential: ${err}`, "error"); + return { success: false, msg: err }; + } + } } export const device = new DeviceManager(); diff --git a/src/lib/device/types.svelte.ts b/src/lib/device/types.svelte.ts index 65b84cf..e505443 100644 --- a/src/lib/device/types.svelte.ts +++ b/src/lib/device/types.svelte.ts @@ -57,3 +57,12 @@ export interface FidoInfo { minPinLength: number; firmwareVersion: string; } + +export interface StoredCredential { + credentialId: string; + rpId: string; + rpName: string; + userId: string; + userName: string; + userDisplayName: string; +} diff --git a/src/lib/layout/sidebar.svelte b/src/lib/layout/sidebar.svelte index 39a9770..1170afb 100644 --- a/src/lib/layout/sidebar.svelte +++ b/src/lib/layout/sidebar.svelte @@ -3,7 +3,7 @@ import { getCurrentWindow } from "@tauri-apps/api/window"; import { listen } from "@tauri-apps/api/event"; - import { Home, Info, Maximize, Minimize, Minus, RefreshCw, ScrollText, Settings, ShieldCheck, X } from "@lucide/svelte"; + import { Home, Info, KeyRound, Maximize, Minimize, Minus, RefreshCw, ScrollText, Settings, ShieldCheck, X } from "@lucide/svelte"; import type { Component } from "svelte"; import { Button } from "$lib/components/ui/button"; @@ -12,7 +12,7 @@ import { device } from "$lib/device/manager.svelte"; - type View = "home" | "config" | "security" | "logs" | "about"; + type View = "home" | "passkeys" | "config" | "security" | "logs" | "about"; interface Props { currentView: View; @@ -29,6 +29,7 @@ const menuItems: Array<{ view: View; icon: Component; label: string }> = [ { view: "home", icon: Home, label: "Home" }, + { view: "passkeys", icon: KeyRound, label: "Passkeys" }, { view: "config", icon: Settings, label: "Configuration" }, { view: "security", icon: ShieldCheck, label: "Security" }, { view: "logs", icon: ScrollText, label: "Logs" }, diff --git a/src/lib/views/passkeysView.svelte b/src/lib/views/passkeysView.svelte new file mode 100644 index 0000000..3992864 --- /dev/null +++ b/src/lib/views/passkeysView.svelte @@ -0,0 +1,260 @@ + + +
+
+

Passkeys

+

Manage resident credentials (passkeys) stored on your device.

+
+ + {#if !device.connected} + + + No Device Connected + Connect your pico-key to manage passkeys. + + {:else} + + + + + Authenticate to View Passkeys + + Enter your FIDO2 PIN to access and manage stored credentials. + +
+
+ + e.key === "Enter" && handleUnlock()} + autofocus + /> +
+ {#if error} + + {error} + + {/if} +
+ + + +
+
+ + {#if device.unlocked} +
+
+ + + Unlocked + + + + {device.credentials.length} + {device.credentials.length === 1 ? "credential" : "credentials"} stored + +
+ +
+ + {#if device.credentials.length === 0} + + +
+ +
+

No Passkeys Found

+

+ This device doesn't have any resident credentials stored yet. Create passkeys on websites to see them here. +

+
+
+ {:else} +
+ {#each device.credentials as cred} + +
+ + + + {/each} +
+ {/if} + + + +
+ + + {selectedCredential?.rpName || selectedCredential?.rpId || "Passkey Details"} + + + Credential details for user {selectedCredential?.userName} + + + +
+
+
+ +
+
+
{selectedCredential?.rpId}
+
{selectedCredential?.userName}
+
+
+ + + +
+
+

Display Name

+

{selectedCredential?.userDisplayName || "N/A"}

+
+ +
+

User ID (Hex)

+

+ {selectedCredential?.userId} +

+
+ +
+

Credential ID (Hex)

+

{selectedCredential?.credentialId}

+
+
+
+ + + Close + +
+
+
+ {/if} + {/if} + diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 766823c..3edd44e 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -8,6 +8,7 @@ import SidebarMenu from "$lib/layout/sidebar.svelte"; import HomeView from "$lib/views/homeView.svelte"; + import PasskeysView from "$lib/views/passkeysView.svelte"; import ConfigView from "$lib/views/configView.svelte"; import SecurityView from "$lib/views/securityView.svelte"; import LogsView from "$lib/views/logsView.svelte"; @@ -17,11 +18,12 @@ import MinPinDialog from "$lib/components/dialogs/minPinDialog.svelte"; import MessageDialog from "$lib/components/dialogs/messageDialog.svelte"; - type View = "home" | "config" | "security" | "logs" | "about"; + type View = "home" | "passkeys" | "config" | "security" | "logs" | "about"; let currentView: View = $state("home"); const viewMap = { home: HomeView, + passkeys: PasskeysView, config: ConfigView, security: SecurityView, logs: LogsView,