1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Error types for Cyphr.
use thiserror::Error;
/// Cyphr error type covering all error conditions from SPEC §17.
#[derive(Debug, Error)]
pub enum Error {
// === ParsedCoz errors (§17.1) ===
/// Signature does not verify against claimed key.
#[error("invalid signature")]
InvalidSignature,
/// Referenced key (`tmb` or `id`) not in current KS.
#[error("unknown key")]
UnknownKey,
/// Client doesn't know or support the algorithm.
#[error("unknown algorithm")]
UnknownAlg,
/// `pre` does not match current AS.
#[error("invalid prior state")]
InvalidPrior,
/// `now` < latest known PS timestamp.
#[error("timestamp in past")]
TimestampPast,
/// `now` > server time + tolerance.
#[error("timestamp in future")]
TimestampFuture,
/// Signing key has `rvk` ≤ `now`.
#[error("key revoked")]
KeyRevoked,
/// Missing required fields for coz type.
#[error("malformed payload")]
MalformedPayload,
/// `key/create` for key already in KS.
#[error("duplicate key")]
DuplicateKey,
/// Signing keys do not meet required weight (Level 5+).
#[error("threshold not met")]
ThresholdNotMet,
// === Recovery errors (§17.2) ===
/// Agent not registered via `recovery/designate`.
#[error("recovery not designated")]
RecoveryNotDesignated,
/// Recovery attempted while regular keys are active.
#[error("account recoverable")]
AccountRecoverable,
/// No active keys AND no designated recovery agents.
#[error("unrecoverable principal")]
UnrecoverablePrincipal,
// === State errors (§17.3) ===
/// Computed PS does not match claimed PS.
#[error("state mismatch")]
StateMismatch,
/// `pre` references do not form valid chain to known state.
#[error("chain broken")]
ChainBroken,
/// Multihash variant computed with wrong algorithm.
#[error("hash algorithm mismatch")]
HashAlgMismatch,
/// MultihashDigest contains no variants (internal invariant violation).
#[error("empty multihash digest")]
EmptyMultihash,
// === Action errors (§17.4) ===
/// Action `typ` not permitted for this key (Level 5+).
#[error("unauthorized action")]
UnauthorizedAction,
// === Internal ===
/// No active keys remain in principal.
#[error("no active keys")]
NoActiveKeys,
/// Algorithm not supported.
#[error("unsupported algorithm: {0}")]
UnsupportedAlgorithm(String),
// === Commit lifecycle errors ===
/// Attempted to finalize an empty commit (no cozies).
#[error("empty commit")]
EmptyCommit,
/// `commit` field appears on a non-terminal coz in the commit array.
///
/// Per SPEC §4.4, `commit` MUST only appear on the last coz.
#[error("commit field on non-terminal coz")]
CommitNotLast,
/// Terminal coz is missing the required `commit` field.
///
/// Per SPEC §4.4, the last coz MUST include `"commit":<CS>`.
#[error("missing commit field on terminal coz")]
MissingCommit,
/// `commit` field value does not match independently computed CS.
///
/// Per SPEC §4.4, the `commit` value must equal `MR(AS, DS?)`.
#[error("state root mismatch")]
CommitMismatch,
/// External reference to transitory (unfinalized) state root.
///
/// Per SPEC §4.2.1, transitory state during a pending commit cannot
/// be referenced by external cozies until the commit is finalized.
#[error("transitory state reference")]
TransitoryStateReference,
// === Digest parsing errors ===
/// Malformed tagged digest string (missing separator, invalid base64).
#[error("malformed digest: {0}")]
MalformedDigest(&'static str),
/// Digest length does not match the algorithm's expected output size.
#[error("digest length mismatch for {alg}: expected {expected} bytes, got {actual}")]
DigestLengthMismatch {
/// The hash algorithm specified in the tagged digest.
alg: crate::state::HashAlg,
/// Expected digest length in bytes for this algorithm.
expected: usize,
/// Actual digest length in bytes received.
actual: usize,
},
/// Underlying Coz error.
#[error("coz: {0}")]
Coz(#[from] coz::Error),
}
/// Result type for Cyphr operations.
pub type Result<T> = std::result::Result<T, Error>;