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#[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 pub const SIZE: usize = 16;
24
25 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 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}