paseto_core/paserk/
id.rs

1use alloc::string::ToString;
2use core::fmt;
3use core::marker::PhantomData;
4
5use crate::PasetoError;
6use crate::key::KeyType;
7use crate::paserk::KeyText;
8use crate::version::Version;
9
10/// This PASETO implementation allows extracting key ids
11pub trait IdVersion: Version {
12    /// How to hash some keydata for creating [`KeyId`]
13    fn hash_key(key_header: &'static str, key_data: &[u8]) -> [u8; 33];
14}
15
16/// A short ID for a key.
17pub struct KeyId<V: IdVersion, K: KeyType> {
18    pub(crate) id: [u8; 33],
19    _key: PhantomData<(V, K)>,
20}
21
22impl<V: IdVersion, K: KeyType> KeyId<V, K> {
23    /// View the raw ID bytes for this key id.
24    pub fn as_bytes(&self) -> &[u8; 33] {
25        &self.id
26    }
27}
28
29impl<V: IdVersion, K: KeyType> Copy for KeyId<V, K> {}
30
31impl<V: IdVersion, K: KeyType> Clone for KeyId<V, K> {
32    fn clone(&self) -> Self {
33        *self
34    }
35}
36
37impl<V: IdVersion, K: KeyType> PartialEq for KeyId<V, K> {
38    fn eq(&self, other: &Self) -> bool {
39        self.id == other.id
40    }
41}
42
43impl<V: IdVersion, K: KeyType> PartialOrd for KeyId<V, K> {
44    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
45        Some(self.cmp(other))
46    }
47}
48
49impl<V: IdVersion, K: KeyType> Eq for KeyId<V, K> {}
50
51impl<V: IdVersion, K: KeyType> Ord for KeyId<V, K> {
52    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
53        self.id.cmp(&other.id)
54    }
55}
56
57impl<V: IdVersion, K: KeyType> core::hash::Hash for KeyId<V, K> {
58    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
59        self.id.hash(state);
60    }
61}
62
63impl<V: IdVersion, K: KeyType> From<&KeyText<V, K>> for KeyId<V, K> {
64    fn from(value: &KeyText<V, K>) -> Self {
65        Self {
66            id: V::hash_key(K::ID_HEADER, value.to_string().as_bytes()),
67            _key: PhantomData,
68        }
69    }
70}
71
72impl<V: IdVersion, K: KeyType> fmt::Display for KeyId<V, K> {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        f.write_str(V::PASERK_HEADER)?;
75        f.write_str(K::ID_HEADER)?;
76        crate::base64::write_to_fmt(&self.id, f)
77    }
78}
79
80impl<V: IdVersion, K: KeyType> core::str::FromStr for KeyId<V, K> {
81    type Err = PasetoError;
82
83    fn from_str(s: &str) -> Result<Self, Self::Err> {
84        let s = s
85            .strip_prefix(V::PASERK_HEADER)
86            .ok_or(PasetoError::InvalidKey)?;
87        let s = s
88            .strip_prefix(K::ID_HEADER)
89            .ok_or(PasetoError::InvalidKey)?;
90
91        let mut id = [0u8; 33];
92        if crate::base64::decode(s, &mut id)?.len() != 33 {
93            return Err(PasetoError::InvalidKey);
94        }
95
96        Ok(Self {
97            id,
98            _key: PhantomData,
99        })
100    }
101}
102
103serde_str!(
104    impl<V, K> KeyId<V, K>
105    where
106        V: IdVersion,
107        K: KeyType,
108    {
109        fn expecting() {
110            format_args!("a {}{} PASERK key id", V::PASERK_HEADER, K::ID_HEADER)
111        }
112    }
113);