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}