1use super::{
16 PublicKeyHex, Sha256Hex, SignatureHex, TrustedHead, canonical_json,
17};
18use crate::crypto::Signature;
19use serde::{Deserialize, Serialize};
20use std::collections::HashSet;
21
22pub const ROOT_DOMAIN: &[u8] = b"agora-governance-root-v1\n";
24
25pub const KEY_CERT_VERSION: u32 = 1;
27
28pub const ROOT_KEYS: &[&str] = &[
33 "d29ed152161d23d75cec48ade38859db07f48f3dc15a337179a8f20b13f12cd5",
35 "200e8efe32391d7f4a1d763c4de0739acfe2e63e69d8be7e19b29d3f5075fb53",
37];
38
39pub const ROOT_THRESHOLD: usize = 1;
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
44#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
45#[cfg_attr(feature = "schemars", schemars(inline))]
46#[serde(rename_all = "snake_case")]
47pub enum CertPurpose {
48 Genesis,
50 Routine,
52 Compromise,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
62#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
63#[cfg_attr(feature = "schemars", schemars(inline))]
64#[serde(deny_unknown_fields)]
65pub struct KeyCertStatement {
66 pub agora_governance_key_cert: u32,
68 pub key: PublicKeyHex,
70 pub purpose: CertPurpose,
71 pub from_seq: u64,
73 pub prev_hash: Option<Sha256Hex>,
76 pub last_trusted: Option<TrustedHead>,
80}
81
82impl KeyCertStatement {
83 pub fn genesis(key: PublicKeyHex) -> Self {
85 Self {
86 agora_governance_key_cert: KEY_CERT_VERSION,
87 key,
88 purpose: CertPurpose::Genesis,
89 from_seq: 1,
90 prev_hash: None,
91 last_trusted: None,
92 }
93 }
94
95 pub fn routine(
98 key: PublicKeyHex,
99 seq: u64,
100 prev_hash: Option<Sha256Hex>,
101 ) -> Self {
102 Self {
103 agora_governance_key_cert: KEY_CERT_VERSION,
104 key,
105 purpose: CertPurpose::Routine,
106 from_seq: seq + 1,
107 prev_hash,
108 last_trusted: None,
109 }
110 }
111
112 pub fn compromise(
115 key: PublicKeyHex,
116 seq: u64,
117 prev_hash: Option<Sha256Hex>,
118 last_trusted: TrustedHead,
119 ) -> Self {
120 Self {
121 agora_governance_key_cert: KEY_CERT_VERSION,
122 key,
123 purpose: CertPurpose::Compromise,
124 from_seq: seq,
125 prev_hash,
126 last_trusted: Some(last_trusted),
127 }
128 }
129
130 pub fn signed_bytes(&self) -> Vec<u8> {
132 let value = serde_json::to_value(self)
133 .expect("a KeyCertStatement always serializes");
134 let mut out = ROOT_DOMAIN.to_vec();
135 out.extend(canonical_json(&value));
136 out
137 }
138}
139
140#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
142#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
143#[cfg_attr(feature = "schemars", schemars(inline))]
144#[serde(deny_unknown_fields)]
145pub struct RootSignature {
146 pub root_key: PublicKeyHex,
147 pub signature: SignatureHex,
148}
149
150#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
152#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
153#[cfg_attr(feature = "schemars", schemars(inline))]
154#[serde(deny_unknown_fields)]
155pub struct KeyCertificate {
156 pub statement: KeyCertStatement,
157 pub signatures: Vec<RootSignature>,
158}
159
160#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
162pub enum CertificateError {
163 #[error("agora_governance_key_cert is {0}, not {KEY_CERT_VERSION}")]
164 UnsupportedVersion(u32),
165 #[error(
166 "the certificate is for a different key, purpose or chain position"
167 )]
168 WrongStatement,
169 #[error(
170 "{valid} valid root signature(s) where {needed} are needed; \
171 unknown and repeated signers count for nothing"
172 )]
173 BelowThreshold { valid: usize, needed: usize },
174}
175
176impl KeyCertificate {
177 pub fn unsigned(statement: KeyCertStatement) -> Self {
179 Self {
180 statement,
181 signatures: Vec::new(),
182 }
183 }
184
185 pub fn with(mut self, signature: RootSignature) -> Self {
187 self.signatures.push(signature);
188 self
189 }
190
191 pub fn verify(&self, roots: &RootSet) -> Result<(), CertificateError> {
198 let version = self.statement.agora_governance_key_cert;
199 if version != KEY_CERT_VERSION {
200 return Err(CertificateError::UnsupportedVersion(version));
201 }
202 let message = self.statement.signed_bytes();
203 let mut signers = HashSet::new();
204 for s in &self.signatures {
205 if !roots.contains(&s.root_key) {
206 continue;
207 }
208 let Ok(key) = s.root_key.to_verifying_key() else {
209 continue;
210 };
211 if key
212 .verify_strict(&message, &Signature::from(&s.signature))
213 .is_ok()
214 {
215 signers.insert(s.root_key);
216 }
217 }
218 if signers.len() >= roots.threshold() {
219 Ok(())
220 } else {
221 Err(CertificateError::BelowThreshold {
222 valid: signers.len(),
223 needed: roots.threshold(),
224 })
225 }
226 }
227
228 pub fn verify_for(
232 &self,
233 expected: &KeyCertStatement,
234 roots: &RootSet,
235 ) -> Result<(), CertificateError> {
236 if self.statement != *expected {
237 return Err(CertificateError::WrongStatement);
238 }
239 self.verify(roots)
240 }
241}
242
243#[derive(Debug, Clone, PartialEq, Eq)]
245pub struct RootSet {
246 keys: HashSet<PublicKeyHex>,
247 threshold: usize,
248}
249
250impl RootSet {
251 pub fn published() -> Self {
253 Self::new(
254 ROOT_KEYS.iter().map(|k| {
255 k.parse().expect("ROOT_KEYS are valid 32-byte hex keys")
256 }),
257 ROOT_THRESHOLD,
258 )
259 }
260
261 pub fn new(
263 keys: impl IntoIterator<Item = PublicKeyHex>,
264 threshold: usize,
265 ) -> Self {
266 Self {
267 keys: keys.into_iter().collect(),
268 threshold: threshold.max(1),
269 }
270 }
271
272 pub fn contains(&self, key: &PublicKeyHex) -> bool {
273 self.keys.contains(key)
274 }
275
276 pub fn threshold(&self) -> usize {
277 self.threshold
278 }
279
280 pub fn keys(&self) -> impl Iterator<Item = &PublicKeyHex> {
282 self.keys.iter()
283 }
284}