Skip to main content

offline_protocol_sealed/
error.rs

1//! Errors raised by the sealed layer.
2
3use alloc::string::String;
4use thiserror::Error;
5
6/// Result type alias for sealed-layer operations.
7pub type Result<T> = core::result::Result<T, SealedError>;
8
9/// Errors raised by the envelope codec, address derivation and the canonical
10/// signing-payload construction.
11///
12/// Every variant's `Display` string is byte-identical to the
13/// `offline_protocol_mls::MlsError` variant it maps onto, because these types
14/// moved out of that enum and their rendered text reaches logs, telemetry and
15/// two test suites that assert on substrings of it.
16///
17/// # Why this enum is not `#[non_exhaustive]`
18///
19/// Its sibling `MlsError` is, and the difference is deliberate.
20/// `From<SealedError> for MlsError` in the MLS crate must map every variant to
21/// the one it replaced; `#[non_exhaustive]` would force that impl to carry a
22/// wildcard arm, and a wildcard arm is exactly how a new variant added here
23/// silently starts rendering as some other error's text. Leaving the enum
24/// exhaustive makes adding a variant a compile error in the MLS crate until
25/// someone maps it, which is the point. Adding a variant is a breaking change
26/// for external consumers, and the workspace already moves every crate's
27/// version together.
28#[derive(Debug, Clone, PartialEq, Eq, Error)]
29pub enum SealedError {
30    /// Failed to serialize data.
31    #[error("Serialization failed: {0}")]
32    Serialization(String),
33
34    /// Failed to deserialize data.
35    #[error("Deserialization failed: {0}")]
36    Deserialization(String),
37
38    /// Group id failed storage-key validation (empty, oversized, or
39    /// containing path-traversal / storage-hostile characters).
40    #[error("Invalid group id: {0}")]
41    InvalidGroupId(String),
42
43    /// Invalid public key format.
44    #[error("Invalid public key: {0}")]
45    InvalidPublicKey(String),
46
47    /// A field of a canonical signing payload does not fit its `u32` length
48    /// prefix.
49    ///
50    /// Carried as its own variant rather than folded into
51    /// [`SealedError::Serialization`] so that both callers keep the exact
52    /// message they raised before this construction moved here: the engine
53    /// renders it bare into `Error::Other`, and the MLS crate wraps it in
54    /// `MlsError::Serialization`, which prepends its own prefix.
55    #[error("Field too large for canonical payload length prefix: {0} bytes")]
56    FieldTooLarge(usize),
57}