diff --git a/Cargo.lock b/Cargo.lock index 71b132c..3e04c49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2278,9 +2278,9 @@ dependencies = [ [[package]] name = "soft-fido2" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd7f4ae5853a7faa7d5a6b9230b339ac23c0211a6771d8b1f7bbfb94d4dc289" +checksum = "a76343bd6795b3bbb72e2c996285b8e48b5cacb8094727dc7fe31151bbbee85d" dependencies = [ "aes", "cbc", @@ -2303,9 +2303,9 @@ dependencies = [ [[package]] name = "soft-fido2-crypto" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a0f6c6ff4257f8cb51ce03d47938deb2c3f43488e2dd08f3cc6a8437e65ad1" +checksum = "4eb85f9a3a47d27d868e709bdad777bbb76ca258783d4886b79ff76d4505dac0" dependencies = [ "aes", "cbc", @@ -2320,9 +2320,9 @@ dependencies = [ [[package]] name = "soft-fido2-ctap" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55f37978773ac94d4e27dfa7d253e15925fb64a4d94415289c4d2950ae48948" +checksum = "9b029fca0dfdaf739416a66cc6ddc6214a3ececb0bb059d112501c81d0a7b4f1" dependencies = [ "cbor4ii", "core2", @@ -2340,9 +2340,9 @@ dependencies = [ [[package]] name = "soft-fido2-transport" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33646cad9c98df5570d251b25db47dd5692489202ad724cf167dd70f11716cd2" +checksum = "964b8e51a7df41c6d1fff6d38cd5032ca39dd5490f7e3e8d4697467de8abeedd" dependencies = [ "hex", "hidapi", diff --git a/Cargo.toml b/Cargo.toml index 9e1c315..83db4d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,9 @@ repository = "https://github.com/pando85/passless" passless-core = { path = "./passless-core", version = "0.6.3" } passless-config-doc = { path = "./passless-config-doc", version = "0.6.3" } -soft-fido2 = "0.7.0" -soft-fido2-ctap = "0.7.0" -soft-fido2-transport = "0.7.0" +soft-fido2 = "0.8.0" +soft-fido2-ctap = "0.8.0" +soft-fido2-transport = "0.8.0" thiserror = "2.0" clap = { version = "4.5", features = ["std", "color", "derive", "cargo", "env"] } diff --git a/cmd/passless/src/authenticator.rs b/cmd/passless/src/authenticator.rs index cf4c19b..2517a47 100644 --- a/cmd/passless/src/authenticator.rs +++ b/cmd/passless/src/authenticator.rs @@ -104,29 +104,23 @@ impl AuthenticatorCallbacks for PasslessCallbacks { Ok(UvResult::Accepted) } - fn write_credential( - &self, - cred_id: &[u8], - rp_id: &str, - credential: &CredentialRef, - ) -> Result<()> { - info!("Storing credential for RP: {}", rp_id); - debug!("Credential ID: {}", bytes_to_hex(cred_id)); + fn write_credential(&self, credential: &CredentialRef) -> Result<()> { + info!("Storing credential for RP: {}", credential.rp_id); + debug!("Credential ID: {}", bytes_to_hex(credential.id)); let mut storage = self.storage.lock().unwrap(); - storage.write(cred_id, rp_id, *credential)?; - info!("Credential persisted successfully for RP: {}", rp_id); + storage.write(*credential)?; + info!( + "Credential persisted successfully for RP: {}", + credential.rp_id + ); Ok(()) } - fn read_credential(&self, cred_id: &[u8], rp_id: &str) -> Result> { - debug!( - "Reading credential: rp={}, id={}", - rp_id, - bytes_to_hex(cred_id) - ); + fn read_credential(&self, cred_id: &[u8]) -> Result> { + debug!("Reading credential: id={}", bytes_to_hex(cred_id)); let mut storage = self.storage.lock().unwrap(); - match storage.read(cred_id, rp_id) { + match storage.read(cred_id) { Ok(cred) => { debug!("Credential found"); Ok(Some(cred)) diff --git a/cmd/passless/src/storage/local/mod.rs b/cmd/passless/src/storage/local/mod.rs index 75eaba1..d53ed4c 100644 --- a/cmd/passless/src/storage/local/mod.rs +++ b/cmd/passless/src/storage/local/mod.rs @@ -188,7 +188,7 @@ impl CredentialStorage for LocalStorageAdapter { self.find_next() } - fn read(&mut self, id: &[u8], _rp: &str) -> Result { + fn read(&mut self, id: &[u8]) -> Result { debug!("read called for credential ID"); // Use index for direct path lookup @@ -203,7 +203,7 @@ impl CredentialStorage for LocalStorageAdapter { self.load_credential_from_path(&path) } - fn write(&mut self, _id: &[u8], _rp: &str, cred_ref: soft_fido2::CredentialRef) -> Result<()> { + fn write(&mut self, cred_ref: soft_fido2::CredentialRef) -> Result<()> { // Just save the credential as provided by soft-fido2 // This is called both during registration (new credential) and // during authentication (updating sign counter) @@ -234,89 +234,9 @@ impl CredentialStorage for LocalStorageAdapter { Ok(()) } - fn update_user_info( - &mut self, - id: &[u8], - user_name: Option<&str>, - display_name: Option<&str>, - ) -> Result<()> { - debug!("update_user_info called for credential ID"); - - // Use index for direct path lookup - let path_info = self - .indexes - .id - .get(id) - .ok_or(soft_fido2::Error::DoesNotExist)?; - - let path = path_info.to_path(&self.storage_dir); - - // Load the credential - let mut cred = self.load_credential_from_path(&path)?; - - // Update user information - if let Some(name) = user_name { - cred.user.name = Some(name.to_string()); - } - if let Some(display) = display_name { - cred.user.display_name = Some(display.to_string()); - } - - // Save the updated credential - self.save_credential(&cred)?; - - debug!("Updated user info for credential on RP: {}", cred.rp.id); - Ok(()) - } - - fn select_users(&self, rp_id: &str) -> Vec { - debug!("select_users called for RP: {}", rp_id); - - // Use index to get credential IDs for this RP - let cred_ids = match self.indexes.rp.get(rp_id) { - Some(ids) => ids, - None => { - debug!("No credentials found for RP: {}", rp_id); - return Vec::new(); - } - }; - - // Load only credentials for this RP - let users: Vec = cred_ids - .iter() - .filter_map(|cred_id| { - let path_info = self.indexes.id.get(cred_id)?; - let path = path_info.to_path(&self.storage_dir); - let cred = self.load_credential_from_path(&path).ok()?; - Some(String::from_utf8_lossy(&cred.user.id).to_string()) - }) - .collect(); - - debug!("Found {} users for RP: {}", users.len(), rp_id); - users - } - fn count_credentials(&self) -> usize { let count = self.indexes.id.len(); debug!("count_credentials: {}", count); count } - - fn get_relying_parties(&self) -> Result> { - debug!("get_relying_parties called"); - - // Extract RP info directly from path structure - no file loading needed! - let rp_list: Vec = self - .indexes - .rp - .keys() - .map(|rp_id| soft_fido2_ctap::types::RelyingParty { - id: rp_id.clone(), - name: None, // Name not available in path structure - }) - .collect(); - - debug!("Found {} relying parties", rp_list.len()); - Ok(rp_list) - } } diff --git a/cmd/passless/src/storage/mod.rs b/cmd/passless/src/storage/mod.rs index 6ea4c9b..5d9eab6 100644 --- a/cmd/passless/src/storage/mod.rs +++ b/cmd/passless/src/storage/mod.rs @@ -37,105 +37,23 @@ pub enum CredentialFilter { /// Any storage backend must implement this trait. pub trait CredentialStorage: Send + Sync { /// Start a new iteration and return the first matching credential - /// - /// # Arguments - /// - /// * `filter` - Filter criteria to apply - /// - /// # Returns - /// - /// The first matching credential or an error if none found fn read_first(&mut self, filter: CredentialFilter) -> Result; /// Continue the current iteration and return the next credential - /// - /// # Returns - /// - /// The next matching credential or an error if no more credentials fn read_next(&mut self) -> Result; /// Read a specific credential by ID and RP - /// - /// # Arguments - /// - /// * `id` - The credential ID - /// * `rp` - The relying party ID - /// - /// # Returns - /// - /// The credential or an error - fn read(&mut self, id: &[u8], rp: &str) -> Result; + fn read(&mut self, id: &[u8]) -> Result; /// Store a new credential - /// - /// # Arguments - /// - /// * `id` - The credential ID - /// * `rp` - The relying party ID - /// * `cred` - The credential reference to store - /// - /// # Returns - /// - /// Ok(()) on success or an error - fn write(&mut self, id: &[u8], rp: &str, cred: soft_fido2::CredentialRef) -> Result<()>; + fn write(&mut self, cred: soft_fido2::CredentialRef) -> Result<()>; /// Delete a credential by ID - /// - /// # Arguments - /// - /// * `id` - The credential ID to delete - /// - /// # Returns - /// - /// Ok(()) on success or an error fn delete(&mut self, id: &[u8]) -> Result<()>; - /// Update user information for a credential - /// - /// # Arguments - /// - /// * `id` - The credential ID to update - /// * `user_name` - Optional new user name (login identifier) - /// * `display_name` - Optional new display name (friendly name) - /// - /// # Returns - /// - /// Ok(()) on success or an error - #[allow(dead_code)] - fn update_user_info( - &mut self, - id: &[u8], - user_name: Option<&str>, - display_name: Option<&str>, - ) -> Result<()>; - - /// Get all user names for a given relying party - /// - /// # Arguments - /// - /// * `rp_id` - The relying party ID - /// - /// # Returns - /// - /// Vector of user names - #[allow(dead_code)] - fn select_users(&self, rp_id: &str) -> Vec; - /// Count total number of stored credentials - /// - /// # Returns - /// - /// The number of credentials in storage fn count_credentials(&self) -> usize; - /// Get all relying parties that have credentials - /// - /// # Returns - /// - /// # Vector of relying party IDs - #[allow(dead_code)] - fn get_relying_parties(&self) -> Result>; - /// Check if user verification should be disabled for this backend /// /// Some backends (like pass) don't support user verification. diff --git a/cmd/passless/src/storage/pass/mod.rs b/cmd/passless/src/storage/pass/mod.rs index 315c09a..4c67b16 100644 --- a/cmd/passless/src/storage/pass/mod.rs +++ b/cmd/passless/src/storage/pass/mod.rs @@ -535,7 +535,7 @@ impl CredentialStorage for PassStorageAdapter { self.find_next().map_err(Into::into) } - fn read(&mut self, id: &[u8], _rp: &str) -> soft_fido2::Result { + fn read(&mut self, id: &[u8]) -> soft_fido2::Result { self.cache.evict_expired(); debug!("read called with id: {}", bytes_to_hex(id)); @@ -544,12 +544,7 @@ impl CredentialStorage for PassStorageAdapter { self.read_credential_by_id(id).map_err(Into::into) } - fn write( - &mut self, - _id: &[u8], - _rp: &str, - cred_ref: soft_fido2::CredentialRef, - ) -> soft_fido2::Result<()> { + fn write(&mut self, cred_ref: soft_fido2::CredentialRef) -> soft_fido2::Result<()> { self.cache.evict_expired(); debug!("write called for RP: {}", cred_ref.rp_id); @@ -573,97 +568,12 @@ impl CredentialStorage for PassStorageAdapter { self.delete_credential(id).map_err(Into::into) } - fn update_user_info( - &mut self, - id: &[u8], - user_name: Option<&str>, - display_name: Option<&str>, - ) -> soft_fido2::Result<()> { - self.cache.evict_expired(); - - debug!("update_user_info called with id: {}", bytes_to_hex(id)); - - // Read the credential - let mut cred = self - .read_credential_by_id(id) - .map_err(Into::::into)?; - - // Update user information - if let Some(name) = user_name { - cred.user.name = Some(name.to_string()); - } - if let Some(display) = display_name { - cred.user.display_name = Some(display.to_string()); - } - - // Convert to our format and serialize - let our_cred = Credential::from_soft_fido2(&cred); - let cred_bytes = Zeroizing::new(our_cred.to_bytes().map_err(|e| { - debug!("Failed to serialize credential: {:?}", e); - Error::Storage(format!("Failed to serialize credential: {:?}", e)) - })?); - - // Write back the credential - self.write_credential_bytes(&cred, &cred_bytes)?; - - debug!( - "Successfully updated user info for credential on RP: {}", - cred.rp.id - ); - Ok(()) - } - - fn select_users(&self, rp_id: &str) -> Vec { - debug!("select_users called for RP: {}", rp_id); - - // Use index to get credential IDs for this RP - let cred_ids = match self.indexes.rp.get(rp_id) { - Some(ids) => ids, - None => { - debug!("No credentials found for RP: {}", rp_id); - return Vec::new(); - } - }; - - // Load only credentials for this specific RP (decrypt only what's needed) - let users: Vec = cred_ids - .iter() - .filter_map(|cred_id| { - let path_info = self.indexes.id.get(cred_id)?; - let path = path_info.to_path(&self.get_fido2_path()); - let cred = self.read_credential_from_path_no_cache(&path).ok()?; - Some(String::from_utf8_lossy(&cred.user.id).to_string()) - }) - .collect(); - - debug!("Found {} users for RP: {}", users.len(), rp_id); - users - } - fn count_credentials(&self) -> usize { let count = self.indexes.id.len(); debug!("count_credentials: {}", count); count } - fn get_relying_parties(&self) -> soft_fido2::Result> { - debug!("get_relying_parties called"); - - // Extract RP info directly from path structure - no file loading/decryption needed! - let rp_list: Vec = self - .indexes - .rp - .keys() - .map(|rp_id| soft_fido2_ctap::types::RelyingParty { - id: rp_id.clone(), - name: None, // Name not available in path structure - }) - .collect(); - - debug!("Found {} relying parties", rp_list.len()); - Ok(rp_list) - } - fn disable_user_verification(&self) -> bool { // Pass backend doesn't support user verification true diff --git a/cmd/passless/src/storage/tpm/mod.rs b/cmd/passless/src/storage/tpm/mod.rs index d6e07fc..26fa20a 100644 --- a/cmd/passless/src/storage/tpm/mod.rs +++ b/cmd/passless/src/storage/tpm/mod.rs @@ -708,7 +708,7 @@ impl CredentialStorage for TpmStorageAdapter { self.find_next() } - fn read(&mut self, id: &[u8], _rp: &str) -> Result { + fn read(&mut self, id: &[u8]) -> Result { self.cache.evict_expired(); debug!("read called with id: {}", bytes_to_hex(id)); @@ -717,7 +717,7 @@ impl CredentialStorage for TpmStorageAdapter { self.read_credential_by_id(id) } - fn write(&mut self, _id: &[u8], _rp: &str, cred_ref: soft_fido2::CredentialRef) -> Result<()> { + fn write(&mut self, cred_ref: soft_fido2::CredentialRef) -> Result<()> { self.cache.evict_expired(); debug!("write called for RP: {}", cred_ref.rp_id); @@ -736,87 +736,12 @@ impl CredentialStorage for TpmStorageAdapter { self.delete_credential(id) } - fn update_user_info( - &mut self, - id: &[u8], - user_name: Option<&str>, - display_name: Option<&str>, - ) -> Result<()> { - self.cache.evict_expired(); - - debug!("update_user_info called with id: {}", bytes_to_hex(id)); - - // Read the credential - let mut cred = self.read_credential_by_id(id)?; - - // Update user information - if let Some(name) = user_name { - cred.user.name = Some(name.to_string()); - } - if let Some(display) = display_name { - cred.user.display_name = Some(display.to_string()); - } - - // Write back the credential - self.write_credential(&cred)?; - - debug!( - "Successfully updated user info for credential on RP: {}", - cred.rp.id - ); - Ok(()) - } - - fn select_users(&self, rp_id: &str) -> Vec { - debug!("select_users called for RP: {}", rp_id); - - let cred_ids = match self.indexes.rp.get(rp_id) { - Some(ids) => ids, - None => { - debug!("No credentials found for RP: {}", rp_id); - return Vec::new(); - } - }; - - // Load only credentials for this specific RP (unseal only what's needed) - let users: Vec = cred_ids - .iter() - .filter_map(|cred_id| { - let path_info = self.indexes.id.get(cred_id)?; - let path = path_info.to_path(&self.storage_dir); - let cred = self.read_credential_from_path_no_cache(&path).ok()?; - Some(String::from_utf8_lossy(&cred.user.id).to_string()) - }) - .collect(); - - debug!("Found {} users for RP: {}", users.len(), rp_id); - users - } - fn count_credentials(&self) -> usize { let count = self.indexes.id.len(); debug!("count_credentials: {}", count); count } - fn get_relying_parties(&self) -> Result> { - debug!("get_relying_parties called"); - - // Extract RP info directly from path structure - no file loading/unsealing needed! - let rp_list: Vec = self - .indexes - .rp - .keys() - .map(|rp_id| soft_fido2_ctap::types::RelyingParty { - id: rp_id.clone(), - name: None, // Name not available in path structure - }) - .collect(); - - debug!("Found {} relying parties", rp_list.len()); - Ok(rp_list) - } - fn disable_user_verification(&self) -> bool { // TPM backend supports user verification false