p2panda_encryption/data_scheme/
group_secret.rs1use std::collections::HashMap;
5use std::collections::hash_map::{IntoIter, Iter, Keys, Values};
6use std::fmt;
7use std::hash::Hash as StdHash;
8use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH};
9
10use p2panda_core::cbor::{DecodeError, EncodeError, decode_cbor, encode_cbor};
11use serde::de::{SeqAccess, Visitor};
12use serde::ser::SerializeSeq;
13use serde::{Deserialize, Serialize};
14use thiserror::Error;
15
16use crate::crypto::sha2::{SHA256_DIGEST_SIZE, sha2_256};
17use crate::crypto::{Rng, RngError, Secret};
18
19pub const GROUP_SECRET_SIZE: usize = 32;
21
22pub type GroupSecretId = [u8; SHA256_DIGEST_SIZE];
27
28pub type Timestamp = u64;
34
35#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
40pub struct GroupSecret(Secret<GROUP_SECRET_SIZE>, Timestamp);
41
42impl GroupSecret {
43 #[cfg(any(test, feature = "test_utils"))]
44 pub fn new(bytes: [u8; GROUP_SECRET_SIZE], timestamp: Timestamp) -> Self {
45 Self(Secret::from_bytes(bytes), timestamp)
46 }
47
48 pub(crate) fn from_rng(rng: &Rng) -> Result<Self, GroupSecretError> {
50 let bytes: [u8; GROUP_SECRET_SIZE] = rng.random_array()?;
51 Self::from_bytes(bytes)
52 }
53
54 pub(crate) fn from_bytes(bytes: [u8; GROUP_SECRET_SIZE]) -> Result<Self, GroupSecretError> {
56 let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
57 Ok(Self(Secret::from_bytes(bytes), now))
58 }
59
60 pub(crate) fn try_from_bytes(bytes: &[u8]) -> Result<Self, GroupSecretError> {
62 Ok(decode_cbor(bytes)?)
63 }
64
65 pub fn id(&self) -> GroupSecretId {
67 sha2_256(&[self.0.as_bytes()])
68 }
69
70 pub fn timestamp(&self) -> Timestamp {
72 self.1
73 }
74
75 pub(crate) fn set_timestamp(&mut self, timestamp: Timestamp) {
77 self.1 = timestamp;
78 }
79
80 pub(crate) fn as_bytes(&self) -> &[u8; GROUP_SECRET_SIZE] {
82 self.0.as_bytes()
83 }
84
85 pub(crate) fn to_bytes(&self) -> Result<Vec<u8>, GroupSecretError> {
87 Ok(encode_cbor(self)?)
88 }
89}
90
91impl StdHash for GroupSecret {
92 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
93 self.id().hash(state);
94 }
95}
96
97#[derive(Debug)]
105pub struct SecretBundle;
106
107#[derive(Debug, PartialEq, Eq)]
108#[cfg_attr(any(test, feature = "test_utils"), derive(Clone))]
109pub struct SecretBundleState {
110 secrets: HashMap<GroupSecretId, GroupSecret>,
111 latest: Option<GroupSecretId>,
112}
113
114impl SecretBundleState {
115 pub fn latest(&self) -> Option<&GroupSecret> {
117 self.latest.as_ref().and_then(|id| self.secrets.get(id))
118 }
119
120 pub fn get(&self, id: &GroupSecretId) -> Option<&GroupSecret> {
125 self.secrets.get(id)
126 }
127
128 pub fn contains(&self, id: &GroupSecretId) -> bool {
130 self.secrets.contains_key(id)
131 }
132
133 pub fn len(&self) -> usize {
135 self.secrets.len()
136 }
137
138 pub fn is_empty(&self) -> bool {
140 self.secrets.is_empty()
141 }
142
143 pub fn iter(&self) -> Iter<'_, GroupSecretId, GroupSecret> {
145 self.secrets.iter()
146 }
147
148 pub fn ids(&self) -> Keys<'_, GroupSecretId, GroupSecret> {
150 self.secrets.keys()
151 }
152
153 pub fn secrets(&self) -> Values<'_, GroupSecretId, GroupSecret> {
155 self.secrets.values()
156 }
157
158 pub(crate) fn to_bytes(&self) -> Result<Vec<u8>, GroupSecretError> {
160 Ok(encode_cbor(self)?)
161 }
162}
163
164impl std::iter::IntoIterator for SecretBundleState {
165 type Item = (GroupSecretId, GroupSecret);
166
167 type IntoIter = IntoIter<GroupSecretId, GroupSecret>;
168
169 fn into_iter(self) -> Self::IntoIter {
170 self.secrets.into_iter()
171 }
172}
173
174impl Serialize for SecretBundleState {
175 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
176 where
177 S: serde::Serializer,
178 {
179 let mut s = serializer.serialize_seq(Some(self.secrets.len()))?;
180 for secret in self.secrets.values() {
181 s.serialize_element(secret)?;
182 }
183 s.end()
184 }
185}
186
187impl<'de> Deserialize<'de> for SecretBundleState {
188 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
189 where
190 D: serde::Deserializer<'de>,
191 {
192 struct SecretListVisitor;
193
194 impl<'de> Visitor<'de> for SecretListVisitor {
195 type Value = Vec<GroupSecret>;
196
197 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
198 formatter.write_str("list of group secrets")
199 }
200
201 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
202 where
203 A: SeqAccess<'de>,
204 {
205 let mut result = Vec::new();
206 while let Some(secret) = seq.next_element()? {
207 result.push(secret);
208 }
209 Ok(result)
210 }
211 }
212
213 let secrets = deserializer.deserialize_seq(SecretListVisitor)?;
214
215 Ok(SecretBundle::from_secrets(secrets))
216 }
217}
218
219impl SecretBundle {
220 pub fn init() -> SecretBundleState {
222 SecretBundleState {
223 secrets: HashMap::new(),
224 latest: None,
225 }
226 }
227
228 pub fn from_secrets(secrets: Vec<GroupSecret>) -> SecretBundleState {
230 let secrets = HashMap::from_iter(secrets.into_iter().map(|secret| (secret.id(), secret)));
231 SecretBundleState {
232 latest: find_latest(&secrets),
233 secrets,
234 }
235 }
236
237 pub(crate) fn try_from_bytes(bytes: &[u8]) -> Result<SecretBundleState, GroupSecretError> {
239 Ok(decode_cbor(bytes)?)
240 }
241
242 pub fn generate(y: &SecretBundleState, rng: &Rng) -> Result<GroupSecret, GroupSecretError> {
249 let mut secret = GroupSecret::from_rng(rng)?;
250
251 let latest_timestamp = y.latest().map(|latest| latest.timestamp()).unwrap_or(0);
252 if secret.timestamp() <= latest_timestamp {
253 secret.set_timestamp(latest_timestamp + 1);
254 }
255
256 Ok(secret)
257 }
258
259 pub fn insert(mut y: SecretBundleState, secret: GroupSecret) -> SecretBundleState {
261 y.secrets.insert(secret.id(), secret);
262 y.latest = find_latest(&y.secrets);
263 y
264 }
265
266 pub fn remove(
268 mut y: SecretBundleState,
269 id: &GroupSecretId,
270 ) -> (SecretBundleState, Option<GroupSecret>) {
271 let result = y.secrets.remove(id);
272 y.latest = find_latest(&y.secrets);
273 (y, result)
274 }
275
276 pub fn extend(mut y: SecretBundleState, other: SecretBundleState) -> SecretBundleState {
278 y.secrets.extend(other.secrets);
279 y.latest = find_latest(&y.secrets);
280 y
281 }
282}
283
284fn find_latest(secrets: &HashMap<GroupSecretId, GroupSecret>) -> Option<GroupSecretId> {
287 let mut latest_timestamp: Timestamp = 0;
288 let mut latest_secret_id: Option<GroupSecretId> = None;
289 for (id, secret) in secrets {
290 let timestamp = secret.timestamp();
291 if latest_timestamp < timestamp
292 || (latest_timestamp == timestamp
293 && *id > latest_secret_id.unwrap_or([0; SHA256_DIGEST_SIZE]))
294 {
295 latest_timestamp = timestamp;
296 latest_secret_id = Some(id.to_owned());
297 }
298 }
299 latest_secret_id
300}
301
302#[derive(Debug, Error)]
303pub enum GroupSecretError {
304 #[error("the given key does not match the required 32 byte length")]
305 InvalidKeySize,
306
307 #[error(transparent)]
308 Rng(#[from] RngError),
309
310 #[error(transparent)]
311 Encode(#[from] EncodeError),
312
313 #[error(transparent)]
314 Decode(#[from] DecodeError),
315
316 #[error(transparent)]
317 SystemTime(#[from] SystemTimeError),
318}
319
320#[cfg(test)]
321mod tests {
322 use crate::Rng;
323
324 use super::{GroupSecret, SecretBundle};
325
326 #[test]
327 fn group_secret_bundle() {
328 let rng = Rng::from_seed([1; 32]);
329
330 let secret = GroupSecret::from_rng(&rng).unwrap();
331
332 let bundle_1 = SecretBundle::from_secrets(vec![secret.clone()]);
333 let bundle_2 = SecretBundle::from_secrets(vec![secret.clone()]);
334 assert_eq!(bundle_1.len(), 1);
335 assert_eq!(bundle_2.len(), 1);
336
337 assert_eq!(bundle_1.get(&secret.id()), bundle_2.get(&secret.id()));
338 assert!(bundle_1.get(&secret.id()).is_some());
339 assert!(bundle_1.contains(&secret.id()));
340
341 let unknown_secret = GroupSecret::from_rng(&rng).unwrap();
342 assert!(bundle_1.get(&unknown_secret.id()).is_none());
343 assert!(!bundle_1.contains(&unknown_secret.id()));
344
345 let secret_2 = GroupSecret::from_rng(&rng).unwrap();
346 let bundle_2 = SecretBundle::insert(bundle_2, secret_2.clone());
347 assert_eq!(bundle_2.len(), 2);
348
349 let bundle_1 = SecretBundle::extend(bundle_1, bundle_2);
350 assert_eq!(bundle_1.len(), 2);
351
352 let (bundle_1, result) = SecretBundle::remove(bundle_1, &secret_2.id());
353 assert_eq!(result, Some(secret_2));
354 assert_eq!(bundle_1.len(), 1);
355 }
356
357 #[test]
358 fn latest_secret() {
359 let bundle = SecretBundle::init();
360 assert!(bundle.latest().is_none());
361
362 let secret_1 = GroupSecret::new([1; 32], 234);
363 assert_eq!(secret_1.timestamp(), 234);
364 let secret_2 = GroupSecret::new([2; 32], 234); assert_eq!(secret_2.timestamp(), 234);
366 let secret_3 = GroupSecret::new([3; 32], 345);
367 assert_eq!(secret_3.timestamp(), 345);
368 let secret_4 = GroupSecret::new([4; 32], 123);
369 assert_eq!(secret_4.timestamp(), 123);
370
371 let bundle = SecretBundle::insert(bundle, secret_1.clone());
373 assert_eq!(bundle.len(), 1);
374 assert_eq!(bundle.latest(), Some(&secret_1));
375
376 let bundle = SecretBundle::insert(bundle, secret_2.clone());
379 assert_eq!(bundle.len(), 2);
380 assert_eq!(bundle.latest(), Some(&secret_2));
381
382 {
384 let bundle_2 = SecretBundle::init();
385 let bundle_2 = SecretBundle::insert(bundle_2, secret_2.clone());
386 let bundle_2 = SecretBundle::insert(bundle_2, secret_1.clone());
387 assert_eq!(bundle_2.latest(), Some(&secret_2));
388 }
389
390 let bundle = SecretBundle::insert(bundle, secret_3.clone());
392 assert_eq!(bundle.len(), 3);
393 assert_eq!(bundle.latest(), Some(&secret_3));
394
395 let bundle = SecretBundle::insert(bundle, secret_4.clone());
397 assert_eq!(bundle.len(), 4);
398 assert_eq!(bundle.latest(), Some(&secret_3));
399 }
400
401 #[test]
402 fn serde() {
403 let rng = Rng::from_seed([1; 32]);
404
405 let bundle = SecretBundle::from_secrets(vec![
407 GroupSecret::from_rng(&rng).unwrap(),
408 GroupSecret::from_rng(&rng).unwrap(),
409 GroupSecret::from_rng(&rng).unwrap(),
410 GroupSecret::from_rng(&rng).unwrap(),
411 ]);
412
413 let bytes = bundle.to_bytes().unwrap();
414 assert_eq!(bundle, SecretBundle::try_from_bytes(&bytes).unwrap());
415
416 let secret = GroupSecret::from_rng(&rng).unwrap();
418 let timestamp = secret.timestamp();
419
420 let bytes = secret.to_bytes().unwrap();
421 let secret_again = GroupSecret::try_from_bytes(&bytes).unwrap();
422 assert_eq!(secret, secret_again);
423 assert_eq!(timestamp, secret_again.timestamp());
424 }
425
426 #[test]
427 fn generated_always_latest() {
428 let rng = Rng::from_seed([1; 32]);
429
430 let bundle = SecretBundle::init();
431
432 let secret_0 = SecretBundle::generate(&bundle, &rng).unwrap();
433 let bundle = SecretBundle::insert(bundle, secret_0.clone());
434 assert_eq!(bundle.latest(), Some(&secret_0));
435
436 let secret_1 = SecretBundle::generate(&bundle, &rng).unwrap();
437 let bundle = SecretBundle::insert(bundle, secret_1.clone());
438 assert_eq!(bundle.latest(), Some(&secret_1));
439
440 let secret_2 = SecretBundle::generate(&bundle, &rng).unwrap();
441 let bundle = SecretBundle::insert(bundle, secret_2.clone());
442 assert_eq!(bundle.latest(), Some(&secret_2));
443 }
444}