aranya_runtime/vm_policy/
protocol.rs

1extern crate alloc;
2
3use alloc::{borrow::Cow, collections::BTreeMap, string::String};
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    /// The command's priority.
55    priority: Priority,
56}
57
58impl<'a> VmProtocol<'a> {
59    pub fn new(
60        data: &'a [u8],
61        id: CommandId,
62        unpacked: VmProtocolData<'a>,
63        priority: Priority,
64    ) -> VmProtocol<'a> {
65        VmProtocol {
66            data,
67            id,
68            unpacked,
69            priority,
70        }
71    }
72}
73
74impl Command for VmProtocol<'_> {
75    fn priority(&self) -> Priority {
76        self.priority.clone()
77    }
78
79    fn id(&self) -> CommandId {
80        self.id
81    }
82
83    fn parent(&self) -> Prior<Address> {
84        match self.unpacked {
85            VmProtocolData::Init { .. } => Prior::None,
86            VmProtocolData::Merge { left, right, .. } => Prior::Merge(left, right),
87            VmProtocolData::Basic { parent, .. } => Prior::Single(parent),
88        }
89    }
90
91    fn policy(&self) -> Option<&[u8]> {
92        match self.unpacked {
93            VmProtocolData::Init { ref policy, .. } => Some(policy),
94            _ => None,
95        }
96    }
97
98    fn bytes(&self) -> &[u8] {
99        self.data
100    }
101}
102
103#[derive(Clone, Debug)]
104pub struct Envelope<'a> {
105    pub parent_id: CommandId,
106    pub author_id: DeviceId,
107    pub command_id: CommandId,
108    pub payload: Cow<'a, [u8]>,
109    pub signature: Cow<'a, [u8]>,
110}
111
112impl From<Envelope<'_>> for Struct {
113    fn from(e: Envelope<'_>) -> Self {
114        Self::new(
115            "Envelope",
116            [
117                ("parent_id".into(), e.parent_id.into_id().into()),
118                ("author_id".into(), e.author_id.into_id().into()),
119                ("command_id".into(), e.command_id.into_id().into()),
120                ("payload".into(), e.payload.into_owned().into()),
121                ("signature".into(), e.signature.into_owned().into()),
122            ],
123        )
124    }
125}
126
127impl TryFrom<Struct> for Envelope<'_> {
128    type Error = EnvelopeError;
129
130    fn try_from(
131        Struct {
132            name,
133            ref mut fields,
134        }: Struct,
135    ) -> Result<Self, Self::Error> {
136        if name != "Envelope" {
137            return Err(EnvelopeError::InvalidName(name));
138        }
139
140        Ok(Self {
141            parent_id: get::<aranya_crypto::Id>(fields, "parent_id")?.into(),
142            author_id: get::<aranya_crypto::Id>(fields, "author_id")?.into(),
143            command_id: get::<aranya_crypto::Id>(fields, "command_id")?.into(),
144            payload: Cow::Owned(get(fields, "payload")?),
145            signature: Cow::Owned(get(fields, "signature")?),
146        })
147    }
148}
149
150fn get<T: TryFrom<Value>>(
151    fields: &mut BTreeMap<String, Value>,
152    key: &'static str,
153) -> Result<T, EnvelopeError> {
154    fields
155        .remove(key)
156        .ok_or(EnvelopeError::MissingField(key))?
157        .try_into()
158        .map_err(|_| EnvelopeError::InvalidType(key))
159}
160
161#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
162pub enum EnvelopeError {
163    #[error("invalid struct name {0:?}")]
164    InvalidName(String),
165    #[error("missing field {0:?}")]
166    MissingField(&'static str),
167    #[error("invalid type for field {0:?}")]
168    InvalidType(&'static str),
169}