use super::{Error, HmacSha256Snafu};
use hex::encode;
use hmac::{Hmac, KeyInit, Mac};
use sha2::Sha256;
use snafu::ResultExt;
use std::sync::Arc;
use std::sync::RwLock;
type Result<T> = std::result::Result<T, Error>;
type HmacSha256 = Hmac<Sha256>;
enum KeyStore {
Static(Vec<Vec<u8>>),
Shared(Arc<RwLock<Vec<Vec<u8>>>>),
}
pub struct KeyGrip {
store: KeyStore,
}
fn sign_with_key(data: &[u8], key: &[u8]) -> Result<String> {
let mut mac = HmacSha256::new_from_slice(key).context(HmacSha256Snafu)?;
mac.update(data);
Ok(encode(mac.finalize().into_bytes()))
}
impl KeyGrip {
pub fn new(keys: Vec<Vec<u8>>) -> Result<Self> {
if keys.is_empty() {
return Err(Error::KeyGripEmpty);
}
Ok(Self {
store: KeyStore::Static(keys),
})
}
pub fn new_with_lock(keys: Vec<Vec<u8>>) -> Result<Self> {
if keys.is_empty() {
return Err(Error::KeyGripEmpty);
}
Ok(Self {
store: KeyStore::Shared(Arc::new(RwLock::new(keys))),
})
}
fn with_keys<F, R>(&self, f: F) -> R
where
F: FnOnce(&[Vec<u8>]) -> R,
{
match &self.store {
KeyStore::Static(keys) => f(keys),
KeyStore::Shared(lock_keys) => {
if let Ok(keys) = lock_keys.read() {
f(&keys)
} else {
f(&[])
}
}
}
}
pub fn update_keys(&self, new_keys: Vec<Vec<u8>>) {
if let KeyStore::Shared(lock_keys) = &self.store
&& let Ok(mut keys) = lock_keys.write()
{
*keys = new_keys;
}
}
fn index(&self, data: &[u8], digest: &str) -> Result<Option<usize>> {
self.with_keys(|keys| {
for (index, key) in keys.iter().enumerate() {
match sign_with_key(data, key) {
Ok(signature) if signature == digest => return Ok(Some(index)),
Ok(_) => continue,
Err(e) => return Err(e),
}
}
Ok(None)
})
}
pub fn sign(&self, data: &[u8]) -> Result<String> {
self.with_keys(|keys| {
let key = keys.first().ok_or(Error::KeyGripEmpty)?;
sign_with_key(data, key)
})
}
pub fn verify(&self, data: &[u8], digest: &str) -> Result<(bool, bool)> {
match self.index(data, digest)? {
Some(0) => Ok((true, true)), Some(_) => Ok((true, false)), None => Ok((false, false)), }
}
}