bc_components/id/
arid.rs

1use bc_rand::random_data;
2use bc_ur::prelude::*;
3
4use crate::tags;
5use anyhow::{ bail, Result };
6
7/// An "Apparently Random Identifier" (ARID)
8///
9/// An ARID is a cryptographically strong, universally unique identifier with the following properties:
10/// - Non-correlatability: The sequence of bits cannot be correlated with its referent or any other ARID
11/// - Neutral semantics: Contains no inherent type information
12/// - Open generation: Any method of generation is allowed as long as it produces statistically random bits
13/// - Minimum strength: Must be 256 bits (32 bytes) in length
14/// - Cryptographic suitability: Can be used as inputs to cryptographic constructs
15///
16/// Unlike digests/hashes which identify a fixed, immutable state of data, ARIDs can serve as stable
17/// identifiers for mutable data structures.
18///
19/// ARIDs should not be confused with or cast to/from other identifier types (like UUIDs),
20/// used as nonces, keys, or cryptographic seeds.
21///
22/// As defined in [BCR-2022-002](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2022-002-arid.md).
23#[derive(Clone, Copy, Eq, PartialEq, Hash)]
24pub struct ARID([u8; Self::ARID_SIZE]);
25
26impl ARID {
27    pub const ARID_SIZE: usize = 32;
28
29    /// Create a new random ARID.
30    pub fn new() -> Self {
31        let data = random_data(Self::ARID_SIZE);
32        Self::from_data_ref(data).unwrap()
33    }
34
35    /// Restore a ARID from a fixed-size array of bytes.
36    pub fn from_data(data: [u8; Self::ARID_SIZE]) -> Self {
37        Self(data)
38    }
39
40    /// Create a new ARID from a reference to an array of bytes.
41    pub fn from_data_ref(data: impl AsRef<[u8]>) -> Result<Self> {
42        let data = data.as_ref();
43        if data.len() != Self::ARID_SIZE {
44            bail!("Invalid ARID size");
45        }
46        let mut arr = [0u8; Self::ARID_SIZE];
47        arr.copy_from_slice(data);
48        Ok(Self::from_data(arr))
49    }
50
51    /// Get the data of the ARID.
52    pub fn data(&self) -> &[u8] {
53        self.into()
54    }
55
56    /// Create a new ARID from the given hexadecimal string.
57    ///
58    /// # Panics
59    /// Panics if the string is not exactly 64 hexadecimal digits.
60    pub fn from_hex(hex: impl AsRef<str>) -> Self {
61        Self::from_data_ref(hex::decode(hex.as_ref()).unwrap()).unwrap()
62    }
63
64    /// The data as a hexadecimal string.
65    pub fn hex(&self) -> String {
66        hex::encode(self.data())
67    }
68
69    /// The first four bytes of the ARID as a hexadecimal string.
70    pub fn short_description(&self) -> String {
71        hex::encode(&self.0[0..4])
72    }
73}
74
75/// Implements the Default trait to create a new random ARID.
76impl Default for ARID {
77    fn default() -> Self {
78        Self::new()
79    }
80}
81
82/// Implements conversion from an ARID reference to a byte slice reference.
83impl<'a> From<&'a ARID> for &'a [u8] {
84    fn from(value: &'a ARID) -> Self {
85        &value.0
86    }
87}
88
89/// Implements the CBORTagged trait to provide CBOR tag information.
90impl CBORTagged for ARID {
91    fn cbor_tags() -> Vec<Tag> {
92        tags_for_values(&[tags::TAG_ARID])
93    }
94}
95
96/// Implements conversion from ARID to CBOR for serialization.
97impl From<ARID> for CBOR {
98    fn from(value: ARID) -> Self {
99        value.tagged_cbor()
100    }
101}
102
103/// Implements CBORTaggedEncodable to provide CBOR encoding functionality.
104impl CBORTaggedEncodable for ARID {
105    fn untagged_cbor(&self) -> CBOR {
106        CBOR::to_byte_string(self.data())
107    }
108}
109
110/// Implements `TryFrom<CBOR>` for ARID to support conversion from CBOR data.
111impl TryFrom<CBOR> for ARID {
112    type Error = dcbor::Error;
113
114    fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
115        Self::from_tagged_cbor(cbor)
116    }
117}
118
119/// Implements CBORTaggedDecodable to provide CBOR decoding functionality.
120impl CBORTaggedDecodable for ARID {
121    fn from_untagged_cbor(untagged_cbor: CBOR) -> dcbor::Result<Self> {
122        let data = CBOR::try_into_byte_string(untagged_cbor)?;
123        Ok(Self::from_data_ref(data)?)
124    }
125}
126
127/// Implements Debug formatting for ARID showing the full hex representation.
128impl std::fmt::Debug for ARID {
129    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130        write!(f, "ARID({})", self.hex())
131    }
132}
133
134/// Implements Display formatting for ARID showing the hex representation.
135impl std::fmt::Display for ARID {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        write!(f, "ARID({})", self.hex())
138    }
139}
140
141/// Implements PartialOrd to allow ARIDs to be compared and ordered.
142impl PartialOrd for ARID {
143    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
144        Some(self.0.cmp(&other.0))
145    }
146}