paseto_core/
key.rs

1use core::fmt;
2use std::hash::Hash;
3use std::marker::PhantomData;
4
5use base64ct::Encoding;
6
7use crate::PasetoError;
8use crate::version::{self, Marker};
9
10/// Defines a PASERK key type
11pub trait Key: Clone {
12    type Version: version::Version;
13    type KeyType: Marker;
14
15    fn encode(&self) -> Box<[u8]>;
16    fn decode(bytes: &[u8]) -> Result<Self, PasetoError>;
17}
18
19/// Defines a secret PASETO key that can be used to create PASETO tokens.
20///
21/// We define "sealing" as encrypting or deriving a new signature.
22pub trait SealingKey<Purpose>: Key {
23    /// The type of key that can unseal the tokens we will seal.
24    type UnsealingKey: UnsealingKey<Purpose, Version = Self::Version>;
25
26    /// Generate the key that can unseal the tokens this key will seal.
27    fn unsealing_key(&self) -> Self::UnsealingKey;
28
29    /// Generate a random key
30    fn random() -> Result<Self, PasetoError>;
31
32    /// Do not call this method directly.
33    fn nonce() -> Result<Vec<u8>, PasetoError>;
34
35    /// Do not call this method directly. Use [`UnsealedToken::seal`](crate::tokens::UnsealedToken::seal) instead.
36    fn dangerous_seal_with_nonce(
37        &self,
38        encoding: &'static str,
39        nonce: Vec<u8>,
40        footer: &[u8],
41        aad: &[u8],
42    ) -> Result<Vec<u8>, PasetoError>;
43}
44
45/// Defines a PASETO key that can be used to validate and read PASETO tokens.
46///
47/// We define "unsealing" as decrypting or validating a signature.
48pub trait UnsealingKey<Purpose>: Key {
49    fn unseal<'a>(
50        &self,
51        encoding: &'static str,
52        payload: &'a mut [u8],
53        footer: &[u8],
54        aad: &[u8],
55    ) -> Result<&'a [u8], PasetoError>;
56}
57
58/// A short ID for a key.
59pub struct KeyId<K: Key> {
60    id: [u8; 33],
61    _key: PhantomData<K>,
62}
63
64impl<K: Key> PartialEq for KeyId<K> {
65    fn eq(&self, other: &Self) -> bool {
66        self.id == other.id
67    }
68}
69
70impl<K: Key> PartialOrd for KeyId<K> {
71    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
72        Some(self.cmp(other))
73    }
74}
75
76impl<K: Key> Eq for KeyId<K> {}
77
78impl<K: Key> Ord for KeyId<K> {
79    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
80        self.id.cmp(&other.id)
81    }
82}
83
84impl<K: Key> Hash for KeyId<K> {
85    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
86        self.id.hash(state);
87    }
88}
89
90impl<K: Key> From<&KeyText<K>> for KeyId<K> {
91    fn from(value: &KeyText<K>) -> Self {
92        Self {
93            id: <K::Version as version::Version>::hash_key(
94                <K::KeyType as Marker>::ID_HEADER,
95                value.to_string().as_bytes(),
96            ),
97            _key: value._key,
98        }
99    }
100}
101
102/// A plaintext encoding of a key.
103///
104/// Be advised that this encoding has no extra security, so it is not safe to transport as is.
105pub struct KeyText<K: Key> {
106    data: Box<[u8]>,
107    _key: PhantomData<K>,
108}
109
110impl<K: Key> PartialEq for KeyText<K> {
111    fn eq(&self, other: &Self) -> bool {
112        self.data == other.data
113    }
114}
115
116impl<K: Key> PartialOrd for KeyText<K> {
117    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
118        Some(self.cmp(other))
119    }
120}
121
122impl<K: Key> Eq for KeyText<K> {}
123
124impl<K: Key> Ord for KeyText<K> {
125    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
126        self.data.cmp(&other.data)
127    }
128}
129
130impl<K: Key> Hash for KeyText<K> {
131    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
132        self.data.hash(state);
133    }
134}
135
136impl<K: Key> KeyText<K> {
137    pub fn decode(&self) -> Result<K, PasetoError> {
138        K::decode(&self.data)
139    }
140}
141
142impl<K: Key> From<&K> for KeyText<K> {
143    fn from(value: &K) -> Self {
144        Self {
145            data: value.encode(),
146            _key: PhantomData,
147        }
148    }
149}
150
151impl<K: Key> fmt::Display for KeyId<K> {
152    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153        f.write_str(<K::Version as version::Version>::PASERK_HEADER)?;
154        f.write_str(<K::KeyType as Marker>::ID_HEADER)?;
155
156        let mut id = [0u8; 44];
157        let id = &<base64ct::Base64UrlUnpadded as Encoding>::encode(&self.id, &mut id)
158            .map_err(|_| fmt::Error)?;
159        f.write_str(id)
160    }
161}
162
163impl<K: Key> std::str::FromStr for KeyId<K> {
164    type Err = PasetoError;
165
166    fn from_str(s: &str) -> Result<Self, Self::Err> {
167        let s = s
168            .strip_prefix(<K::Version as version::Version>::PASERK_HEADER)
169            .ok_or(PasetoError::InvalidKey)?;
170        let s = s
171            .strip_prefix(<K::KeyType as Marker>::ID_HEADER)
172            .ok_or(PasetoError::InvalidKey)?;
173
174        let mut id = [0u8; 33];
175        let len = <base64ct::Base64UrlUnpadded as Encoding>::decode(s, &mut id)
176            .map_err(|_| PasetoError::Base64DecodeError)?
177            .len();
178
179        if len != 33 {
180            return Err(PasetoError::InvalidKey);
181        }
182
183        Ok(Self {
184            id,
185            _key: PhantomData,
186        })
187    }
188}
189
190impl<K: Key> fmt::Display for KeyText<K> {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        f.write_str(<K::Version as version::Version>::PASERK_HEADER)?;
193        f.write_str(<K::KeyType as Marker>::HEADER)?;
194        f.write_str(&base64ct::Base64UrlUnpadded::encode_string(&self.data))
195    }
196}
197
198impl<K: Key> std::str::FromStr for KeyText<K> {
199    type Err = PasetoError;
200
201    fn from_str(s: &str) -> Result<Self, Self::Err> {
202        let s = s
203            .strip_prefix(<K::Version as version::Version>::PASERK_HEADER)
204            .ok_or(PasetoError::InvalidKey)?;
205        let s = s
206            .strip_prefix(<K::KeyType as Marker>::HEADER)
207            .ok_or(PasetoError::InvalidKey)?;
208
209        let data = base64ct::Base64UrlUnpadded::decode_vec(s)
210            .map_err(|_| PasetoError::Base64DecodeError)?
211            .into_boxed_slice();
212
213        Ok(Self {
214            data,
215            _key: PhantomData,
216        })
217    }
218}