paseto_core/
tokens.rs

1//! Generic Tokens
2
3use alloc::boxed::Box;
4use alloc::vec::Vec;
5use core::marker::PhantomData;
6
7use crate::encodings::{Footer, Payload};
8use crate::key::Key;
9use crate::validation::Validate;
10use crate::version::{Local, Public};
11use crate::{
12    EncryptedToken, LocalKey, PasetoError, PublicKey, SecretKey, SignedToken, UnencryptedToken,
13    UnsignedToken, version,
14};
15
16/// An unsealed token.
17///
18/// This represents a PASETO which has had signatures or encryption validated.
19/// Using one of the following aliases is suggested
20/// * [`UnsignedToken`] - A [`public`](version::Public) PASETO which has had signature validated.
21/// * [`UnencryptedToken`] - A [`local`](version::Local) PASETO which has successfully been decrypted.
22///
23/// This type is un-serializable as it isn't sealed. For that you will want [`SealedToken`].
24pub struct UnsealedToken<V, P, M, F = ()> {
25    /// The message that was contained in the token
26    pub claims: M,
27    /// The footer that was sent with the token
28    pub footer: F,
29    pub(crate) _version: PhantomData<V>,
30    pub(crate) _purpose: PhantomData<P>,
31}
32
33impl<V: crate::version::Version, T: crate::version::Purpose, M> UnsealedToken<V, T, M> {
34    /// Create a new [`UnsealedToken`] builder with the given message payload
35    pub fn new(claims: M) -> Self {
36        UnsealedToken {
37            claims,
38            footer: (),
39            _version: PhantomData,
40            _purpose: PhantomData,
41        }
42    }
43}
44
45impl<V, T, M> UnsealedToken<V, T, M, ()> {
46    /// Set the footer for this token.
47    ///
48    /// Footers are embedded into the token as base64 only. They are authenticated but not encrypted.
49    pub fn with_footer<F>(self, footer: F) -> UnsealedToken<V, T, M, F> {
50        UnsealedToken {
51            claims: self.claims,
52            footer,
53            _version: self._version,
54            _purpose: self._purpose,
55        }
56    }
57}
58
59/// A secured token.
60///
61/// This represents a PASETO that is signed or encrypted.
62/// Using one of the following aliases is suggested
63/// * [`SignedToken`] - A [`public`](version::Public) PASETO that is signed.
64/// * [`EncryptedToken`] - A [`local`](version::Local) PASETO that is encryption.
65///
66/// This type has a payload that is currently inaccessible. To access it, you will need to
67/// decrypt/verify the contents. For that you will want [`UnsealedToken`].
68///
69/// To convert to an [`UnsealedToken`], you will need to use either
70/// * [`SignedToken::verify`]
71/// * [`EncryptedToken::decrypt`]
72pub struct SealedToken<V, P, M, F = ()> {
73    pub(crate) payload: Box<[u8]>,
74    pub(crate) encoded_footer: Box<[u8]>,
75    pub(crate) footer: F,
76    pub(crate) _version: PhantomData<V>,
77    pub(crate) _purpose: PhantomData<P>,
78    pub(crate) _message: PhantomData<M>,
79}
80
81impl<V, T, M, F> SealedToken<V, T, M, F> {
82    /// View the **unverified** footer for this token
83    pub fn unverified_footer(&self) -> &F {
84        &self.footer
85    }
86}
87
88impl<V, P, M, F> SealedToken<V, P, M, F>
89where
90    V: version::UnsealingVersion<P>,
91    P: version::Purpose,
92    M: Payload,
93    F: Footer,
94{
95    /// Unseal a token and validate the claims inside.
96    #[doc(alias = "decrypt")]
97    #[doc(alias = "verify")]
98    pub fn unseal(
99        mut self,
100        key: &Key<V, P>,
101        aad: &[u8],
102        v: &impl Validate<Claims = M>,
103    ) -> Result<UnsealedToken<V, P, M, F>, PasetoError> {
104        let cleartext = V::unseal(
105            &key.0,
106            M::SUFFIX,
107            &mut self.payload,
108            &self.encoded_footer,
109            aad,
110        )?;
111
112        let message = M::decode(cleartext).map_err(PasetoError::PayloadError)?;
113
114        v.validate(&message)?;
115
116        Ok(UnsealedToken {
117            claims: message,
118            footer: self.footer,
119            _version: PhantomData,
120            _purpose: PhantomData,
121        })
122    }
123}
124
125impl<V, P, M, F> UnsealedToken<V, P, M, F>
126where
127    V: version::SealingVersion<P>,
128    P: version::Purpose,
129    M: Payload,
130    F: Footer,
131{
132    /// Seal a token and authenticate the claims
133    #[doc(alias = "encrypt")]
134    #[doc(alias = "sign")]
135    #[inline(always)]
136    pub fn seal(
137        self,
138        key: &Key<V, P::SealingKey>,
139        aad: &[u8],
140    ) -> Result<SealedToken<V, P, M, F>, PasetoError> {
141        self.dangerous_seal_with_nonce(key, aad, V::nonce()?)
142    }
143
144    /// Use [`UnsealedToken::seal`](crate::tokens::UnsealedToken::seal) instead.
145    ///
146    /// This is provided for testing purposes only.
147    /// Do not use this method directly.
148    pub fn dangerous_seal_with_nonce(
149        self,
150        key: &Key<V, P::SealingKey>,
151        aad: &[u8],
152        nonce: Vec<u8>,
153    ) -> Result<SealedToken<V, P, M, F>, PasetoError> {
154        let mut footer = Vec::new();
155        self.footer
156            .encode(&mut footer)
157            .map_err(PasetoError::PayloadError)?;
158        let footer = footer.into_boxed_slice();
159
160        let mut payload = nonce;
161        self.claims
162            .encode(&mut payload)
163            .map_err(PasetoError::PayloadError)?;
164
165        let payload = V::dangerous_seal_with_nonce(&key.0, M::SUFFIX, payload, &footer, aad)?
166            .into_boxed_slice();
167
168        Ok(SealedToken {
169            payload,
170            encoded_footer: footer,
171            footer: self.footer,
172            _version: PhantomData,
173            _purpose: PhantomData,
174            _message: PhantomData,
175        })
176    }
177}
178
179impl<V, M, F> EncryptedToken<V, M, F>
180where
181    V: version::UnsealingVersion<Local>,
182    M: Payload,
183    F: Footer,
184{
185    /// Try to decrypt the token
186    #[inline(always)]
187    pub fn decrypt(
188        self,
189        key: &LocalKey<V>,
190        v: &impl Validate<Claims = M>,
191    ) -> Result<UnencryptedToken<V, M, F>, PasetoError> {
192        self.decrypt_with_aad(key, &[], v)
193    }
194
195    /// Try to decrypt the token and authenticate the implicit assertion
196    #[inline(always)]
197    pub fn decrypt_with_aad(
198        self,
199        key: &LocalKey<V>,
200        aad: &[u8],
201        v: &impl Validate<Claims = M>,
202    ) -> Result<UnencryptedToken<V, M, F>, PasetoError> {
203        self.unseal(key, aad, v)
204    }
205}
206
207impl<V, M, F> UnencryptedToken<V, M, F>
208where
209    V: version::SealingVersion<Local>,
210    M: Payload,
211    F: Footer,
212{
213    /// Encrypt the token
214    #[inline(always)]
215    pub fn encrypt(self, key: &LocalKey<V>) -> Result<EncryptedToken<V, M, F>, PasetoError> {
216        self.encrypt_with_aad(key, &[])
217    }
218
219    /// Encrypt the token, additionally authenticating the implicit assertions.
220    #[inline(always)]
221    pub fn encrypt_with_aad(
222        self,
223        key: &LocalKey<V>,
224        aad: &[u8],
225    ) -> Result<EncryptedToken<V, M, F>, PasetoError> {
226        self.seal(key, aad)
227    }
228}
229
230impl<V, M, F> SignedToken<V, M, F>
231where
232    V: version::UnsealingVersion<Public>,
233    M: Payload,
234    F: Footer,
235{
236    /// Try to verify the token signature
237    #[inline(always)]
238    pub fn verify(
239        self,
240        key: &PublicKey<V>,
241        v: &impl Validate<Claims = M>,
242    ) -> Result<UnsignedToken<V, M, F>, PasetoError> {
243        self.verify_with_aad(key, &[], v)
244    }
245
246    /// Try to verify the token signature and authenticate the implicit assertion
247    #[inline(always)]
248    pub fn verify_with_aad(
249        self,
250        key: &PublicKey<V>,
251        aad: &[u8],
252        v: &impl Validate<Claims = M>,
253    ) -> Result<UnsignedToken<V, M, F>, PasetoError> {
254        self.unseal(key, aad, v)
255    }
256}
257
258impl<V, M, F> UnsignedToken<V, M, F>
259where
260    V: version::SealingVersion<Public>,
261    M: Payload,
262    F: Footer,
263{
264    /// Sign the token
265    #[inline(always)]
266    pub fn sign(self, key: &SecretKey<V>) -> Result<SignedToken<V, M, F>, PasetoError> {
267        self.sign_with_aad(key, &[])
268    }
269
270    /// Sign the token, additionally authenticating the implicit assertions.
271    #[inline(always)]
272    pub fn sign_with_aad(
273        self,
274        key: &SecretKey<V>,
275        aad: &[u8],
276    ) -> Result<SignedToken<V, M, F>, PasetoError> {
277        self.seal(key, aad)
278    }
279}