willow_data_model/authorisation/
mod.rs

1//! Authorisation of entries.
2//!
3//! This module provides the [`AuthorisationToken`] trait for types which can serve as willow [AuthorisationTokens](https://willowprotocol.org/specs/data-model/index.html#AuthorisationToken). It further provides types for [PossiblyAuthorisedEntries](https://willowprotocol.org/specs/data-model/index.html#PossiblyAuthorisedEntry) ([`PossiblyAuthorisedEntry`]) and [AuthorisedEntries](https://willowprotocol.org/specs/data-model/index.html#AuthorisedEntry) ([`AuthorisedEntry`]).
4
5#[cfg(feature = "dev")]
6use arbitrary::Arbitrary;
7
8use crate::{
9    entry::{Entry, Entrylike, EntrylikeExt},
10    prelude::{Coordinatelike, Keylike, Namespaced},
11};
12
13/// A trait for [AuthorisationTokens](https://willowprotocol.org/specs/data-model/index.html#AuthorisationToken).
14///
15/// This trait serves a dual role: it describes both how to authorise entries (i.e., how to create valid authorisation tokens) and how to verify authorisation tokens (i.e., how to compute the [is_authorised_write](https://willowprotocol.org/specs/data-model/index.html#is_authorised_write) function of the spec).
16pub trait AuthorisationToken<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>:
17    Sized
18{
19    /// The type of the information you need in order to authorise an entry. For example, in [Meadowcap](https://willowprotocol.org/specs/meadowcap/), this would be a pair of a capability and a secret key.
20    type Ingredients;
21
22    /// Everything that can go wrong when trying to create a new authorisation token from some [`Ingredients`](AuthorisationToken::Ingredients) for some entry.
23    type CreationError;
24
25    /// Creates an authorisation token for the given entry, if possible.
26    fn new_for_entry<E>(
27        entry: &E,
28        ingredients: &Self::Ingredients,
29    ) -> Result<Self, Self::CreationError>
30    where
31        E: EntrylikeExt<MCL, MCC, MPL, N, S, PD> + ?Sized;
32
33    /// Determines whether `self` [authorises](https://willowprotocol.org/specs/data-model/index.html#is_authorised_write) the given entry.
34    fn does_authorise<E>(&self, entry: &E) -> bool
35    where
36        E: EntrylikeExt<MCL, MCC, MPL, N, S, PD> + ?Sized;
37}
38
39/// An entry, together with an authorisation token that may or may not [authorise](https://willowprotocol.org/specs/data-model/index.html#is_authorised_write) the entry.
40///
41/// ```
42/// # #[cfg(feature = "dev")] {
43/// use willow_data_model::prelude::*;
44/// use willow_data_model::test_parameters::*;
45///
46/// let entry = Entry::builder()
47///     .namespace_id(Family)
48///     .subspace_id(Alfie)
49///     .path(Path::<4, 4, 4>::new())
50///     .timestamp(12345)
51///     .payload_digest(Spades)
52///     .payload_length(2)
53///     .build().unwrap();
54///
55/// let pae = PossiblyAuthorisedEntry {
56///     entry: entry.clone(),
57///     authorisation_token: TestSubspaceSignature::new_for_entry(&entry, &AlfieSecret).unwrap(),
58/// };
59/// assert!(pae.into_authorised_entry().is_ok());
60/// # }
61/// ```
62///
63/// [Specification](https://willowprotocol.org/specs/data-model/index.html#PossiblyAuthorisedEntry)
64#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
65#[cfg_attr(feature = "dev", derive(Arbitrary))]
66pub struct PossiblyAuthorisedEntry<
67    const MCL: usize,
68    const MCC: usize,
69    const MPL: usize,
70    N,
71    S,
72    PD,
73    AT,
74> {
75    /// The entry.
76    pub entry: Entry<MCL, MCC, MPL, N, S, PD>,
77    /// The authorisation token, which may or may not [authorise](https://willowprotocol.org/specs/data-model/index.html#is_authorised_write) the entry.
78    pub authorisation_token: AT,
79}
80
81impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>
82    PossiblyAuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
83{
84    /// Checks whether `self.authorisation_token` [authorise](https://willowprotocol.org/specs/data-model/index.html#is_authorised_write) `self.entry`. If so, converts self into an [`AuthorisedEntry`], otherwise returns `Err(self)`.
85    ///
86    /// ```
87    /// # #[cfg(feature = "dev")] {
88    /// use willow_data_model::prelude::*;
89    /// use willow_data_model::test_parameters::*;
90    ///
91    /// let entry = Entry::builder()
92    ///     .namespace_id(Family)
93    ///     .subspace_id(Alfie)
94    ///     .path(Path::<4, 4, 4>::new())
95    ///     .timestamp(12345)
96    ///     .payload_digest(Spades)
97    ///     .payload_length(2)
98    ///     .build().unwrap();
99    ///
100    /// let pae1 = PossiblyAuthorisedEntry {
101    ///     entry: entry.clone(),
102    ///     authorisation_token: TestSubspaceSignature::new_for_entry(&entry, &AlfieSecret).unwrap(),
103    /// };
104    /// assert!(pae1.into_authorised_entry().is_ok());
105    ///
106    /// let pae2 = PossiblyAuthorisedEntry {
107    ///     entry: entry.clone(),
108    ///     authorisation_token: BettySignature,
109    /// };
110    /// assert!(pae2.into_authorised_entry().is_err());
111    /// # }
112    /// ```
113    pub fn into_authorised_entry(self) -> Result<AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>, Self>
114    where
115        AT: AuthorisationToken<MCL, MCC, MPL, N, S, PD>,
116    {
117        if self.authorisation_token.does_authorise(&self.entry) {
118            Ok(AuthorisedEntry {
119                entry: self.entry,
120                authorisation_token: self.authorisation_token,
121            })
122        } else {
123            Err(self)
124        }
125    }
126
127    /// Converts self into an [`AuthorisedEntry`], without checking if `self.authorisation_token` [authorise](https://willowprotocol.org/specs/data-model/index.html#is_authorised_write) `self.entry`.
128    ///
129    /// ```
130    /// # #[cfg(feature = "dev")] {
131    /// use willow_data_model::prelude::*;
132    /// use willow_data_model::test_parameters::*;
133    ///
134    /// let entry = Entry::builder()
135    ///     .namespace_id(Family)
136    ///     .subspace_id(Alfie)
137    ///     .path(Path::<4, 4, 4>::new())
138    ///     .timestamp(12345)
139    ///     .payload_digest(Spades)
140    ///     .payload_length(2)
141    ///     .build().unwrap();
142    ///
143    /// let pae = PossiblyAuthorisedEntry {
144    ///     entry: entry.clone(),
145    ///     authorisation_token: TestSubspaceSignature::new_for_entry(&entry, &AlfieSecret).unwrap(),
146    /// };
147    /// let authed = unsafe { pae.into_authorised_entry_unchecked() };
148    /// assert_eq!(authed.as_entry(), &entry);
149    /// assert_eq!(authed.as_authorisation_token(), &AlfieSignature);
150    /// # }
151    /// ```
152    ///
153    /// #### Safety
154    ///
155    /// Undefined behaviour may occur if `self.authorisation_token.does_authorise(&self.entry)` is `false`. If it returns `true`, this method is safe to call.
156    pub unsafe fn into_authorised_entry_unchecked(
157        self,
158    ) -> AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT> {
159        AuthorisedEntry {
160            entry: self.entry,
161            authorisation_token: self.authorisation_token,
162        }
163    }
164}
165
166impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT> Keylike<MCL, MCC, MPL, S>
167    for PossiblyAuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
168{
169    fn wdm_subspace_id(&self) -> &S {
170        self.entry.wdm_subspace_id()
171    }
172
173    fn wdm_path(&self) -> &crate::prelude::Path<MCL, MCC, MPL> {
174        self.entry.wdm_path()
175    }
176}
177
178impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>
179    Coordinatelike<MCL, MCC, MPL, S> for PossiblyAuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
180{
181    fn wdm_timestamp(&self) -> crate::Timestamp {
182        self.entry.wdm_timestamp()
183    }
184}
185
186impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT> Namespaced<N>
187    for PossiblyAuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
188{
189    fn wdm_namespace_id(&self) -> &N {
190        self.entry.wdm_namespace_id()
191    }
192}
193
194impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>
195    Entrylike<MCL, MCC, MPL, N, S, PD> for PossiblyAuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
196{
197    fn wdm_payload_length(&self) -> u64 {
198        self.entry.wdm_payload_length()
199    }
200
201    fn wdm_payload_digest(&self) -> &PD {
202        self.entry.wdm_payload_digest()
203    }
204}
205
206/// An entry, together with an authorisation token that [authorises](https://willowprotocol.org/specs/data-model/index.html#is_authorised_write) the entry.
207///
208/// There are three typical scenarios for creating these: authorising an [`Entry`] yourself (via [`Entry::into_authorised_entry`] or [`crate::entry::EntrylikeExt::wdm_authorise`]), or receiving and verifying an untrusted authorisation token ([`PossiblyAuthorisedEntry::into_authorised_entry`]).
209///
210/// ```
211/// # #[cfg(feature = "dev")] {
212/// use willow_data_model::prelude::*;
213/// use willow_data_model::test_parameters::*;
214///
215/// let entry = Entry::builder()
216///     .namespace_id(Family)
217///     .subspace_id(Alfie)
218///     .path(Path::<4, 4, 4>::new())
219///     .timestamp(12345)
220///     .payload_digest(Spades)
221///     .payload_length(2)
222///     .build().unwrap();
223///
224/// let authed = entry.into_authorised_entry::<TestSubspaceSignature>(&AlfieSecret).unwrap();
225/// assert_eq!(authed.as_authorisation_token(), &AlfieSignature);
226/// # }
227/// ```
228///
229/// [Specification](https://willowprotocol.org/specs/data-model/index.html#AuthorisedEntry)
230#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
231#[cfg_attr(feature = "dev", derive(Arbitrary))]
232pub struct AuthorisedEntry<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT> {
233    entry: Entry<MCL, MCC, MPL, N, S, PD>,
234    authorisation_token: AT,
235}
236
237impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>
238    AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
239{
240    /// Consumes self, returning the entry and the authorisation token.
241    pub fn into_parts(self) -> (Entry<MCL, MCC, MPL, N, S, PD>, AT) {
242        (self.entry, self.authorisation_token)
243    }
244
245    /// Returns a reference to the entry.
246    pub fn as_entry(&self) -> &Entry<MCL, MCC, MPL, N, S, PD> {
247        &self.entry
248    }
249
250    /// Returns a mutable reference to the entry.
251    pub fn as_mut_entry(&mut self) -> &mut Entry<MCL, MCC, MPL, N, S, PD> {
252        &mut self.entry
253    }
254
255    /// Returns a reference to the authorisation token.
256    pub fn as_authorisation_token(&self) -> &AT {
257        &self.authorisation_token
258    }
259
260    /// Returns a mutable reference to the authorisation token.
261    pub fn as_mut_authorisation_token(&mut self) -> &mut AT {
262        &mut self.authorisation_token
263    }
264}
265
266impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT> Keylike<MCL, MCC, MPL, S>
267    for AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
268{
269    fn wdm_subspace_id(&self) -> &S {
270        self.entry.wdm_subspace_id()
271    }
272
273    fn wdm_path(&self) -> &crate::prelude::Path<MCL, MCC, MPL> {
274        self.entry.wdm_path()
275    }
276}
277
278impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>
279    Coordinatelike<MCL, MCC, MPL, S> for AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
280{
281    fn wdm_timestamp(&self) -> crate::Timestamp {
282        self.entry.wdm_timestamp()
283    }
284}
285
286impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT> Namespaced<N>
287    for AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
288{
289    fn wdm_namespace_id(&self) -> &N {
290        self.entry.wdm_namespace_id()
291    }
292}
293
294impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>
295    Entrylike<MCL, MCC, MPL, N, S, PD> for AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
296{
297    fn wdm_payload_length(&self) -> u64 {
298        self.entry.wdm_payload_length()
299    }
300
301    fn wdm_payload_digest(&self) -> &PD {
302        self.entry.wdm_payload_digest()
303    }
304}