Skip to main content

aven_protocol/claim/
codec.rs

1use anyhow::{Result, ensure};
2use ed25519_dalek::{Signature, VerifyingKey};
3
4pub use crate::codec::{bytes, cce, hash};
5
6use super::{CLAIM_BYTES, GENESIS_BYTES, Genesis, LocalSharedStatePackageContext};
7
8pub const MEMBERSHIP_SIGN: &str = "aven-e2ee/v1/membership/sign";
9
10pub fn state_hash(state: &[u8]) -> [u8; 32] {
11    hash(&cce("aven-e2ee/v1/membership/state", &[state]))
12}
13
14/// Signed membership core shared by genesis, publication and later actions.
15pub fn membership_core(
16    vault: &[u8],
17    sequence: u64,
18    head: &[u8],
19    signer: &[u8],
20    action: u8,
21    body: &[u8],
22    state: &[u8],
23) -> Vec<u8> {
24    let mut out = vec![1];
25    bytes(&mut out, vault);
26    out.extend(sequence.to_be_bytes());
27    bytes(&mut out, head);
28    out.push(1);
29    bytes(&mut out, signer);
30    out.push(action);
31    bytes(&mut out, body);
32    bytes(&mut out, &state_hash(state));
33    out
34}
35
36pub fn valid(condition: bool) -> Result<()> {
37    ensure!(condition, "error seed-claim-invalid");
38    Ok(())
39}
40
41pub struct Reader<'a>(pub &'a [u8]);
42
43impl<'a> Reader<'a> {
44    pub fn take(&mut self, n: usize) -> Result<&'a [u8]> {
45        valid(n <= self.0.len())?;
46        let (value, rest) = self.0.split_at(n);
47        self.0 = rest;
48        Ok(value)
49    }
50
51    pub fn array<const N: usize>(&mut self) -> Result<[u8; N]> {
52        Ok(self.take(N)?.try_into()?)
53    }
54
55    pub fn blob(&mut self, n: usize) -> Result<&'a [u8]> {
56        valid(u32::from_be_bytes(self.array()?) as u64 == n as u64)?;
57        self.take(n)
58    }
59
60    pub fn end(self) -> Result<()> {
61        valid(self.0.is_empty())
62    }
63}
64
65pub fn claim_record(request: &[u8]) -> Result<&[u8]> {
66    valid(request.len() == CLAIM_BYTES)?;
67    let mut reader = Reader(request);
68    valid(reader.take(6)? == b"AVCL\0\x01")?;
69    let record = reader.blob(GENESIS_BYTES)?;
70    reader.end()?;
71    Ok(record)
72}
73
74type Components<'a> = (&'a [u8], &'a [u8], &'a [u8], &'a [u8]);
75
76pub fn components(record: &[u8]) -> Result<Components<'_>> {
77    valid(record.len() == GENESIS_BYTES)?;
78    let mut reader = Reader(record);
79    valid(reader.take(1)? == [1])?;
80    let parts = (
81        reader.blob(229)?,
82        reader.blob(280)?,
83        reader.blob(312)?,
84        reader.blob(64)?,
85    );
86    reader.end()?;
87    Ok(parts)
88}
89
90pub(super) fn parse(record: &[u8]) -> Result<Genesis> {
91    let (core, state, attachments, signature) = components(record)?;
92    let mut r = Reader(state);
93    valid(r.take(7)? == b"AVGS\0\x01\x01")?;
94    let vault_id = r.array()?;
95    valid(r.take(4)? == [0, 0, 0, 1])?;
96    let device = r.array()?;
97    let signing_public = r.array()?;
98    let hpke_public = r.array()?;
99    let verifier = r.array()?;
100    valid(r.take(4)? == 1_u32.to_be_bytes())?;
101    let claim = r.array()?;
102    valid(r.take(1)? == [1])?;
103    let generation_id = r.array()?;
104    let generation_commitment = r.array()?;
105    valid(r.take(8)? == [0; 8])?;
106    r.end()?;
107
108    let mut r = Reader(core);
109    valid(r.take(1)? == [1])?;
110    valid(r.blob(32)? == vault_id)?;
111    valid(r.take(8)? == [0; 8])?;
112    valid(r.blob(32)? == [0; 32])?;
113    valid(r.take(1)? == [1])?;
114    valid(r.blob(32)? == device)?;
115    valid(r.take(1)? == [1])?;
116    let mut body = Reader(r.blob(70)?);
117    valid(body.take(6)? == b"AVGC\0\x01")?;
118    let setup = body.array()?;
119    valid(body.take(32)? == claim)?;
120    body.end()?;
121    valid(r.blob(32)? == state_hash(state))?;
122    r.end()?;
123
124    let mut r = Reader(attachments);
125    valid(r.take(9)? == b"AVGA\0\x01\x01\x01\x01")?;
126    valid(r.blob(32)? == device)?;
127    valid(r.blob(32)? == hpke_public)?;
128    r.blob(32)?;
129    r.blob(191)?;
130    r.end()?;
131
132    let public = VerifyingKey::from_bytes(&signing_public)
133        .map_err(|_| anyhow::anyhow!("error seed-claim-signature"))?;
134    let signature = Signature::from_slice(signature)
135        .map_err(|_| anyhow::anyhow!("error seed-claim-signature"))?;
136    public
137        .verify_strict(&cce(MEMBERSHIP_SIGN, &[core, attachments]), &signature)
138        .map_err(|_| anyhow::anyhow!("error seed-claim-signature"))?;
139    Ok(Genesis {
140        record: record.try_into()?,
141        context: LocalSharedStatePackageContext {
142            vault_id,
143            generation_id,
144        },
145        setup,
146        claim,
147        device,
148        signing_public,
149        hpke_public,
150        verifier,
151        generation_commitment,
152    })
153}