use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::crypto::x25519::{PublicKey, SecretKey, X25519Error};
use crate::crypto::xeddsa::{XEdDSAError, XSignature};
use crate::crypto::{Rng, RngError};
use crate::key_bundle::{
Lifetime, LongTermKeyBundle, OneTimeKeyBundle, OneTimePreKey, OneTimePreKeyId, PreKey,
PreKeyId, latest_prekey,
};
use crate::traits::{IdentityManager, PreKeyManager};
#[derive(Clone, Debug)]
pub struct KeyManager;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct KeyManagerState {
identity_secret: SecretKey,
identity_key: PublicKey,
prekeys: PreKeyBundlesState,
onetime_secrets: HashMap<OneTimePreKeyId, (PreKeyId, SecretKey)>,
onetime_next_id: OneTimePreKeyId,
}
impl KeyManagerState {
pub fn prekey_bundles(&self) -> &PreKeyBundlesState {
&self.prekeys
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct PreKeyBundlesState(HashMap<PreKeyId, PreKeyBundle>);
impl PreKeyBundlesState {
fn new() -> Self {
Self::default()
}
fn latest(&self) -> Option<PreKeyBundle> {
let prekeys = self.0.values().map(|state| &state.prekey).collect();
let latest = latest_prekey(prekeys);
latest.map(|prekey| {
self.0
.get(prekey.key())
.expect("we know the item exists in the set")
.clone()
})
}
fn contains(&self, id: &PreKeyId) -> bool {
self.0.contains_key(id)
}
#[allow(unused)]
fn len(&self) -> usize {
self.0.len()
}
fn get(&self, id: &PreKeyId) -> Option<&PreKeyBundle> {
self.0.get(id)
}
fn insert(mut self, bundle: PreKeyBundle) -> Self {
self.0.insert(bundle.id(), bundle);
self
}
#[allow(clippy::manual_retain)]
fn remove_expired(self) -> Self {
Self(
self.0
.into_iter()
.filter(|(_, prekey)| prekey.prekey.verify_lifetime().is_ok())
.collect(),
)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PreKeyBundle {
prekey: PreKey,
signature: XSignature,
secret: SecretKey,
}
impl PreKeyBundle {
pub fn new(
identity_secret: &SecretKey,
lifetime: Lifetime,
rng: &Rng,
) -> Result<Self, KeyManagerError> {
let secret = SecretKey::from_bytes(rng.random_array()?);
let prekey = PreKey::new(secret.public_key()?, lifetime);
let signature = prekey.sign(identity_secret, rng)?;
Ok(Self {
prekey,
signature,
secret,
})
}
pub fn id(&self) -> PreKeyId {
*self.prekey.key()
}
pub fn lifetime(&self) -> &Lifetime {
self.prekey.lifetime()
}
}
impl KeyManager {
pub fn init(identity_secret: &SecretKey) -> Result<KeyManagerState, KeyManagerError> {
Ok(KeyManagerState {
identity_key: identity_secret.public_key()?,
identity_secret: identity_secret.clone(),
prekeys: PreKeyBundlesState::new(),
onetime_secrets: HashMap::new(),
onetime_next_id: 0,
})
}
pub fn init_from_prekey_bundles(
identity_secret: &SecretKey,
prekeys: PreKeyBundlesState,
) -> Result<KeyManagerState, KeyManagerError> {
Ok(KeyManagerState {
identity_key: identity_secret.public_key()?,
identity_secret: identity_secret.clone(),
prekeys,
onetime_secrets: HashMap::new(),
onetime_next_id: 0,
})
}
#[cfg(any(test, feature = "test_utils"))]
pub fn init_and_generate_prekey(
identity_secret: &SecretKey,
lifetime: Lifetime,
rng: &Rng,
) -> Result<KeyManagerState, KeyManagerError> {
let bundle = PreKeyBundle::new(identity_secret, lifetime, rng)?;
let prekeys = PreKeyBundlesState::new().insert(bundle);
Ok(KeyManagerState {
identity_key: identity_secret.public_key()?,
identity_secret: identity_secret.clone(),
prekeys,
onetime_secrets: HashMap::new(),
onetime_next_id: 0,
})
}
#[allow(clippy::manual_retain)]
pub fn remove_expired(mut y: KeyManagerState) -> KeyManagerState {
y.prekeys = y.prekeys.remove_expired();
y.onetime_secrets = y
.onetime_secrets
.into_iter()
.filter(|(_, (prekey_id, _))| y.prekeys.contains(prekey_id))
.collect();
y
}
}
impl IdentityManager<KeyManagerState> for KeyManager {
fn identity_secret(y: &KeyManagerState) -> &SecretKey {
&y.identity_secret
}
}
impl PreKeyManager for KeyManager {
type State = KeyManagerState;
type Error = KeyManagerError;
fn prekey_secret<'a>(
y: &'a Self::State,
id: &'a PreKeyId,
) -> Result<&'a SecretKey, Self::Error> {
match y.prekeys.get(id) {
Some(prekey) => Ok(&prekey.secret),
None => Err(KeyManagerError::UnknownPreKeySecret(*id)),
}
}
fn rotate_prekey(
mut y: Self::State,
lifetime: Lifetime,
rng: &Rng,
) -> Result<Self::State, Self::Error> {
let prekey = PreKeyBundle::new(&y.identity_secret, lifetime, rng)?;
y.prekeys = y.prekeys.insert(prekey);
Ok(y)
}
fn prekey_bundle(y: &Self::State) -> Result<LongTermKeyBundle, Self::Error> {
y.prekeys
.latest()
.map(|latest| LongTermKeyBundle::new(y.identity_key, latest.prekey, latest.signature))
.ok_or(KeyManagerError::NoPreKeysAvailable)
}
fn generate_onetime_bundle(
mut y: Self::State,
rng: &Rng,
) -> Result<(Self::State, OneTimeKeyBundle), Self::Error> {
let latest = y
.prekeys
.latest()
.ok_or(KeyManagerError::NoPreKeysAvailable)?;
let onetime_secret = SecretKey::from_bytes(rng.random_array()?);
let onetime_key = OneTimePreKey::new(onetime_secret.public_key()?, y.onetime_next_id);
{
let existing_key = y
.onetime_secrets
.insert(onetime_key.id(), (latest.id(), onetime_secret));
assert!(
existing_key.is_none(),
"should never insert same id more than once"
);
};
let bundle = OneTimeKeyBundle::new(
y.identity_key,
latest.prekey,
latest.signature,
Some(onetime_key),
);
y.onetime_next_id += 1;
Ok((y, bundle))
}
fn use_onetime_secret(
mut y: Self::State,
id: OneTimePreKeyId,
) -> Result<(Self::State, Option<SecretKey>), Self::Error> {
match y.onetime_secrets.remove(&id) {
Some(secret) => Ok((y, Some(secret.1))),
None => Err(KeyManagerError::UnknownOneTimeSecret(id)),
}
}
}
#[derive(Debug, Error)]
pub enum KeyManagerError {
#[error(transparent)]
Rng(#[from] RngError),
#[error(transparent)]
XEdDSA(#[from] XEdDSAError),
#[error(transparent)]
X25519(#[from] X25519Error),
#[error("could not find one-time pre-key secret with id {0}")]
UnknownOneTimeSecret(OneTimePreKeyId),
#[error("could not find pre-key secret with id {0}")]
UnknownPreKeySecret(PreKeyId),
#[error("no valid pre-keys available, they are either expired or too early")]
NoPreKeysAvailable,
}
#[cfg(test)]
mod tests {
use std::time::{SystemTime, UNIX_EPOCH};
use crate::crypto::Rng;
use crate::crypto::x25519::SecretKey;
use crate::key_bundle::Lifetime;
use crate::key_manager::KeyManagerError;
use crate::traits::KeyBundle;
use super::{KeyManager, PreKeyManager};
#[test]
fn generate_onetime_keys() {
let rng = Rng::from_seed([1; 32]);
let identity_secret = SecretKey::from_bytes(rng.random_array().unwrap());
let state =
KeyManager::init_and_generate_prekey(&identity_secret, Lifetime::default(), &rng)
.unwrap();
let (state, bundle_1) = KeyManager::generate_onetime_bundle(state, &rng).unwrap();
let (state, bundle_2) = KeyManager::generate_onetime_bundle(state, &rng).unwrap();
assert_eq!(
bundle_1.signed_prekey(),
&KeyManager::prekey_secret(&state, bundle_1.signed_prekey())
.expect("non-expired prekey exists")
.public_key()
.unwrap()
);
assert_eq!(bundle_1.signed_prekey(), bundle_2.signed_prekey());
assert_eq!(
bundle_1.identity_key(),
&identity_secret.public_key().unwrap()
);
assert_eq!(
bundle_2.identity_key(),
&identity_secret.public_key().unwrap()
);
assert!(bundle_1.verify().is_ok());
assert!(bundle_2.verify().is_ok());
let (state, onetime_secret_1) =
KeyManager::use_onetime_secret(state, bundle_1.onetime_prekey_id().unwrap()).unwrap();
let (state, onetime_secret_2) =
KeyManager::use_onetime_secret(state, bundle_2.onetime_prekey_id().unwrap()).unwrap();
assert_eq!(state.onetime_secrets.len(), 0);
assert!(KeyManager::use_onetime_secret(state.clone(), 42).is_err());
assert!(
KeyManager::use_onetime_secret(state.clone(), bundle_1.onetime_prekey_id().unwrap())
.is_err()
);
assert!(
KeyManager::use_onetime_secret(state.clone(), bundle_2.onetime_prekey_id().unwrap())
.is_err()
);
assert_eq!(
bundle_1.onetime_prekey().unwrap(),
&onetime_secret_1.unwrap().public_key().unwrap()
);
assert_eq!(
bundle_2.onetime_prekey().unwrap(),
&onetime_secret_2.unwrap().public_key().unwrap()
);
assert_ne!(bundle_1.onetime_prekey(), bundle_2.onetime_prekey());
assert_ne!(bundle_1.onetime_prekey_id(), bundle_2.onetime_prekey_id());
}
#[test]
fn expired_prekey_bundles() {
let rng = Rng::from_seed([1; 32]);
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!")
.as_secs();
let identity_secret = SecretKey::from_bytes(rng.random_array().unwrap());
let y = KeyManager::init_and_generate_prekey(
&identity_secret,
Lifetime::from_range(now - 120, now - 60), &rng,
)
.unwrap();
assert!(matches!(
KeyManager::prekey_bundle(&y),
Err(KeyManagerError::NoPreKeysAvailable)
));
assert!(matches!(
KeyManager::generate_onetime_bundle(y.clone(), &rng),
Err(KeyManagerError::NoPreKeysAvailable)
));
let y_i = KeyManager::rotate_prekey(y, Lifetime::default(), &rng).unwrap();
assert!(KeyManager::prekey_bundle(&y_i).is_ok());
}
#[test]
fn garbage_collection() {
let rng = Rng::from_seed([1; 32]);
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!")
.as_secs();
let identity_secret = SecretKey::from_bytes(rng.random_array().unwrap());
let y = KeyManager::init_and_generate_prekey(
&identity_secret,
Lifetime::from_range(now - 120, now - 60), &rng,
)
.unwrap();
assert_eq!(y.prekeys.len(), 1);
let y = KeyManager::rotate_prekey(y, Lifetime::default(), &rng).unwrap();
assert_eq!(y.prekeys.len(), 2);
let y = KeyManager::remove_expired(y);
assert_eq!(y.prekeys.len(), 1);
}
}