aranya_runtime/vm_policy/
protocol.rs

1extern crate alloc;
2
3use alloc::{borrow::Cow, collections::BTreeMap, string::String, sync::Arc};
4
5use aranya_crypto::DeviceId;
6use aranya_policy_vm::{Struct, Value};
7use serde::{Deserialize, Serialize};
8
9use crate::{
10    command::{Command, CommandId, Priority},
11    Address, Prior,
12};
13
14/// The data inside a [VmProtocol]. It gets serialized and deserialized over the wire.
15#[derive(Debug, Serialize, Deserialize)]
16pub enum VmProtocolData<'a> {
17    Init {
18        policy: [u8; 8],
19        author_id: DeviceId,
20        #[serde(borrow)]
21        kind: &'a str,
22        #[serde(borrow)]
23        serialized_fields: &'a [u8],
24        #[serde(borrow)]
25        signature: &'a [u8],
26    },
27    Merge {
28        left: Address,
29        right: Address,
30    },
31    Basic {
32        parent: Address,
33        author_id: DeviceId,
34        #[serde(borrow)]
35        kind: &'a str,
36        #[serde(borrow)]
37        serialized_fields: &'a [u8],
38        #[serde(borrow)]
39        signature: &'a [u8],
40    },
41}
42
43/// The Command implementation as used by the VM. It deserializes the interior data into a
44/// [VmProtocolData] struct, and it keeps the original serialized copy around for quick
45/// access to that.
46#[derive(Debug)]
47pub struct VmProtocol<'a> {
48    /// Reference to the serialized data underlying the command
49    data: &'a [u8],
50    /// The ID of the command
51    id: CommandId,
52    /// The deserialized data
53    unpacked: VmProtocolData<'a>,
54    /// A mapping between command names and priorities, shared with the underlying
55    /// [`super::VmPolicy`] and other [`VmProtocol`] instances.
56    priority_map: Arc<BTreeMap<String, u32>>,
57}
58
59impl<'a> VmProtocol<'a> {
60    pub fn new(
61        data: &'a [u8],
62        id: CommandId,
63        unpacked: VmProtocolData<'a>,
64        priority_map: Arc<BTreeMap<String, u32>>,
65    ) -> VmProtocol<'a> {
66        VmProtocol {
67            data,
68            id,
69            unpacked,
70            priority_map,
71        }
72    }
73}
74
75impl Command for VmProtocol<'_> {
76    fn priority(&self) -> Priority {
77        match self.unpacked {
78            VmProtocolData::Init { .. } => Priority::Init,
79            VmProtocolData::Merge { .. } => Priority::Merge,
80            VmProtocolData::Basic { kind, .. } => {
81                Priority::Basic(self.priority_map.get(kind).copied().unwrap_or_default())
82            }
83        }
84    }
85
86    fn id(&self) -> CommandId {
87        self.id
88    }
89
90    fn parent(&self) -> Prior<Address> {
91        match self.unpacked {
92            VmProtocolData::Init { .. } => Prior::None,
93            VmProtocolData::Merge { left, right, .. } => Prior::Merge(left, right),
94            VmProtocolData::Basic { parent, .. } => Prior::Single(parent),
95        }
96    }
97
98    fn policy(&self) -> Option<&[u8]> {
99        match self.unpacked {
100            VmProtocolData::Init { ref policy, .. } => Some(policy),
101            _ => None,
102        }
103    }
104
105    fn bytes(&self) -> &[u8] {
106        self.data
107    }
108}
109
110#[derive(Clone, Debug)]
111pub struct Envelope<'a> {
112    pub parent_id: CommandId,
113    pub author_id: DeviceId,
114    pub command_id: CommandId,
115    pub payload: Cow<'a, [u8]>,
116    pub signature: Cow<'a, [u8]>,
117}
118
119impl From<Envelope<'_>> for Struct {
120    fn from(e: Envelope<'_>) -> Self {
121        Self::new(
122            "Envelope",
123            [
124                ("parent_id".into(), e.parent_id.into_id().into()),
125                ("author_id".into(), e.author_id.into_id().into()),
126                ("command_id".into(), e.command_id.into_id().into()),
127                ("payload".into(), e.payload.into_owned().into()),
128                ("signature".into(), e.signature.into_owned().into()),
129            ],
130        )
131    }
132}
133
134impl TryFrom<Struct> for Envelope<'_> {
135    type Error = EnvelopeError;
136
137    fn try_from(
138        Struct {
139            name,
140            ref mut fields,
141        }: Struct,
142    ) -> Result<Self, Self::Error> {
143        if name != "Envelope" {
144            return Err(EnvelopeError::InvalidName(name));
145        }
146
147        Ok(Self {
148            parent_id: get::<aranya_crypto::Id>(fields, "parent_id")?.into(),
149            author_id: get::<aranya_crypto::Id>(fields, "author_id")?.into(),
150            command_id: get::<aranya_crypto::Id>(fields, "command_id")?.into(),
151            payload: Cow::Owned(get(fields, "payload")?),
152            signature: Cow::Owned(get(fields, "signature")?),
153        })
154    }
155}
156
157fn get<T: TryFrom<Value>>(
158    fields: &mut BTreeMap<String, Value>,
159    key: &'static str,
160) -> Result<T, EnvelopeError> {
161    fields
162        .remove(key)
163        .ok_or(EnvelopeError::MissingField(key))?
164        .try_into()
165        .map_err(|_| EnvelopeError::InvalidType(key))
166}
167
168#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
169pub enum EnvelopeError {
170    #[error("invalid struct name {0:?}")]
171    InvalidName(String),
172    #[error("missing field {0:?}")]
173    MissingField(&'static str),
174    #[error("invalid type for field {0:?}")]
175    InvalidType(&'static str),
176}