openmls/ciphersuite/
hash_ref.rs

1//! # Hash References
2//!
3//!
4//! Some MLS messages refer to other MLS objects by hash.  For example, Welcome
5//! messages refer to KeyPackages for the members being welcomed, and Commits refer
6//! to Proposals they cover.  These identifiers are computed as follows:
7//!
8//! ```text
9//! opaque HashReference<V>;
10//!
11//! MakeKeyPackageRef(value) = RefHash("MLS 1.0 KeyPackage Reference", value)
12//! MakeProposalRef(value)   = RefHash("MLS 1.0 Proposal Reference", value)
13//!
14//! RefHash(label, value) = Hash(RefHashInput)
15//!
16//! Where RefHashInput is defined as:
17//!
18//! struct {
19//!  opaque label<V> = label;
20//!  opaque value<V> = value;
21//! } RefHashInput;
22//! ```
23//!
24//! For a KeyPackageRef, the `value` input is the encoded KeyPackage, and the
25//! ciphersuite specified in the KeyPackage determines the hash function used.  For a
26//! ProposalRef, the `value` input is the PublicMessage carrying the proposal, and
27//! the hash function is determined by the group's ciphersuite.
28
29use openmls_traits::{crypto::OpenMlsCrypto, types::CryptoError};
30use serde::{Deserialize, Serialize};
31use tls_codec::{
32    Serialize as TlsSerializeTrait, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize,
33    VLByteSlice, VLBytes,
34};
35
36use super::Ciphersuite;
37
38const KEY_PACKAGE_REF_LABEL: &[u8; 28] = b"MLS 1.0 KeyPackage Reference";
39const PROPOSAL_REF_LABEL: &[u8; 26] = b"MLS 1.0 Proposal Reference";
40
41/// A reference to an MLS object computed as a hash of the value.
42#[derive(
43    Clone,
44    Hash,
45    PartialEq,
46    Eq,
47    Serialize,
48    Ord,
49    PartialOrd,
50    Deserialize,
51    TlsDeserialize,
52    TlsDeserializeBytes,
53    TlsSerialize,
54    TlsSize,
55)]
56pub struct HashReference {
57    value: VLBytes,
58}
59
60/// A reference to a key package.
61/// This value uniquely identifies a key package.
62pub type KeyPackageRef = HashReference;
63
64/// A reference to a proposal.
65/// This value uniquely identifies a proposal.
66pub type ProposalRef = HashReference;
67
68#[derive(TlsSerialize, TlsSize)]
69struct HashReferenceInput<'a> {
70    label: VLByteSlice<'a>,
71    value: VLBytes,
72}
73
74/// Compute a new [`ProposalRef`] value for a `value`.
75pub fn make_proposal_ref(
76    value: &[u8],
77    ciphersuite: Ciphersuite,
78    crypto: &impl OpenMlsCrypto,
79) -> Result<ProposalRef, CryptoError> {
80    HashReference::new(value, ciphersuite, crypto, PROPOSAL_REF_LABEL)
81}
82
83/// Compute a new [`KeyPackageRef`] value for a `value`.
84pub fn make_key_package_ref(
85    value: &[u8],
86    ciphersuite: Ciphersuite,
87    crypto: &impl OpenMlsCrypto,
88) -> Result<KeyPackageRef, CryptoError> {
89    HashReference::new(value, ciphersuite, crypto, KEY_PACKAGE_REF_LABEL)
90}
91
92impl HashReference {
93    /// Compute a new [`HashReference`] value for a `value`.
94    pub fn new(
95        value: &[u8],
96        ciphersuite: Ciphersuite,
97        crypto: &impl OpenMlsCrypto,
98        label: &[u8],
99    ) -> Result<Self, CryptoError> {
100        let input = HashReferenceInput {
101            label: VLByteSlice(label),
102            value: VLBytes::new(value.to_vec()),
103        };
104        let payload = input
105            .tls_serialize_detached()
106            .map_err(|_| CryptoError::TlsSerializationError)?;
107        let value = crypto.hash(ciphersuite.hash_algorithm(), &payload)?;
108        Ok(Self {
109            value: VLBytes::new(value),
110        })
111    }
112
113    /// Get a reference to the hash reference's value as slice.
114    pub fn as_slice(&self) -> &[u8] {
115        self.value.as_slice()
116    }
117
118    #[cfg(any(feature = "test-utils", test))]
119    pub fn from_slice(slice: &[u8]) -> Self {
120        Self {
121            value: VLBytes::from(slice),
122        }
123    }
124}
125
126impl core::fmt::Display for HashReference {
127    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128        write!(f, "HashReference: ")?;
129        for b in self.value.as_slice() {
130            write!(f, "{b:02X}")?;
131        }
132        Ok(())
133    }
134}
135
136impl core::fmt::Debug for HashReference {
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        write!(f, "{self}")
139    }
140}