Skip to main content

reallyme_crypto/operations/
error.rs

1// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use thiserror::Error;
6
7/// Stable, redacted operation-layer error.
8///
9/// This is the domain error shape future operation implementations return
10/// before adapter-specific mapping. Variants carry only typed reasons so FFI,
11/// protobuf, SDK, and telemetry paths cannot accidentally include raw inputs,
12/// secrets, backend exception text, or arbitrary strings.
13#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
14#[non_exhaustive]
15pub enum OperationError {
16    /// The primitive rejected caller-controlled data or verification failed.
17    #[error("primitive operation failure: {reason}")]
18    Primitive {
19        /// Fixed primitive failure reason.
20        reason: PrimitiveErrorReason,
21    },
22    /// Provider policy, availability, or lane support rejected the request.
23    #[error("provider operation failure: {reason}")]
24    Provider {
25        /// Fixed provider failure reason.
26        reason: ProviderErrorReason,
27    },
28    /// Backend execution failed after the request crossed the semantic layer.
29    #[error("backend operation failure: {reason}")]
30    Backend {
31        /// Fixed backend failure reason.
32        reason: BackendErrorReason,
33    },
34}
35
36/// Primitive-origin failure reasons.
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38#[non_exhaustive]
39pub enum PrimitiveErrorReason {
40    /// A required algorithm selector was absent.
41    MissingAlgorithm,
42    /// A required operation selector was absent.
43    MissingOperation,
44    /// Key material failed length or mathematical validity checks.
45    InvalidKey,
46    /// A public key failed length, encoding, or mathematical validity checks.
47    InvalidPublicKey,
48    /// A private key failed length, encoding, or mathematical validity checks.
49    InvalidPrivateKey,
50    /// A ciphertext or encapsulated key failed structural validation.
51    MalformedCiphertext,
52    /// A typed operation parameter violated its protocol contract.
53    InvalidParameter,
54    /// Nonce, IV, salt, tag, signature, or ciphertext length was invalid.
55    InvalidLength,
56    /// A checked buffer length, offset, or capacity calculation overflowed.
57    LengthOverflow,
58    /// Authentication, MAC, signature, or constant-time equality failed.
59    VerificationFailed,
60    /// A raw key-agreement output failed contributory or shape validation.
61    InvalidSharedSecret,
62    /// A primitive resource limit was exceeded.
63    ResourceLimitExceeded,
64}
65
66impl core::fmt::Display for PrimitiveErrorReason {
67    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
68        let reason = match self {
69            PrimitiveErrorReason::MissingAlgorithm => "missing algorithm",
70            PrimitiveErrorReason::MissingOperation => "missing operation",
71            PrimitiveErrorReason::InvalidKey => "invalid key",
72            PrimitiveErrorReason::InvalidPublicKey => "invalid public key",
73            PrimitiveErrorReason::InvalidPrivateKey => "invalid private key",
74            PrimitiveErrorReason::MalformedCiphertext => "malformed ciphertext",
75            PrimitiveErrorReason::InvalidParameter => "invalid parameter",
76            PrimitiveErrorReason::InvalidLength => "invalid length",
77            PrimitiveErrorReason::LengthOverflow => "length overflow",
78            PrimitiveErrorReason::VerificationFailed => "verification failed",
79            PrimitiveErrorReason::InvalidSharedSecret => "invalid shared secret",
80            PrimitiveErrorReason::ResourceLimitExceeded => "resource limit exceeded",
81        };
82        write!(f, "{reason}")
83    }
84}
85
86/// Provider-origin failure reasons.
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88#[non_exhaustive]
89pub enum ProviderErrorReason {
90    /// The selected algorithm is not supported by the selected lane/provider.
91    UnsupportedAlgorithm,
92    /// The requested provider is unavailable in this build or on this device.
93    ProviderUnavailable,
94    /// The selected provider could not obtain cryptographic randomness.
95    RandomnessUnavailable,
96    /// Policy forbids fallback or cross-lane substitution for this operation.
97    FallbackProhibited,
98    /// Required platform authentication or hardware state is unavailable.
99    PlatformUnavailable,
100    /// A persistent platform key already exists for the requested identifier.
101    KeyExists,
102    /// The requested persistent platform key does not exist.
103    KeyNotFound,
104    /// Platform access-control policy denied the operation.
105    AccessDenied,
106    /// The platform requires user authentication before continuing.
107    UserAuthenticationRequired,
108    /// The user canceled an authentication or consent operation.
109    UserCanceled,
110    /// The requested hardware-backed security level is unavailable.
111    HardwareUnavailable,
112    /// The hardware provider rejected the key or operation.
113    HardwareRejectedKey,
114}
115
116impl core::fmt::Display for ProviderErrorReason {
117    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
118        let reason = match self {
119            ProviderErrorReason::UnsupportedAlgorithm => "unsupported algorithm",
120            ProviderErrorReason::ProviderUnavailable => "provider unavailable",
121            ProviderErrorReason::RandomnessUnavailable => "provider randomness unavailable",
122            ProviderErrorReason::FallbackProhibited => "fallback prohibited",
123            ProviderErrorReason::PlatformUnavailable => "platform unavailable",
124            ProviderErrorReason::KeyExists => "key exists",
125            ProviderErrorReason::KeyNotFound => "key not found",
126            ProviderErrorReason::AccessDenied => "access denied",
127            ProviderErrorReason::UserAuthenticationRequired => "user authentication required",
128            ProviderErrorReason::UserCanceled => "user canceled",
129            ProviderErrorReason::HardwareUnavailable => "hardware unavailable",
130            ProviderErrorReason::HardwareRejectedKey => "hardware rejected key",
131        };
132        write!(f, "{reason}")
133    }
134}
135
136/// Backend-origin failure reasons.
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138#[non_exhaustive]
139pub enum BackendErrorReason {
140    /// A backend returned an output shape that violates the operation contract.
141    InvalidOutput,
142    /// A panic firewall or equivalent adapter guard caught an unexpected fault.
143    PanicContained,
144    /// A backend reported a fixed, redacted internal failure.
145    Internal,
146}
147
148impl core::fmt::Display for BackendErrorReason {
149    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
150        let reason = match self {
151            BackendErrorReason::InvalidOutput => "invalid output",
152            BackendErrorReason::PanicContained => "panic contained",
153            BackendErrorReason::Internal => "internal failure",
154        };
155        write!(f, "{reason}")
156    }
157}