Merge 'impl linux credential storage' #115

From PR description:

See the test_basic() test in linux_key_storage.rs. I ran it successfully
on my MacOS machine and a linux VM. The BasicFileStorage impl just
stores each Keypair as a SerializableKeypair json object with the file
name as the public key hex in ~/.notedeck_credentials. The
SerializableKeypair uses the nip49 EncryptedSecretKey, but we just use
an empty string as the password for now.

The BasicFileStorage impl works in MacOS, but it only conditionally
compiles to linux for simplicity.

pub enum KeyStorageResponse<R> {
    Waiting,
    ReceivedResult(Result<R, KeyStorageError>),
}

This is used as a response so that it's possible for the storage impl to
be async, since secret_service is async. It seems that secret_service
would allow for a more robust linux key storage impl so I went ahead and
made the API compatible with an async impl.

* kernelkind (1):
      impl linux credential storage
This commit is contained in:
William Casarin
2024-07-01 10:42:46 -07:00
9 changed files with 319 additions and 33 deletions

View File

@@ -5,7 +5,9 @@ use enostr::{Keypair, Pubkey, SecretKey};
use security_framework::item::{ItemClass, ItemSearchOptions, Limit, SearchResult};
use security_framework::passwords::{delete_generic_password, set_generic_password};
use crate::key_storage::KeyStorageError;
use crate::key_storage::{KeyStorage, KeyStorageError, KeyStorageResponse};
use tracing::error;
pub struct MacOSKeyStorage<'a> {
pub service_name: &'a str,
@@ -16,7 +18,7 @@ impl<'a> MacOSKeyStorage<'a> {
MacOSKeyStorage { service_name }
}
pub fn add_key(&self, key: &Keypair) -> Result<(), KeyStorageError> {
fn add_key(&self, key: &Keypair) -> Result<(), KeyStorageError> {
match set_generic_password(
self.service_name,
key.pubkey.hex().as_str(),
@@ -52,7 +54,7 @@ impl<'a> MacOSKeyStorage<'a> {
accounts
}
pub fn get_pubkeys(&self) -> Vec<Pubkey> {
fn get_pubkeys(&self) -> Vec<Pubkey> {
self.get_pubkey_strings()
.iter_mut()
.filter_map(|pubkey_str| Pubkey::from_hex(pubkey_str.as_str()).ok())
@@ -84,7 +86,7 @@ impl<'a> MacOSKeyStorage<'a> {
}
}
pub fn get_all_keypairs(&self) -> Vec<Keypair> {
fn get_all_keypairs(&self) -> Vec<Keypair> {
self.get_pubkeys()
.iter()
.map(|pubkey| {
@@ -94,17 +96,31 @@ impl<'a> MacOSKeyStorage<'a> {
.collect()
}
pub fn delete_key(&self, pubkey: &Pubkey) -> Result<(), KeyStorageError> {
fn delete_key(&self, pubkey: &Pubkey) -> Result<(), KeyStorageError> {
match delete_generic_password(self.service_name, pubkey.hex().as_str()) {
Ok(_) => Ok(()),
Err(e) => {
println!("got error: {}", e);
error!("delete key error {}", e);
Err(KeyStorageError::Removal(pubkey.hex()))
}
}
}
}
impl<'a> KeyStorage for MacOSKeyStorage<'a> {
fn add_key(&self, key: &Keypair) -> KeyStorageResponse<()> {
KeyStorageResponse::ReceivedResult(self.add_key(key))
}
fn get_keys(&self) -> KeyStorageResponse<Vec<Keypair>> {
KeyStorageResponse::ReceivedResult(Ok(self.get_all_keypairs()))
}
fn remove_key(&self, key: &Keypair) -> KeyStorageResponse<()> {
KeyStorageResponse::ReceivedResult(self.delete_key(&key.pubkey))
}
}
#[cfg(test)]
mod tests {
use super::*;