credo/scope/
scope_state.rs1use std::collections::{BTreeSet, HashMap};
2
3use litl::impl_debug_as_litl;
4use ridl::{
5 asymm_encr::{AsymmDecryptionError, RecipientID},
6 hashing::HashOf,
7 symm_encr::{KeyID, KeySecret},
8};
9use serde_derive::{Deserialize, Serialize};
10use thiserror::Error;
11use ti64::MsSinceEpoch;
12
13use crate::{claims_and_permissions_v1::CredoV1Claim, ClaimID, ClaimBody, Credential, ScopeID};
14
15use super::combined_claim_set::ClaimInvalidReason;
16
17pub type ValidClaims = HashMap<ClaimID, (CredoV1Claim, ScopeID)>;
18pub type InvalidClaims = HashMap<ClaimID, ClaimInvalidReason>;
19pub type SecretRecipients = HashMap<String, BTreeSet<RecipientID>>;
20pub type ScopeSecretRecipientState = HashOf<SecretRecipients>;
21
22#[derive(Clone, Serialize, Deserialize)]
23pub struct ScopeState {
24 pub valid_claims: ValidClaims,
25 pub invalid_claims: InvalidClaims,
26 pub secret_recipients: SecretRecipients,
27}
28
29impl_debug_as_litl!(ScopeState);
30
31impl ScopeState {
32 pub fn secret_state(&self) -> ScopeSecretRecipientState {
33 HashOf::hash(&self.secret_recipients)
34 }
35
36 pub fn newest_shared_secret_ids(&self, secret_kind: &str) -> HashMap<ScopeID, KeyID> {
37 let mut newest_shared_secret_per_scope = HashMap::<ScopeID, (KeyID, MsSinceEpoch)>::new();
38
39 for (claim, source_scope) in self.valid_claims.values() {
40 if let ClaimBody::RevealSharedSecret {
41 key_id,
42 secret_kind: revealed_secret_kind,
43 ..
44 } = &claim.body
45 {
46 if secret_kind == revealed_secret_kind {
47 match newest_shared_secret_per_scope.entry(*source_scope) {
48 std::collections::hash_map::Entry::Occupied(mut existing) => {
49 if existing.get().1 < claim.made_at {
50 existing.insert((*key_id, claim.made_at));
51 }
52 }
53 std::collections::hash_map::Entry::Vacant(vacant) => {
54 vacant.insert((*key_id, claim.made_at));
55 }
56 }
57 }
58 }
59 }
60
61 newest_shared_secret_per_scope
62 .into_iter()
63 .map(|(scope, (key_id, _))| (scope, key_id))
64 .collect()
65 }
66
67 pub fn get_current_shared_secrets(
68 &self,
69 secret_kind: &str,
70 credentials_to_try: &[Credential],
71 ) -> Result<HashMap<ScopeID, Result<KeySecret, GetSharedSecretError>>, GetSharedSecretError>
72 {
73 let newest_key_ids = self.newest_shared_secret_ids(secret_kind);
74
75 if newest_key_ids.is_empty() {
76 return Err(GetSharedSecretError::NoNewestKeyId);
77 }
78
79 Ok(newest_key_ids
80 .into_iter()
81 .map(|(scope, key_id)| {
82 (
83 scope,
84 self.get_shared_secret(secret_kind, key_id, credentials_to_try),
85 )
86 })
87 .collect())
88 }
89
90 pub fn get_shared_secret(
91 &self,
92 shared_secret_kind: &str,
93 shared_secret_key_id: KeyID,
94 credentials_to_try: &[Credential],
95 ) -> Result<KeySecret, GetSharedSecretError> {
96 let mut encrypted_per_recipient_entries = self
97 .valid_claims
98 .iter()
99 .filter_map(|(_, (claim, _))| match &claim.body {
100 ClaimBody::RevealSharedSecret {
101 key_id,
102 encrypted_per_recipient,
103 secret_kind,
104 } if key_id == &shared_secret_key_id && secret_kind == shared_secret_kind => {
105 Some(encrypted_per_recipient)
106 }
107 _ => None,
108 })
109 .flatten()
110 .peekable();
111
112 if encrypted_per_recipient_entries.peek().is_none() {
113 Err(GetSharedSecretError::NoRevelationFound(
114 shared_secret_kind.to_string(),
115 shared_secret_key_id,
116 ))
117 } else if let Some((credential, matching_encrypted_key)) = encrypted_per_recipient_entries
118 .find_map(|(recipent, encrypted_key)| {
119 credentials_to_try.iter().find_map(|credential| {
120 if recipent == &credential.for_accepting_secrets.pub_id() {
121 Some((credential, encrypted_key))
122 } else {
123 None
124 }
125 })
126 })
127 {
128 Ok(credential
129 .for_accepting_secrets
130 .decrypt(matching_encrypted_key)?)
131 } else {
132 Err(GetSharedSecretError::NoRevelationFoundForCredentials(
133 shared_secret_kind.to_string(),
134 shared_secret_key_id,
135 credentials_to_try
136 .iter()
137 .map(|c| c.for_accepting_secrets.pub_id())
138 .collect(),
139 ))
140 }
141 }
142}
143
144#[derive(Error, Debug)]
145pub enum GetSharedSecretError {
146 #[error("Could not find newest key id")]
147 NoNewestKeyId,
148 #[error("Could not find revelation of shared secret of kind {0}, id {1:?}")]
149 NoRevelationFound(String, KeyID),
150 #[error("Could not find revelation of shared secret of kind {0}, id {1:?} for available credentials {2:?}")]
151 NoRevelationFoundForCredentials(String, KeyID, Vec<RecipientID>),
152 #[error("No credentials for scope")]
153 NoCredentialsForScope,
154 #[error(transparent)]
155 DecryptionError(#[from] AsymmDecryptionError),
156}