nucypher_core/
hrac.rs

1use core::fmt;
2
3use generic_array::{sequence::Split, typenum::U16, GenericArray};
4use serde::{Deserialize, Serialize};
5use sha3::{digest::Update, Digest, Keccak256};
6use umbral_pre::{serde_bytes, PublicKey};
7
8/// "hashed resource access code".
9///
10/// A hash of:
11/// * Publisher's verifying key
12/// * Bob's verifying key
13/// * the label
14///
15/// Publisher and Bob have all the information they need to construct this.
16/// Ursula does not, so we share it with her.
17#[allow(clippy::upper_case_acronyms)]
18#[derive(PartialEq, Eq, Debug, Copy, Clone, Serialize, Deserialize)]
19pub struct HRAC(#[serde(with = "serde_bytes::as_hex")] [u8; HRAC::SIZE]);
20
21impl HRAC {
22    /// The size of HRAC in bytes.
23    pub const SIZE: usize = 16;
24
25    /// Creates a new HRAC.
26    pub fn new(
27        publisher_verifying_key: &PublicKey,
28        bob_verifying_key: &PublicKey,
29        label: &[u8],
30    ) -> Self {
31        let digest = Keccak256::new()
32            .chain(publisher_verifying_key.to_compressed_bytes())
33            .chain(bob_verifying_key.to_compressed_bytes())
34            .chain(label)
35            .finalize();
36
37        // No problem with hardcoding here, since the size will be checked in compile-time
38        let (hrac, _rest): (GenericArray<u8, U16>, GenericArray<u8, _>) = digest.split();
39        Self(hrac.into())
40    }
41}
42
43impl From<[u8; HRAC::SIZE]> for HRAC {
44    fn from(bytes: [u8; HRAC::SIZE]) -> Self {
45        Self(bytes)
46    }
47}
48
49impl AsRef<[u8]> for HRAC {
50    fn as_ref(&self) -> &[u8] {
51        self.0.as_ref()
52    }
53}
54
55impl fmt::Display for HRAC {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        let hex_repr = hex::encode(&self.0[..8]);
58        write!(f, "HRAC:{hex_repr}...")
59    }
60}