use core::fmt;
use std::sync::{Mutex, PoisonError, RwLock};
use indexmap::IndexMap;
use super::local::{self, Entry, SeededRandom};
use super::{KeyId, KeySpec, KeyStore, KeyStoreCapabilities, KeyStoreError};
use crate::jose::{Jwk, JwsAlgorithm};
pub struct InMemoryKeyStore {
keys: RwLock<IndexMap<KeyId, Entry>>,
seeded: Option<Mutex<SeededRandom>>,
}
impl Default for InMemoryKeyStore {
fn default() -> Self {
Self::new()
}
}
impl InMemoryKeyStore {
pub fn new() -> Self {
Self {
keys: RwLock::new(IndexMap::new()),
seeded: None,
}
}
pub fn with_seed(seed: [u8; 32]) -> Self {
Self {
keys: RwLock::new(IndexMap::new()),
seeded: Some(Mutex::new(SeededRandom::new(seed))),
}
}
pub fn key_ids(&self) -> Vec<KeyId> {
self.keys
.read()
.unwrap_or_else(PoisonError::into_inner)
.keys()
.cloned()
.collect()
}
pub fn is_seeded(&self) -> bool {
self.seeded.is_some()
}
fn random(&self, buf: &mut [u8]) -> Result<(), KeyStoreError> {
match &self.seeded {
Some(seeded) => {
seeded
.lock()
.unwrap_or_else(PoisonError::into_inner)
.fill(buf);
Ok(())
}
None => local::os_random(buf),
}
}
}
impl fmt::Debug for InMemoryKeyStore {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("InMemoryKeyStore")
.field("keys", &self.key_ids())
.field("seeded", &self.is_seeded())
.finish()
}
}
#[async_trait::async_trait]
impl KeyStore for InMemoryKeyStore {
fn capabilities(&self) -> KeyStoreCapabilities {
KeyStoreCapabilities::new(local::ALGORITHMS.iter().copied())
.with_generate()
.with_delete()
}
async fn public_jwk(&self, key: &KeyId) -> Result<Jwk, KeyStoreError> {
self.keys
.read()
.unwrap_or_else(PoisonError::into_inner)
.get(key)
.map(Entry::public_jwk)
.ok_or_else(|| KeyStoreError::NotFound(key.clone()))
}
async fn sign(
&self,
key: &KeyId,
alg: JwsAlgorithm,
data: &[u8],
) -> Result<Vec<u8>, KeyStoreError> {
self.keys
.read()
.unwrap_or_else(PoisonError::into_inner)
.get(key)
.ok_or_else(|| KeyStoreError::NotFound(key.clone()))?
.sign(key, alg, data)
}
async fn exists(&self, key: &KeyId) -> Result<bool, KeyStoreError> {
Ok(self
.keys
.read()
.unwrap_or_else(PoisonError::into_inner)
.contains_key(key))
}
async fn generate(&self, spec: KeySpec) -> Result<KeyId, KeyStoreError> {
let (id, key) = local::generate(&spec, &mut |buf| self.random(buf))?;
let mut keys = self.keys.write().unwrap_or_else(PoisonError::into_inner);
if keys.contains_key(&id) {
return Err(KeyStoreError::AlreadyExists(id));
}
keys.insert(id.clone(), Entry::new(&id, key, spec.alg));
Ok(id)
}
async fn delete(&self, key: &KeyId) -> Result<(), KeyStoreError> {
self.keys
.write()
.unwrap_or_else(PoisonError::into_inner)
.shift_remove(key)
.map(drop)
.ok_or_else(|| KeyStoreError::NotFound(key.clone()))
}
}