aranya_runtime/vm_policy/
protocol.rs

1extern crate alloc;
2
3use alloc::{borrow::Cow, collections::BTreeMap};
4
5use aranya_crypto::DeviceId;
6use aranya_policy_vm::{
7    Struct, Value,
8    ast::{Identifier, ident},
9};
10use serde::{Deserialize, Serialize};
11
12use crate::{
13    Address, Prior,
14    command::{CmdId, Command, Priority},
15};
16
17/// The data inside a [`VmProtocol`]. It gets serialized and deserialized over the wire.
18#[derive(Debug, Serialize, Deserialize)]
19pub struct VmProtocolData<'a> {
20    pub author_id: DeviceId,
21    pub kind: Identifier,
22    #[serde(borrow)]
23    pub serialized_fields: &'a [u8],
24    #[serde(borrow)]
25    pub signature: &'a [u8],
26}
27
28/// The Command implementation as used by the VM. It deserializes the interior data into a
29/// [VmProtocolData] struct, and it keeps the original serialized copy around for quick
30/// access to that.
31#[derive(Debug)]
32pub struct VmProtocol<'a> {
33    pub id: CmdId,
34    pub priority: Priority,
35    pub parent: Prior<Address>,
36    pub policy: Option<[u8; 8]>,
37    /// Serialized [`VmProtocolData`].
38    pub data: &'a [u8],
39}
40
41impl Command for VmProtocol<'_> {
42    fn priority(&self) -> Priority {
43        self.priority.clone()
44    }
45
46    fn id(&self) -> CmdId {
47        self.id
48    }
49
50    fn parent(&self) -> Prior<Address> {
51        self.parent
52    }
53
54    fn policy(&self) -> Option<&[u8]> {
55        self.policy.as_ref().map(|p| &p[..])
56    }
57
58    fn bytes(&self) -> &[u8] {
59        self.data
60    }
61}
62
63#[derive(Clone, Debug)]
64pub struct Envelope<'a> {
65    pub parent_id: CmdId,
66    pub author_id: DeviceId,
67    pub command_id: CmdId,
68    pub payload: Cow<'a, [u8]>,
69    pub signature: Cow<'a, [u8]>,
70}
71
72impl From<Envelope<'_>> for Struct {
73    fn from(e: Envelope<'_>) -> Self {
74        Self::new(
75            ident!("Envelope"),
76            [
77                (ident!("parent_id"), e.parent_id.into()),
78                (ident!("author_id"), e.author_id.into()),
79                (ident!("command_id"), e.command_id.into()),
80                (ident!("payload"), e.payload.into_owned().into()),
81                (ident!("signature"), e.signature.into_owned().into()),
82            ],
83        )
84    }
85}
86
87impl TryFrom<Struct> for Envelope<'_> {
88    type Error = EnvelopeError;
89
90    fn try_from(
91        Struct {
92            name,
93            ref mut fields,
94        }: Struct,
95    ) -> Result<Self, Self::Error> {
96        if name != "Envelope" {
97            return Err(EnvelopeError::InvalidName(name));
98        }
99
100        Ok(Self {
101            parent_id: get(fields, "parent_id")?,
102            author_id: get(fields, "author_id")?,
103            command_id: get(fields, "command_id")?,
104            payload: Cow::Owned(get(fields, "payload")?),
105            signature: Cow::Owned(get(fields, "signature")?),
106        })
107    }
108}
109
110fn get<T: TryFrom<Value>>(
111    fields: &mut BTreeMap<Identifier, Value>,
112    key: &'static str,
113) -> Result<T, EnvelopeError> {
114    fields
115        .remove(key)
116        .ok_or(EnvelopeError::MissingField(key))?
117        .try_into()
118        .map_err(|_| EnvelopeError::InvalidType(key))
119}
120
121#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
122pub enum EnvelopeError {
123    #[error("invalid struct name {0:?}")]
124    InvalidName(Identifier),
125    #[error("missing field {0:?}")]
126    MissingField(&'static str),
127    #[error("invalid type for field {0:?}")]
128    InvalidType(&'static str),
129}