1use std::collections::{HashMap, HashSet};
2
3use endr::ObjectID;
4use litl::impl_debug_as_litl;
5use ridl::{
6 asymm_encr::EncrFromAnon,
7 signing::SignerID,
8 symm_encr::{Encrypted, KeyID, KeySecret},
9};
10use serde_derive::{Deserialize, Serialize};
11
12use crate::{changes::RevealSecretToParent, crew::CrewID, tx::CrewChangeTxEntry};
13use crate::{
14 tx::CrewChangeTx, AddParent, AddRole, CrewChange, CrewRuleset, EntrustInfo, MakeStatement,
15 Member, MemberCredential, RemoveParent, RemoveRole, RevealSecret,
16};
17
18#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
19pub struct CrewState {
20 pub parents: HashSet<CrewID>,
21 pub roles: HashSet<(Member, String)>,
22 pub shared_secrets: HashMap<String, HashMap<SignerID, EncrFromAnon<KeySecret>>>,
23 pub entrusted_info: HashMap<String, HashMap<String, Encrypted<litl::Val>>>,
24 pub statements: HashSet<(String, litl::Val, SignerID)>,
25 #[serde(with = "serialize_as_vec")]
26 pub parent_secret_map: HashMap<(CrewID, String, KeyID), Encrypted<KeySecret>>,
27}
28impl_debug_as_litl!(CrewState);
29
30pub mod serialize_as_vec;
31
32impl Default for CrewState {
33 fn default() -> Self {
34 CrewState::new()
35 }
36}
37
38impl CrewState {
39 pub fn new() -> Self {
40 CrewState {
41 parents: HashSet::new(),
42 roles: HashSet::new(),
43 shared_secrets: HashMap::new(),
44 entrusted_info: HashMap::new(),
45 statements: HashSet::new(),
46 parent_secret_map: HashMap::new(),
47 }
48 }
49
50 pub fn apply_unchecked(&self, change: &CrewChange, made_by: &SignerID) -> Self {
51 let mut new_state = self.clone();
52
53 match change {
54 CrewChange::AddRole(AddRole { to, role }) => {
55 new_state.roles.insert((to.clone(), role.clone()));
56 }
57 CrewChange::RemoveRole(RemoveRole { from, role }) => {
58 new_state.roles.remove(&(from.clone(), role.clone()));
59 }
60 CrewChange::RevealSecret(RevealSecret {
61 secret_kind,
62 to,
63 encr,
64 }) => {
65 new_state
66 .shared_secrets
67 .entry(secret_kind.clone())
68 .or_default()
69 .insert(to.clone(), encr.clone());
70 }
71 CrewChange::RevealSecretToParent(RevealSecretToParent {
72 secret_kind,
73 parent_secret_id,
74 parent_id,
75 encr,
76 }) => {
77 new_state.parent_secret_map.insert(
78 (
79 parent_id.clone(),
80 secret_kind.clone(),
81 parent_secret_id.clone(),
82 ),
83 encr.clone(),
84 );
85 }
86 CrewChange::EntrustInfo(EntrustInfo {
87 to_secret_kind,
88 info_id,
89 info,
90 }) => {
91 new_state
92 .entrusted_info
93 .entry(to_secret_kind.clone())
94 .or_default()
95 .insert(info_id.clone(), info.clone());
96 }
97 CrewChange::AddParent(AddParent { parent }) => {
98 new_state.parents.insert(parent.clone());
99 }
100 CrewChange::RemoveParent(RemoveParent { parent }) => {
101 new_state.parents.remove(parent);
102 }
103 CrewChange::MakeStatement(MakeStatement { path, value }) => {
104 new_state
105 .statements
106 .insert((path.clone(), value.clone(), made_by.clone()));
107 }
108 }
109
110 new_state
111 }
112
113 pub fn apply<R: CrewRuleset>(
114 &self,
115 tx: CrewChangeTxEntry,
116 rule_set: R,
117 ) -> Result<CrewState, String> {
118 tx.correctly_signed()?;
119
120 let mut state = self.clone();
121
122 for (change_idx, change) in tx.1.content.attested.changes.iter().enumerate() {
123 rule_set
124 .is_valid_change(&state, &change, &tx.1.by, tx.1.content.attested.made_at)
125 .map_err(|err| format!("Error in change {} in tx: {}", change_idx, err))?;
126 state = state.apply_unchecked(&change, &tx.1.by);
127 }
128
129 Ok(state)
130 }
131
132 pub fn merge_parent_roles(&self, parent_state: &CrewState) -> CrewState {
133 let mut merged = self.clone();
134
135 for (member, role) in &parent_state.roles {
136 if !merged.roles.contains(&(member.clone(), role.clone())) {
137 merged.roles.insert((member.clone(), role.clone()));
138 }
139 }
140
141 merged
142 }
143
144 pub fn roles_of(&self, signer: &SignerID) -> HashSet<String> {
145 self.roles
146 .iter()
147 .filter(|(role_member, _)| &role_member.signer == signer)
148 .map(|(_, role)| role.clone())
149 .collect()
150 }
151
152 pub fn get_shared_secret(
153 &self,
154 secret_kind: &str,
155 credentials: &[MemberCredential],
156 ) -> Result<KeySecret, String> {
157 let (encrypted_secret, matching_credential) = self
158 .shared_secrets
159 .get(secret_kind)
160 .and_then(|secrets| {
161 credentials
162 .iter()
163 .find_map(|credential| secrets.get(&credential.pub_id().signer).map(|encr| (encr, credential)))
164 })
165 .ok_or_else(|| "No secret revelation found".to_owned())?;
166
167 matching_credential
168 .decrypt_secret(encrypted_secret)
169 .map_err(|err| err.to_string())
170 }
171
172 pub fn get_entrusted_info(
173 &self,
174 secret_kind: &str,
175 info_id: &str,
176 credentials: &[MemberCredential],
177 ) -> Result<litl::Val, String> {
178 let encrypted_info = self
179 .entrusted_info
180 .get(secret_kind)
181 .and_then(|infos| infos.get(info_id))
182 .ok_or_else(|| "No entrusted info found".to_owned())?;
183
184 let key_secret = self.get_shared_secret(secret_kind, credentials)?;
185 key_secret
186 .decrypt(encrypted_info)
187 .map_err(|err| err.to_string())
188 }
189
190 pub fn merge_with_parent_states(
191 &self,
192 parent_states: HashMap<ObjectID, CrewState>,
193 ) -> Result<CrewState, String> {
194 todo!()
197 }
198
199 pub fn statements_with_prefix(&self, prefix: &str) -> HashMap<String, litl::Val> {
200 self.statements
201 .iter()
202 .filter(|(path, _, _)| path.starts_with(prefix))
203 .map(|(path, value, _)| (path.clone(), value.clone()))
204 .collect()
205 }
206}