Skip to main content

pubhubs/
id.rs

1//! [`Id`]s for PubHubs objects like [hub](crate::hub::BasicInfo)s and [attrbute
2//! types](crate::attr::Type).
3use crate::misc::serde_ext::{self, bytes_wrapper};
4
5/// An identifier, a random 256-bit number, which is encoded
6/// using unpadded url-safe base64
7#[allow( // not "expect", because of https://github.com/rust-lang/rust-clippy/issues/13356
8    clippy::derived_hash_with_manual_eq,
9    reason = "the manual PartialEq implementation agrees with the default one, but is constant time"
10)]
11#[derive(Clone, Copy, Debug, Eq, Hash, serde::Serialize, serde::Deserialize)]
12#[serde(transparent)]
13pub struct Id {
14    inner: bytes_wrapper::B64UU<serde_ext::ByteArray<32>>,
15}
16
17impl Id {
18    /// Creates a new random id
19    pub fn random() -> Self {
20        crate::misc::crypto::random_32_bytes().into()
21    }
22
23    /// Returns byte slice to underlying `[u8; 32]`.
24    pub fn as_slice(&self) -> &[u8] {
25        self.inner.as_slice()
26    }
27}
28
29impl From<[u8; 32]> for Id {
30    fn from(bytes: [u8; 32]) -> Self {
31        Id {
32            inner: serde_ext::ByteArray::<32>::from(bytes).into(),
33        }
34    }
35}
36
37impl core::str::FromStr for Id {
38    type Err = <bytes_wrapper::B64UU<[u8; 32]> as core::str::FromStr>::Err;
39
40    fn from_str(s: &str) -> Result<Self, Self::Err> {
41        Ok(Id { inner: s.parse()? })
42    }
43}
44
45impl std::fmt::Display for Id {
46    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
47        write!(f, "{}", self.inner)
48    }
49}
50
51/// Compare [`Id`]s, in constant time.
52impl PartialEq for Id {
53    fn eq(&self, other: &Id) -> bool {
54        subtle::ConstantTimeEq::ct_eq(self.inner.as_slice(), other.inner.as_slice()).into()
55    }
56}
57
58impl PartialOrd for Id {
59    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
60        Some(self.cmp(other))
61    }
62}
63
64impl Ord for Id {
65    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
66        self.inner.as_slice().cmp(other.inner.as_slice())
67    }
68}
69
70impl crate::common::secret::DigestibleSecret for Id {
71    fn as_bytes(&self) -> &[u8] {
72        self.as_slice()
73    }
74}