mkit_server/takedown/
publication.rs1use crate::{Code, ServerError};
3use mkit_core::hash::{hash, to_hex};
4use serde::Deserialize;
5
6#[derive(Deserialize)]
7#[serde(rename_all = "camelCase", deny_unknown_fields)]
8struct ReceiptEntry {
9 key_id: String,
10 alg: String,
11 public_key: String,
12 not_before_ms: Option<String>,
13 not_after_ms: Option<String>,
14}
15#[derive(Deserialize)]
16#[serde(deny_unknown_fields)]
17struct List {
18 version: u32,
19 keys: Vec<ReceiptEntry>,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct PublicationConfig {
25 pub public_key: [u8; 32],
27 pub key_id: String,
29 pub key_list: String,
31 keys: Vec<[u8; 32]>,
32}
33impl PublicationConfig {
34 pub fn parse(seed_hex: &str, list: &str) -> Result<Self, ServerError> {
38 let invalid = || {
39 ServerError::new(
40 Code::InvalidArgument,
41 "invalid receipt-and-notice key configuration",
42 )
43 };
44 let seed =
45 zeroize::Zeroizing::new(crate::admin::auth::hex::<32>(seed_hex).ok_or_else(invalid)?);
46 let signing_key = ed25519_dalek::SigningKey::from_bytes(&seed);
47 let public_key = signing_key.verifying_key().to_bytes();
48 let key_id = to_hex(&hash(&public_key));
49 if list.len() > 65_536 {
50 return Err(invalid());
51 }
52 let parsed: List = serde_json::from_str(list).map_err(|_| invalid())?;
53 if parsed.version != 1 || parsed.keys.is_empty() || parsed.keys.len() > 128 {
54 return Err(invalid());
55 }
56 let mut key_ids = std::collections::BTreeSet::new();
57 let mut present = false;
58 let mut keys = Vec::new();
59 for entry in parsed.keys {
60 let public = crate::admin::auth::hex::<32>(&entry.public_key).ok_or_else(invalid)?;
61 ed25519_dalek::VerifyingKey::from_bytes(&public).map_err(|_| invalid())?;
62 if entry.alg != "ed25519"
63 || entry.key_id != to_hex(&hash(&public))
64 || !key_ids.insert(entry.key_id.clone())
65 {
66 return Err(invalid());
67 }
68 let bound = |value: Option<String>| -> Result<Option<i64>, ServerError> {
69 value
70 .map(|s| {
71 s.parse::<i64>()
72 .ok()
73 .filter(|v| v.to_string() == s)
74 .ok_or_else(invalid)
75 })
76 .transpose()
77 };
78 let before = bound(entry.not_before_ms)?;
79 let after = bound(entry.not_after_ms)?;
80 if before.zip(after).is_some_and(|(b, a)| b >= a) {
81 return Err(invalid());
82 }
83 present |= entry.key_id == key_id;
84 keys.push(public);
85 }
86 if !present {
87 return Err(invalid());
88 }
89 Ok(Self {
90 public_key,
91 key_id,
92 key_list: list.into(),
93 keys,
94 })
95 }
96 #[must_use]
98 pub fn public_keys(&self) -> &[[u8; 32]] {
99 &self.keys
100 }
101}