Skip to main content

exo_identity/
error.rs

1// Copyright 2026 Exochain Foundation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at:
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// SPDX-License-Identifier: Apache-2.0
16
17//! Identity-specific error types for the EXOCHAIN identity subsystem.
18
19use exo_core::{Did, Timestamp};
20
21/// Errors that can occur during identity operations.
22#[derive(Debug, thiserror::Error)]
23pub enum IdentityError {
24    #[error("DID already registered: {0}")]
25    DuplicateDid(Did),
26
27    #[error(
28        "DID registry capacity exceeded: max_documents={max_documents}, attempted_documents={attempted_documents}"
29    )]
30    RegistryCapacityExceeded {
31        max_documents: usize,
32        attempted_documents: usize,
33    },
34
35    #[error("DID document for {did} has invalid field {field}: {reason}")]
36    InvalidDidDocumentField {
37        did: String,
38        field: String,
39        reason: String,
40    },
41
42    #[error("DID document for {did} exceeds {field} bound: max={max}, actual={actual}")]
43    DidDocumentFieldTooLarge {
44        did: String,
45        field: String,
46        max: usize,
47        actual: usize,
48    },
49
50    #[error("DID not found: {0}")]
51    DidNotFound(Did),
52
53    #[error("DID has been revoked: {0}")]
54    DidRevoked(Did),
55
56    #[error("invalid signature")]
57    InvalidSignature,
58
59    #[error("non-monotonic timestamp for DID {did}: current={current}, proposed={proposed}")]
60    NonMonotonicTimestamp {
61        did: Did,
62        current: Timestamp,
63        proposed: Timestamp,
64    },
65
66    #[error("invalid revocation proof for DID: {0}")]
67    InvalidRevocationProof(Did),
68
69    #[error("invalid registration proof for DID {did}: {reason}")]
70    InvalidRegistrationProof { did: Did, reason: String },
71
72    #[error("registration proof payload encoding failed for DID {did}: {reason}")]
73    RegistrationProofPayloadEncoding { did: Did, reason: String },
74
75    #[error("revocation proof payload encoding failed for DID {did}: {reason}")]
76    RevocationProofPayloadEncoding { did: Did, reason: String },
77
78    #[error("key rotation proof payload encoding failed for DID {did}: {reason}")]
79    KeyRotationProofPayloadEncoding { did: Did, reason: String },
80
81    #[error("public key not found on DID: {0}")]
82    KeyNotFound(Did),
83
84    #[error("key already revoked")]
85    KeyAlreadyRevoked,
86
87    #[error("key already rotated")]
88    KeyAlreadyRotated,
89
90    #[error("invalid Shamir config: threshold={threshold}, shares={shares}")]
91    InvalidShamirConfig { threshold: u8, shares: u8 },
92
93    #[error(
94        "invalid Shamir entropy: min_bytes={min_bytes}, got_bytes={got_bytes}, reason={reason}"
95    )]
96    InvalidShamirEntropy {
97        min_bytes: usize,
98        got_bytes: usize,
99        reason: String,
100    },
101
102    #[error("Shamir input {field} exceeds deterministic encoding bound: max={max}, got={got}")]
103    ShamirInputTooLarge {
104        field: &'static str,
105        max: u64,
106        got: usize,
107    },
108
109    #[error("insufficient shares: need {need}, got {got}")]
110    InsufficientShares { need: u8, got: u8 },
111
112    #[error("invalid share index: {0}")]
113    InvalidShareIndex(u8),
114
115    #[error("share index {index} exceeds configured share count {shares}")]
116    ShareIndexOutOfRange { index: u8, shares: u8 },
117
118    #[error("invalid share length for index {index}: expected {expected}, got {got}")]
119    InvalidShareLength {
120        index: u8,
121        expected: usize,
122        got: usize,
123    },
124
125    #[error("share commitment mismatch at index {index}: expected {expected:?}, got {got:?}")]
126    ShareCommitmentMismatch {
127        index: u8,
128        expected: [u8; 32],
129        got: [u8; 32],
130    },
131
132    #[error("reconstructed secret commitment mismatch: expected {expected:?}, got {got:?}")]
133    ReconstructedSecretCommitmentMismatch { expected: [u8; 32], got: [u8; 32] },
134
135    #[error(
136        "invalid share value at index {index}, byte {byte_index}: expected {expected}, got {got}"
137    )]
138    InvalidShareValue {
139        index: u8,
140        byte_index: usize,
141        expected: u8,
142        got: u8,
143    },
144
145    #[error("duplicate share indices")]
146    DuplicateShareIndices,
147
148    #[error("invalid PACE config: {0}")]
149    InvalidPaceConfig(String),
150
151    #[error("cannot escalate: already at maximum level")]
152    CannotEscalate,
153
154    #[error("cannot de-escalate: already at Normal")]
155    CannotDeescalate,
156
157    #[error("risk attestation expired")]
158    AttestationExpired,
159
160    #[error("risk attestation signing payload encoding failed: {reason}")]
161    RiskAttestationSigningPayloadEncoding { reason: String },
162
163    #[error(
164        "risk attestation expiry overflow: now_physical_ms={now_physical_ms}, validity_ms={validity_ms}"
165    )]
166    RiskAttestationExpiryOverflow {
167        now_physical_ms: u64,
168        validity_ms: u64,
169    },
170
171    #[error("duplicate DID across PACE levels: {0}")]
172    DuplicatePaceDid(Did),
173
174    #[error("vault key derivation failed: {0}")]
175    VaultKeyDerivationFailed(String),
176
177    #[error("vault encryption requires a caller-supplied nonce")]
178    VaultNonceRequired,
179
180    #[error("invalid vault nonce: {reason}")]
181    InvalidVaultNonce { reason: String },
182
183    #[error("vault encryption failed: {0}")]
184    VaultEncryptionFailed(String),
185
186    #[error("vault decryption failed: authentication or ciphertext invalid")]
187    VaultDecryptionFailed,
188
189    #[error("vault ciphertext too short")]
190    VaultCiphertextTooShort,
191}