notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 69b651bbc5ebf6251f4dd4c3923f056894f4e488
parent d1f38c3d19c61d0db92d86ab649dcb3998a5d9bc
Author: kernelkind <kernelkind@gmail.com>
Date:   Sat, 15 Mar 2025 15:05:27 -0400

remove security framework storage

Signed-off-by: kernelkind <kernelkind@gmail.com>

Diffstat:
MCargo.lock | 1-
MCargo.toml | 1-
Mcrates/notedeck/Cargo.toml | 3---
Mcrates/notedeck/src/storage/key_storage_impl.rs | 15---------------
Mcrates/notedeck/src/storage/mod.rs | 3---
Dcrates/notedeck/src/storage/security_framework_key_storage.rs | 198-------------------------------------------------------------------------------
6 files changed, 0 insertions(+), 221 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -2741,7 +2741,6 @@ dependencies = [ "poll-promise", "puffin", "puffin_egui", - "security-framework", "serde", "serde_json", "sha2", diff --git a/Cargo.toml b/Cargo.toml @@ -57,7 +57,6 @@ tempfile = "3.13.0" url = "2.5.2" urlencoding = "2.1.3" uuid = { version = "1.10.0", features = ["v4"] } -security-framework = "2.11.0" sha2 = "0.10.8" bincode = "1.3.3" mime_guess = "2.0.5" diff --git a/crates/notedeck/Cargo.toml b/crates/notedeck/Cargo.toml @@ -35,9 +35,6 @@ egui-winit = { workspace = true } [dev-dependencies] tempfile = { workspace = true } -[target.'cfg(target_os = "macos")'.dependencies] -security-framework = { workspace = true } - [target.'cfg(target_os = "android")'.dependencies] jni = { workspace = true } diff --git a/crates/notedeck/src/storage/key_storage_impl.rs b/crates/notedeck/src/storage/key_storage_impl.rs @@ -3,15 +3,10 @@ use enostr::{Keypair, Pubkey}; use super::file_key_storage::FileKeyStorage; use crate::Result; -#[cfg(target_os = "macos")] -use super::security_framework_key_storage::SecurityFrameworkKeyStorage; - #[derive(Debug, PartialEq)] pub enum KeyStorageType { None, FileSystem(FileKeyStorage), - #[cfg(target_os = "macos")] - SecurityFramework(SecurityFrameworkKeyStorage), } #[allow(dead_code)] @@ -43,8 +38,6 @@ impl KeyStorageType { match self { Self::None => KeyStorageResponse::ReceivedResult(Ok(Vec::new())), Self::FileSystem(f) => f.get_keys(), - #[cfg(target_os = "macos")] - Self::SecurityFramework(f) => f.get_keys(), } } @@ -53,8 +46,6 @@ impl KeyStorageType { match self { Self::None => KeyStorageResponse::ReceivedResult(Ok(())), Self::FileSystem(f) => f.add_key(key), - #[cfg(target_os = "macos")] - Self::SecurityFramework(f) => f.add_key(key), } } @@ -63,8 +54,6 @@ impl KeyStorageType { match self { Self::None => KeyStorageResponse::ReceivedResult(Ok(())), Self::FileSystem(f) => f.remove_key(key), - #[cfg(target_os = "macos")] - Self::SecurityFramework(f) => f.remove_key(key), } } @@ -72,8 +61,6 @@ impl KeyStorageType { match self { Self::None => KeyStorageResponse::ReceivedResult(Ok(None)), Self::FileSystem(f) => f.get_selected_key(), - #[cfg(target_os = "macos")] - Self::SecurityFramework(_) => unimplemented!(), } } @@ -81,8 +68,6 @@ impl KeyStorageType { match self { Self::None => KeyStorageResponse::ReceivedResult(Ok(())), Self::FileSystem(f) => f.select_key(key), - #[cfg(target_os = "macos")] - Self::SecurityFramework(_) => unimplemented!(), } } } diff --git a/crates/notedeck/src/storage/mod.rs b/crates/notedeck/src/storage/mod.rs @@ -4,8 +4,5 @@ mod file_storage; pub use file_key_storage::FileKeyStorage; pub use file_storage::{delete_file, write_file, DataPath, DataPathType, Directory}; -#[cfg(target_os = "macos")] -mod security_framework_key_storage; - pub mod key_storage_impl; pub use key_storage_impl::{KeyStorageResponse, KeyStorageType}; diff --git a/crates/notedeck/src/storage/security_framework_key_storage.rs b/crates/notedeck/src/storage/security_framework_key_storage.rs @@ -1,198 +0,0 @@ -use std::borrow::Cow; - -use enostr::{Keypair, Pubkey, SecretKey}; -use security_framework::{ - item::{ItemClass, ItemSearchOptions, Limit, SearchResult}, - passwords::{delete_generic_password, set_generic_password}, -}; -use tracing::error; - -use crate::{Error, Result}; - -use super::KeyStorageResponse; - -#[derive(Debug, PartialEq)] -pub struct SecurityFrameworkKeyStorage { - pub service_name: Cow<'static, str>, -} - -impl SecurityFrameworkKeyStorage { - pub fn new(service_name: String) -> Self { - SecurityFrameworkKeyStorage { - service_name: Cow::Owned(service_name), - } - } - - fn add_key_internal(&self, key: &Keypair) -> Result<()> { - match set_generic_password( - &self.service_name, - key.pubkey.hex().as_str(), - key.secret_key - .as_ref() - .map_or_else(|| &[] as &[u8], |sc| sc.as_secret_bytes()), - ) { - Ok(_) => Ok(()), - Err(e) => Err(Error::Generic(e.to_string())), - } - } - - fn get_pubkey_strings(&self) -> Vec<String> { - let search_results = ItemSearchOptions::new() - .class(ItemClass::generic_password()) - .service(&self.service_name) - .load_attributes(true) - .limit(Limit::All) - .search(); - - let mut accounts = Vec::new(); - - if let Ok(search_results) = search_results { - for result in search_results { - if let Some(map) = result.simplify_dict() { - if let Some(val) = map.get("acct") { - accounts.push(val.clone()); - } - } - } - } - - accounts - } - - fn get_pubkeys(&self) -> Vec<Pubkey> { - self.get_pubkey_strings() - .iter_mut() - .filter_map(|pubkey_str| Pubkey::from_hex(pubkey_str.as_str()).ok()) - .collect() - } - - fn get_privkey_bytes_for(&self, account: &str) -> Option<Vec<u8>> { - let search_result = ItemSearchOptions::new() - .class(ItemClass::generic_password()) - .service(&self.service_name) - .load_data(true) - .account(account) - .search(); - - if let Ok(results) = search_result { - if let Some(SearchResult::Data(vec)) = results.first() { - return Some(vec.clone()); - } - } - - None - } - - fn get_secret_key_for_pubkey(&self, pubkey: &Pubkey) -> Option<SecretKey> { - if let Some(bytes) = self.get_privkey_bytes_for(pubkey.hex().as_str()) { - SecretKey::from_slice(bytes.as_slice()).ok() - } else { - None - } - } - - fn get_all_keypairs(&self) -> Vec<Keypair> { - self.get_pubkeys() - .iter() - .map(|pubkey| { - let maybe_secret = self.get_secret_key_for_pubkey(pubkey); - Keypair::new(*pubkey, maybe_secret) - }) - .collect() - } - - fn delete_key(&self, pubkey: &Pubkey) -> Result<()> { - match delete_generic_password(&self.service_name, pubkey.hex().as_str()) { - Ok(_) => Ok(()), - Err(e) => { - error!("delete key error {}", e); - Err(Error::Generic(e.to_string())) - } - } - } -} - -impl SecurityFrameworkKeyStorage { - pub fn add_key(&self, key: &Keypair) -> KeyStorageResponse<()> { - KeyStorageResponse::ReceivedResult(self.add_key_internal(key)) - } - - pub fn get_keys(&self) -> KeyStorageResponse<Vec<Keypair>> { - KeyStorageResponse::ReceivedResult(Ok(self.get_all_keypairs())) - } - - pub fn remove_key(&self, key: &Keypair) -> KeyStorageResponse<()> { - KeyStorageResponse::ReceivedResult(self.delete_key(&key.pubkey)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use enostr::FullKeypair; - - static TEST_SERVICE_NAME: &str = "NOTEDECKTEST"; - static STORAGE: SecurityFrameworkKeyStorage = SecurityFrameworkKeyStorage { - service_name: Cow::Borrowed(TEST_SERVICE_NAME), - }; - - // individual tests are ignored so test runner doesn't run them all concurrently - // TODO: a way to run them all serially should be devised - - #[test] - #[ignore] - fn add_and_remove_test_pubkey_only() { - let num_keys_before_test = STORAGE.get_pubkeys().len(); - - let keypair = FullKeypair::generate().to_keypair(); - let add_result = STORAGE.add_key_internal(&keypair); - assert!(add_result.is_ok()); - - let get_pubkeys_result = STORAGE.get_pubkeys(); - assert_eq!(get_pubkeys_result.len() - num_keys_before_test, 1); - - let remove_result = STORAGE.delete_key(&keypair.pubkey); - assert!(remove_result.is_ok()); - - let keys = STORAGE.get_pubkeys(); - assert_eq!(keys.len() - num_keys_before_test, 0); - } - - fn add_and_remove_full_n(n: usize) { - let num_keys_before_test = STORAGE.get_all_keypairs().len(); - // there must be zero keys in storage for the test to work as intended - assert_eq!(num_keys_before_test, 0); - - let expected_keypairs: Vec<Keypair> = (0..n) - .map(|_| FullKeypair::generate().to_keypair()) - .collect(); - - expected_keypairs.iter().for_each(|keypair| { - let add_result = STORAGE.add_key_internal(keypair); - assert!(add_result.is_ok()); - }); - - let asserted_keypairs = STORAGE.get_all_keypairs(); - assert_eq!(expected_keypairs, asserted_keypairs); - - expected_keypairs.iter().for_each(|keypair| { - let remove_result = STORAGE.delete_key(&keypair.pubkey); - assert!(remove_result.is_ok()); - }); - - let num_keys_after_test = STORAGE.get_all_keypairs().len(); - assert_eq!(num_keys_after_test, 0); - } - - #[test] - #[ignore] - fn add_and_remove_full() { - add_and_remove_full_n(1); - } - - #[test] - #[ignore] - fn add_and_remove_full_10() { - add_and_remove_full_n(10); - } -}