Skip to main content

aven_protocol/claim/membership/
keys.rs

1//! Complete commitment-checked key coverage, independent of host readiness.
2use super::*;
3
4/// Every generation key, verified in authenticated ancestry order.
5/// Cloning preserves the verified coverage and zeroizes each owned copy on drop.
6#[derive(Clone)]
7pub struct VerifiedKeys {
8    vault: Hash,
9    generations: Vec<Generation>,
10    keys: Vec<LocalSharedStatePackageKey>,
11}
12impl VerifiedKeys {
13    pub fn key(&self, generation: Hash) -> Result<&LocalSharedStatePackageKey> {
14        let index = self
15            .generations
16            .iter()
17            .position(|g| g.id == generation)
18            .context("error membership-key-missing")?;
19        Ok(&self.keys[index])
20    }
21    pub fn validate(&self, m: &Membership) -> Result<()> {
22        check(self.vault == m.genesis.context().vault_id && self.generations == m.generations)
23    }
24    pub(super) fn write(&self, out: &mut Vec<u8>) {
25        out.extend((self.keys.len() as u16).to_be_bytes());
26        for (g, key) in self.generations.iter().zip(&self.keys) {
27            out.extend(g.id);
28            out.extend(key.protected_storage_bytes());
29            out.extend(g.starts_after.to_be_bytes());
30        }
31    }
32    pub(super) fn read(m: &Membership, r: &mut Reader<'_>) -> Result<Self> {
33        check(u16::from_be_bytes(r.array()?) as usize == m.generations.len())?;
34        let mut keys = Vec::with_capacity(m.generations.len());
35        for g in &m.generations {
36            check(r.array::<32>()? == g.id)?;
37            let key = LocalSharedStatePackageKey::new(r.array()?);
38            check(r.array::<8>()? == g.starts_after.to_be_bytes())?;
39            check(
40                generation_commitment(
41                    LocalSharedStatePackageContext {
42                        vault_id: m.genesis.context().vault_id,
43                        generation_id: g.id,
44                    },
45                    key.protected_storage_bytes(),
46                ) == g.commitment,
47            )?;
48            keys.push(key);
49        }
50        check(r.0.is_empty())?;
51        Ok(Self {
52            vault: m.genesis.context().vault_id,
53            generations: m.generations.clone(),
54            keys,
55        })
56    }
57    pub(super) fn extended(&self, m: &Membership, key: LocalSharedStatePackageKey) -> Result<Self> {
58        check(
59            self.vault == m.genesis.context().vault_id
60                && m.generations.len() == self.generations.len() + 1
61                && m.generations.starts_with(&self.generations),
62        )?;
63        let mut bytes = Zeroizing::new(Vec::new());
64        bytes.extend((m.generations.len() as u16).to_be_bytes());
65        for (g, key) in self
66            .generations
67            .iter()
68            .zip(&self.keys)
69            .chain(std::iter::once((m.current_generation(), &key)))
70        {
71            bytes.extend(g.id);
72            bytes.extend(key.protected_storage_bytes());
73            bytes.extend(g.starts_after.to_be_bytes());
74        }
75        Self::read(m, &mut Reader(&bytes))
76    }
77}
78impl Membership {
79    /// Initial-generation-only construction for existing protected seed authority.
80    pub fn verify_initial_key(&self, key: &LocalSharedStatePackageKey) -> Result<VerifiedKeys> {
81        self.validate_key(key)?;
82        Ok(VerifiedKeys {
83            vault: self.genesis.context().vault_id,
84            generations: self.generations.clone(),
85            keys: vec![key.clone()],
86        })
87    }
88}
89
90/// Protected coverage holding every generation key.
91pub const MAX_COVERAGE_BYTES: usize = 40 + GENERATION_BYTES * MAX_GENERATIONS;
92impl VerifiedKeys {
93    /// Installation-bound protected storage only, never public evidence or SQLite.
94    pub fn protected_storage_bytes(&self) -> Zeroizing<Vec<u8>> {
95        let mut out = Zeroizing::new(b"AVKC\0\x01".to_vec());
96        out.extend(self.vault);
97        self.write(&mut out);
98        out
99    }
100    /// Revalidate complete commitments against authenticated membership on every load.
101    pub fn from_protected_storage(m: &Membership, bytes: &[u8]) -> Result<Self> {
102        check(bytes.len() <= MAX_COVERAGE_BYTES)?;
103        let mut r = Reader(bytes);
104        check(r.take(6)? == b"AVKC\0\x01" && r.array::<32>()? == m.genesis.context().vault_id)?;
105        Self::read(m, &mut r)
106    }
107}