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}