Skip to main content

xmtp_mls_common/
commit_log.rs

1use openmls::prelude::{OpenMlsCrypto, SignatureScheme};
2use prost::Message;
3use xmtp_cryptography::Secret;
4use xmtp_proto::xmtp::{
5    identity::associations::RecoverableEd25519Signature,
6    mls::message_contents::PlaintextCommitLogEntry,
7};
8
9/// Decode a commit-log entry without checking its signature or hash chain.
10pub fn decode_commit_log(data: &[u8]) -> Result<PlaintextCommitLogEntry, prost::DecodeError> {
11    PlaintextCommitLogEntry::decode(data)
12}
13
14pub struct SignedCommitLogEntry {
15    pub serialized_commit_log_entry: Vec<u8>,
16    pub signature: RecoverableEd25519Signature,
17}
18
19#[derive(Debug, thiserror::Error)]
20pub enum CommitLogSigningError {
21    /// Signing failed. Not retryable.
22    #[error(transparent)]
23    Crypto(#[from] openmls::prelude::CryptoError),
24    /// The signing key has an invalid length. Not retryable.
25    #[error(transparent)]
26    KeyLength(#[from] std::array::TryFromSliceError),
27}
28
29/// Construct signed protocol bytes. The caller owns key storage and publication.
30pub fn sign_commit_log(
31    entry: &PlaintextCommitLogEntry,
32    private_key: &Secret,
33    crypto: &impl OpenMlsCrypto,
34) -> Result<SignedCommitLogEntry, CommitLogSigningError> {
35    let serialized_commit_log_entry = entry.encode_to_vec();
36    let bytes = crypto.sign(
37        SignatureScheme::ED25519,
38        &serialized_commit_log_entry,
39        private_key.as_slice(),
40    )?;
41    let public_key = xmtp_cryptography::signature::to_public_key(private_key)?.to_vec();
42    Ok(SignedCommitLogEntry {
43        serialized_commit_log_entry,
44        signature: RecoverableEd25519Signature { bytes, public_key },
45    })
46}