use std::collections::{BTreeSet, HashMap};
use litl::impl_debug_as_litl;
use ridl::{
asymm_encr::{AsymmDecryptionError, RecipientID},
hashing::HashOf,
symm_encr::{KeyID, KeySecret},
};
use serde_derive::{Deserialize, Serialize};
use thiserror::Error;
use ti64::MsSinceEpoch;
use crate::{claims_and_permissions_v1::CredoV1Claim, ClaimID, ClaimBody, Credential, ScopeID};
use super::combined_claim_set::ClaimInvalidReason;
pub type ValidClaims = HashMap<ClaimID, (CredoV1Claim, ScopeID)>;
pub type InvalidClaims = HashMap<ClaimID, ClaimInvalidReason>;
pub type SecretRecipients = HashMap<String, BTreeSet<RecipientID>>;
pub type ScopeSecretRecipientState = HashOf<SecretRecipients>;
#[derive(Clone, Serialize, Deserialize)]
pub struct ScopeState {
pub valid_claims: ValidClaims,
pub invalid_claims: InvalidClaims,
pub secret_recipients: SecretRecipients,
}
impl_debug_as_litl!(ScopeState);
impl ScopeState {
pub fn secret_state(&self) -> ScopeSecretRecipientState {
HashOf::hash(&self.secret_recipients)
}
pub fn newest_shared_secret_ids(&self, secret_kind: &str) -> HashMap<ScopeID, KeyID> {
let mut newest_shared_secret_per_scope = HashMap::<ScopeID, (KeyID, MsSinceEpoch)>::new();
for (claim, source_scope) in self.valid_claims.values() {
if let ClaimBody::RevealSharedSecret {
key_id,
secret_kind: revealed_secret_kind,
..
} = &claim.body
{
if secret_kind == revealed_secret_kind {
match newest_shared_secret_per_scope.entry(*source_scope) {
std::collections::hash_map::Entry::Occupied(mut existing) => {
if existing.get().1 < claim.made_at {
existing.insert((*key_id, claim.made_at));
}
}
std::collections::hash_map::Entry::Vacant(vacant) => {
vacant.insert((*key_id, claim.made_at));
}
}
}
}
}
newest_shared_secret_per_scope
.into_iter()
.map(|(scope, (key_id, _))| (scope, key_id))
.collect()
}
pub fn get_current_shared_secrets(
&self,
secret_kind: &str,
credentials_to_try: &[Credential],
) -> Result<HashMap<ScopeID, Result<KeySecret, GetSharedSecretError>>, GetSharedSecretError>
{
let newest_key_ids = self.newest_shared_secret_ids(secret_kind);
if newest_key_ids.is_empty() {
return Err(GetSharedSecretError::NoNewestKeyId);
}
Ok(newest_key_ids
.into_iter()
.map(|(scope, key_id)| {
(
scope,
self.get_shared_secret(secret_kind, key_id, credentials_to_try),
)
})
.collect())
}
pub fn get_shared_secret(
&self,
shared_secret_kind: &str,
shared_secret_key_id: KeyID,
credentials_to_try: &[Credential],
) -> Result<KeySecret, GetSharedSecretError> {
let mut encrypted_per_recipient_entries = self
.valid_claims
.iter()
.filter_map(|(_, (claim, _))| match &claim.body {
ClaimBody::RevealSharedSecret {
key_id,
encrypted_per_recipient,
secret_kind,
} if key_id == &shared_secret_key_id && secret_kind == shared_secret_kind => {
Some(encrypted_per_recipient)
}
_ => None,
})
.flatten()
.peekable();
if encrypted_per_recipient_entries.peek().is_none() {
Err(GetSharedSecretError::NoRevelationFound(
shared_secret_kind.to_string(),
shared_secret_key_id,
))
} else if let Some((credential, matching_encrypted_key)) = encrypted_per_recipient_entries
.find_map(|(recipent, encrypted_key)| {
credentials_to_try.iter().find_map(|credential| {
if recipent == &credential.for_accepting_secrets.pub_id() {
Some((credential, encrypted_key))
} else {
None
}
})
})
{
Ok(credential
.for_accepting_secrets
.decrypt(matching_encrypted_key)?)
} else {
Err(GetSharedSecretError::NoRevelationFoundForCredentials(
shared_secret_kind.to_string(),
shared_secret_key_id,
credentials_to_try
.iter()
.map(|c| c.for_accepting_secrets.pub_id())
.collect(),
))
}
}
}
#[derive(Error, Debug)]
pub enum GetSharedSecretError {
#[error("Could not find newest key id")]
NoNewestKeyId,
#[error("Could not find revelation of shared secret of kind {0}, id {1:?}")]
NoRevelationFound(String, KeyID),
#[error("Could not find revelation of shared secret of kind {0}, id {1:?} for available credentials {2:?}")]
NoRevelationFoundForCredentials(String, KeyID, Vec<RecipientID>),
#[error("No credentials for scope")]
NoCredentialsForScope,
#[error(transparent)]
DecryptionError(#[from] AsymmDecryptionError),
}