1use super::super::peer::{PeerAuthority, open, request_parts, seal};
2use super::*;
3
4pub struct Device<'a> {
6 pub(super) device: Hash,
7 pub(super) signing: &'a Secret,
8 pub(super) recipient: &'a Secret,
9 bearer: &'a Secret,
10}
11impl<'a> Device<'a> {
12 pub fn seed(seed: &'a SeedAuthority) -> Self {
13 Self {
14 device: seed.genesis().device_id(),
15 signing: seed.protected_signing_seed(),
16 recipient: seed.protected_recipient_key(),
17 bearer: seed.bearer(),
18 }
19 }
20 pub(super) fn active<'m>(&self, membership: &'m Membership) -> Result<&'m Member> {
21 let member = membership.member(&self.device)?;
22 let private = HpkePrivate::from_bytes(self.recipient.expose())
23 .map_err(|_| anyhow::anyhow!("error membership-recipient"))?;
24 check(
25 member.sign
26 == SigningKey::from_bytes(self.signing.expose())
27 .verifying_key()
28 .to_bytes()
29 && member.hpke.as_slice()
30 == <Kem as hpke::Kem>::sk_to_pk(&private).to_bytes().as_slice()
31 && bool::from(member.verifier.ct_eq(&credential_verifier(
32 membership.genesis.context().vault_id,
33 self.device,
34 self.bearer,
35 ))),
36 )?;
37 Ok(member)
38 }
39 pub fn validate(&self, membership: &Membership) -> Result<()> {
40 self.active(membership).map(|_| ())
41 }
42 pub fn prepare_invitation(
43 &self,
44 membership: &Membership,
45 expiry: u64,
46 ) -> Result<(Invitation, Declaration)> {
47 self.invitation_with_psk(membership, expiry, Secret::generate()?)
48 }
49 pub(super) fn invitation_with_psk(
50 &self,
51 membership: &Membership,
52 expiry: u64,
53 psk: Secret,
54 ) -> Result<(Invitation, Declaration)> {
55 let member = self.active(membership)?;
56 check(membership.device_count() < MAX_DEVICES)?;
57 membership.ensure_change_capacity()?;
58 let inv = Invitation {
59 vault: membership.genesis.context().vault_id,
60 inviter: member.hpke,
61 psk,
62 };
63 let mut body = b"AVID\0\x02\x01".to_vec();
64 for field in [
65 inv.vault,
66 membership.genesis.commitment(),
67 membership.head(),
68 self.device,
69 inv.inviter,
70 inv.handle(),
71 ] {
72 body.extend(field);
73 }
74 body.extend(expiry.to_be_bytes());
75 let sig = SigningKey::from_bytes(self.signing.expose())
76 .sign(&cce("aven-e2ee/v1/pairing/declaration", &[&body]));
77 let mut raw = vec![1];
78 bytes(&mut raw, &body);
79 bytes(&mut raw, &sig.to_bytes());
80 Ok((inv, Declaration::from_record(membership, &raw)?))
81 }
82 pub fn prepare_admission(
85 &self,
86 membership: &Membership,
87 declaration: &Declaration,
88 invitation: &Invitation,
89 request: &[u8],
90 keys: &VerifiedKeys,
91 ) -> Result<Vec<u8>> {
92 self.active(membership)?;
93 let d = Declaration::from_record(membership, declaration.record())?;
94 check(
95 d.inviter == self.device
96 && invitation.vault == membership.genesis.context().vault_id
97 && invitation.inviter == d.hpke
98 && invitation.handle() == d.handle,
99 )?;
100 keys.validate(membership)?;
101 let handle = invitation.handle();
102 let (enc, cipher) = request_parts(request, &handle)?;
103 let plain = open(
104 invitation,
105 self.recipient,
106 &cce(
107 "aven-e2ee/v1/pairing/request",
108 &[&invitation.vault, &handle],
109 ),
110 &cce(
111 "aven-e2ee/v1/pairing/request-aad",
112 &[&invitation.vault, &handle, &invitation.inviter],
113 ),
114 enc,
115 cipher,
116 )?;
117 let recipient = Recipient::parse(&plain)?;
118 recipient.verify(&invitation.vault, &handle, &invitation.inviter)?;
119 membership.unique(&recipient, &handle)?;
120 let (state, core) = admission::state_core(membership, &d, request, &recipient);
121 let plain = admission::grant_plaintext(membership, &d, request, &recipient, keys);
122 let info = cce(
123 "aven-e2ee/v1/pairing/grant",
124 &[&invitation.vault, &handle, &hash(request)],
125 );
126 let (enc, cipher) = seal(invitation, &recipient.hpke, &info, &core, &plain)?;
127 let mut attachments = encoding::packages(1);
128 encoding::package(
129 &mut attachments,
130 1,
131 &recipient.device,
132 &recipient.hpke,
133 &enc,
134 &cipher,
135 );
136 let raw = admission::signed(self.signing, &core, &state, &attachments);
137 membership.append(d.record(), request, &raw)?;
138 Ok(raw)
139 }
140}
141
142#[derive(Clone)]
143pub struct Declaration {
144 record: Vec<u8>,
145 pub(super) handle: Hash,
146 pub(super) inviter: Hash,
147 pub(super) hpke: Hash,
148 expiry: u64,
149}
150impl Declaration {
151 pub fn from_record(membership: &Membership, raw: &[u8]) -> Result<Self> {
152 check(raw.len() == DECLARATION_BYTES)?;
153 let mut r = Reader(raw);
154 check(r.take(1)? == [1])?;
155 let body = r.blob(207)?;
156 let signature = r.blob(64)?;
157 r.end()?;
158 let mut b = Reader(body);
159 check(b.take(7)? == b"AVID\0\x02\x01")?;
160 check(
161 b.array::<32>()? == membership.genesis.context().vault_id
162 && b.array::<32>()? == membership.genesis.commitment(),
163 )?;
164 let anchor = b.array::<32>()?;
165 let anchor_sequence = membership
166 .heads
167 .iter()
168 .position(|head| *head == anchor)
169 .context("error membership-anchor")? as u64
170 + 1;
171 let inviter = b.array()?;
172 let hpke = b.array()?;
173 let member = membership.member(&inviter)?;
174 check(member.admitted_at <= anchor_sequence && member.hpke == hpke)?;
175 let handle = b.array()?;
176 let expiry = u64::from_be_bytes(b.array()?);
177 b.end()?;
178 verify(
179 &member.sign,
180 "aven-e2ee/v1/pairing/declaration",
181 &[body],
182 signature,
183 )?;
184 Ok(Self {
185 record: raw.to_vec(),
186 handle,
187 inviter,
188 hpke,
189 expiry,
190 })
191 }
192 pub fn record(&self) -> &[u8] {
193 &self.record
194 }
195 pub fn commitment(&self) -> Hash {
196 hash(&self.record)
197 }
198 pub fn handle(&self) -> Hash {
199 self.handle
200 }
201 pub fn inviter(&self) -> Hash {
202 self.inviter
203 }
204 pub fn expiry(&self) -> u64 {
205 self.expiry
206 }
207}
208
209const ATTEMPT_KEY_BYTES: usize = 128;
211
212pub struct Joiner(pub(super) PeerAuthority);
215impl Joiner {
216 pub fn generate(invitation: Invitation) -> Result<Self> {
217 Ok(Self(PeerAuthority::generate(invitation)?))
218 }
219 pub fn protected_storage_bytes(&self) -> Zeroizing<Vec<u8>> {
220 self.0.protected_storage_bytes()
221 }
222 pub fn from_protected_storage(bytes: &[u8]) -> Result<Self> {
223 Ok(Self(PeerAuthority::from_protected_storage(bytes)?))
224 }
225 pub fn matches_invitation(&self, invitation: &Invitation) -> bool {
226 self.0.matches_invitation(invitation)
227 }
228 pub fn retry(&self, invitation: Invitation) -> Result<Self> {
231 self.same_context(&invitation)?;
232 ensure!(
233 !self.matches_invitation(&invitation),
234 "error enrollment-retry-context"
235 );
236 Ok(Self(self.0.retry(invitation)?))
237 }
238 pub fn attempt_bytes(&self) -> Zeroizing<Vec<u8>> {
240 Zeroizing::new(self.protected_storage_bytes()[ATTEMPT_KEY_BYTES..].to_vec())
241 }
242 pub fn attempt(&self, bytes: &[u8]) -> Result<Self> {
245 let mut raw = Zeroizing::new(self.protected_storage_bytes()[..ATTEMPT_KEY_BYTES].to_vec());
246 raw.extend_from_slice(bytes);
247 let attempt = Self::from_protected_storage(&raw)?;
248 self.same_context(&attempt.0.invitation)?;
249 Ok(attempt)
250 }
251 fn same_context(&self, invitation: &Invitation) -> Result<()> {
252 ensure!(
253 invitation.vault == self.0.invitation.vault
254 && invitation.inviter == self.0.invitation.inviter,
255 "error enrollment-retry-context"
256 );
257 Ok(())
258 }
259 pub fn vault(&self) -> Hash {
260 self.0.vault()
261 }
262 pub fn handle(&self) -> Hash {
263 self.0.handle()
264 }
265 pub fn device(&self) -> Hash {
266 self.0.device()
267 }
268 pub fn bearer(&self) -> &Secret {
269 self.0.bearer()
270 }
271 pub fn request(&self) -> &[u8] {
272 self.0.request()
273 }
274 pub fn authority(&self) -> Device<'_> {
275 Device {
276 device: self.0.device,
277 signing: &self.0.signing,
278 recipient: &self.0.recipient,
279 bearer: &self.0.bearer,
280 }
281 }
282 pub fn open_provisional(&self, declaration: &[u8], record: &[u8]) -> Result<ProvisionalGrant> {
284 check(declaration.len() == DECLARATION_BYTES && record.len() <= MAX_RECORD_BYTES)?;
285 let (core, _, attachments, _) = encoding::components(record)?;
286 let recipient = self.0.recipient()?;
287 let mut r = Reader(attachments);
289 check(r.take(8)? == b"AVGA\0\x05\0\x01" && r.take(2)? == [1, 1])?;
290 check(r.blob(32)? == recipient.device && r.blob(32)? == recipient.hpke)?;
291 let enc = r.blob(32)?;
292 let cipher = encoding::blob(&mut r, MAX_KEY_PLAINTEXT_BYTES + 16)?;
293 r.end()?;
294 let info = cce(
295 "aven-e2ee/v1/pairing/grant",
296 &[&self.0.vault(), &self.0.handle(), &hash(self.request())],
297 );
298 let plain = open(
299 &self.0.invitation,
300 &self.0.recipient,
301 &info,
302 core,
303 enc,
304 cipher,
305 )?;
306 check(plain.len() >= GRANT_PREFIX_BYTES + 2 && plain.len() <= MAX_KEY_PLAINTEXT_BYTES)?;
307 let mut r = Reader(&plain);
308 check(
309 r.take(1)? == [3]
310 && r.array::<32>()? == self.0.vault()
311 && r.array::<32>()? == self.0.handle()
312 && r.array::<32>()? == hash(declaration)
313 && r.array::<32>()? == hash(self.request())
314 && r.array::<32>()? == self.device(),
315 )?;
316 let genesis = r.array()?;
317 let publication = r.array()?;
318 let predecessor = r.array()?;
319 r.take(64)?;
320 let descriptor = r.array()?;
321 Ok(ProvisionalGrant {
322 plaintext: plain,
323 genesis,
324 publication,
325 predecessor,
326 descriptor,
327 outcome: hash(record),
328 })
329 }
330 pub fn verify_enrollment(
331 &self,
332 predecessor: &Membership,
333 declaration: &[u8],
334 record: &[u8],
335 ) -> Result<VerifiedEnrollment> {
336 let grant = self.open_provisional(declaration, record)?;
337 let d = Declaration::from_record(predecessor, declaration)?;
338 let membership = predecessor.admit(&d, self.request(), record)?;
339 check(d.hpke == self.0.invitation.inviter)?;
340 let recipient = self.0.recipient()?;
341 let member = membership.member(&self.device())?;
342 check(
343 member.sign == recipient.sign
344 && member.hpke == recipient.hpke
345 && member.verifier == recipient.verifier
346 && member.admission == d.handle,
347 )?;
348 let mut r = Reader(&grant.plaintext[GRANT_PREFIX_BYTES..]);
349 let keys = VerifiedKeys::read(predecessor, &mut r)?;
350 let expected =
351 admission::grant_plaintext(predecessor, &d, self.request(), &recipient, &keys);
352 check(bool::from(
353 grant.plaintext.as_slice().ct_eq(expected.as_slice()),
354 ))?;
355 Ok(VerifiedEnrollment { membership, keys })
356 }
357}
358
359pub struct ProvisionalGrant {
361 plaintext: Zeroizing<Vec<u8>>,
362 pub genesis: Hash,
363 pub publication: Hash,
364 pub predecessor: Hash,
365 pub descriptor: Hash,
366 pub outcome: Hash,
367}
368pub struct VerifiedEnrollment {
370 membership: Membership,
371 keys: VerifiedKeys,
372}
373impl VerifiedEnrollment {
374 pub fn genesis(&self) -> &Genesis {
375 self.membership.genesis()
376 }
377 pub fn publication(&self) -> &Publication {
378 self.membership.publication()
379 }
380 pub fn checkpoint(&self) -> Hash {
381 self.membership.head()
382 }
383 pub fn membership(&self) -> &Membership {
384 &self.membership
385 }
386 pub fn keys(&self) -> &VerifiedKeys {
387 &self.keys
388 }
389 pub fn key(&self) -> &LocalSharedStatePackageKey {
390 self.keys
391 .key(self.membership.genesis.context().generation_id)
392 .expect("verified bootstrap coverage")
393 }
394}