Skip to main content

crypto_core/
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/// Backend implementation that performed an AEAD operation.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum AeadBackend {
10    /// Pure-Rust native backend.
11    Native,
12    /// Swift/Apple platform backend.
13    Swift,
14    /// WebAssembly backend.
15    Wasm,
16    /// Kotlin/Android platform backend.
17    Kotlin,
18}
19
20impl core::fmt::Display for AeadBackend {
21    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
22        let name = match self {
23            AeadBackend::Native => "native",
24            AeadBackend::Swift => "swift",
25            AeadBackend::Wasm => "wasm",
26            AeadBackend::Kotlin => "kotlin",
27        };
28        write!(f, "{name}")
29    }
30}
31
32/// Specific reason an AEAD encrypt or decrypt operation failed.
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum AeadFailureKind {
35    /// The supplied key material was not valid for the cipher.
36    InvalidKeyMaterial,
37    /// An input length exceeded the representable range.
38    LengthOverflow,
39    /// The ciphertext was shorter than the authentication tag.
40    ShortCiphertext,
41    /// The backend returned an output of unexpected length.
42    InvalidOutputLength,
43    /// Tag verification failed (ciphertext or AAD tampered/wrong key).
44    AuthenticationFailed,
45    /// The backend reported an unspecified internal failure.
46    BackendFailure,
47}
48
49impl core::fmt::Display for AeadFailureKind {
50    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51        let detail = match self {
52            AeadFailureKind::InvalidKeyMaterial => "invalid key material",
53            AeadFailureKind::LengthOverflow => "length overflow",
54            AeadFailureKind::ShortCiphertext => "ciphertext shorter than tag",
55            AeadFailureKind::InvalidOutputLength => "backend returned invalid output length",
56            AeadFailureKind::AuthenticationFailed => "authentication failed",
57            AeadFailureKind::BackendFailure => "backend failure",
58        };
59        write!(f, "{detail}")
60    }
61}
62
63/// Backend implementation that performed a signature operation.
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum SignatureBackend {
66    /// Pure-Rust native backend.
67    Native,
68    /// Swift/Apple platform backend.
69    Swift,
70    /// WebAssembly backend.
71    Wasm,
72    /// Kotlin/Android platform backend.
73    Kotlin,
74    /// Apple Secure Enclave hardware-backed backend.
75    SecureEnclave,
76}
77
78impl core::fmt::Display for SignatureBackend {
79    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
80        let name = match self {
81            SignatureBackend::Native => "native",
82            SignatureBackend::Swift => "swift",
83            SignatureBackend::Wasm => "wasm",
84            SignatureBackend::Kotlin => "kotlin",
85            SignatureBackend::SecureEnclave => "secure_enclave",
86        };
87        write!(f, "{name}")
88    }
89}
90
91/// Signature-related operation being attempted when a failure occurred.
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub enum SignatureOperation {
94    /// Producing a signature over a message.
95    Sign,
96    /// Verifying a signature against a message.
97    Verify,
98    /// Key generation, import, or other key management.
99    KeyManagement,
100}
101
102impl core::fmt::Display for SignatureOperation {
103    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104        let op = match self {
105            SignatureOperation::Sign => "sign",
106            SignatureOperation::Verify => "verify",
107            SignatureOperation::KeyManagement => "key_management",
108        };
109        write!(f, "{op}")
110    }
111}
112
113/// Specific reason a signature operation failed.
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115pub enum SignatureFailureKind {
116    /// The backend reported an unspecified internal failure.
117    BackendFailure,
118    /// The supplied private key was malformed or invalid.
119    InvalidPrivateKey,
120    /// The supplied public key was malformed or invalid.
121    InvalidPublicKey,
122    /// The signature was malformed or failed verification.
123    InvalidSignature,
124    /// The message input was invalid for the operation.
125    InvalidMessage,
126    /// Key generation did not succeed.
127    KeyGenerationFailed,
128    /// The Secure Enclave was not available on this device.
129    SecureEnclaveUnavailable,
130    /// The Secure Enclave rejected the supplied key.
131    SecureEnclaveRejectedKey,
132}
133
134impl core::fmt::Display for SignatureFailureKind {
135    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
136        let detail = match self {
137            SignatureFailureKind::BackendFailure => "backend failure",
138            SignatureFailureKind::InvalidPrivateKey => "invalid private key",
139            SignatureFailureKind::InvalidPublicKey => "invalid public key",
140            SignatureFailureKind::InvalidSignature => "invalid signature",
141            SignatureFailureKind::InvalidMessage => "invalid message",
142            SignatureFailureKind::KeyGenerationFailed => "key generation failed",
143            SignatureFailureKind::SecureEnclaveUnavailable => "secure enclave unavailable",
144            SignatureFailureKind::SecureEnclaveRejectedKey => "secure enclave rejected key",
145        };
146        write!(f, "{detail}")
147    }
148}
149
150/// Specific reason a key agreement operation failed.
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
152pub enum KeyAgreementFailureKind {
153    /// Deriving the shared secret did not succeed.
154    DeriveSharedSecretFailed,
155    /// Key generation did not succeed.
156    KeyGenerationFailed,
157}
158
159impl core::fmt::Display for KeyAgreementFailureKind {
160    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
161        let detail = match self {
162            KeyAgreementFailureKind::DeriveSharedSecretFailed => "derive shared secret failed",
163            KeyAgreementFailureKind::KeyGenerationFailed => "key generation failed",
164        };
165        write!(f, "{detail}")
166    }
167}
168
169/// Specific reason a KEM (key encapsulation) operation failed.
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub enum KemFailureKind {
172    /// Key generation did not succeed.
173    KeyGenerationFailed,
174    /// Encapsulation did not succeed.
175    EncapsulateFailed,
176    /// Decapsulation did not succeed.
177    DecapsulateFailed,
178}
179
180impl core::fmt::Display for KemFailureKind {
181    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
182        let detail = match self {
183            KemFailureKind::KeyGenerationFailed => "key generation failed",
184            KemFailureKind::EncapsulateFailed => "encapsulate failed",
185            KemFailureKind::DecapsulateFailed => "decapsulate failed",
186        };
187        write!(f, "{detail}")
188    }
189}
190
191/// Key-wrapping algorithm identifier.
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
193pub enum KeyWrapAlgorithm {
194    /// AES-256 Key Wrap as specified by RFC 3394 / NIST SP 800-38F.
195    Aes256Kw,
196}
197
198impl core::fmt::Display for KeyWrapAlgorithm {
199    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
200        let detail = match self {
201            KeyWrapAlgorithm::Aes256Kw => "AES-256-KW",
202        };
203        write!(f, "{detail}")
204    }
205}
206
207/// Key-wrap operation being attempted when a failure occurred.
208#[derive(Debug, Clone, Copy, PartialEq, Eq)]
209pub enum KeyWrapOperation {
210    /// Wrapping plaintext key material.
211    Wrap,
212    /// Unwrapping wrapped key material.
213    Unwrap,
214}
215
216impl core::fmt::Display for KeyWrapOperation {
217    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
218        let op = match self {
219            KeyWrapOperation::Wrap => "wrap",
220            KeyWrapOperation::Unwrap => "unwrap",
221        };
222        write!(f, "{op}")
223    }
224}
225
226/// Specific reason a key-wrap operation failed.
227#[derive(Debug, Clone, Copy, PartialEq, Eq)]
228pub enum KeyWrapFailureKind {
229    /// The key-encryption key did not have the required length.
230    InvalidKekLength,
231    /// Plaintext key material was too short for RFC 3394 AES-KW.
232    InvalidPlaintextLength,
233    /// Wrapped key material was too short or malformed for RFC 3394 AES-KW.
234    InvalidWrappedLength,
235    /// An input or output length exceeded the representable range.
236    LengthOverflow,
237    /// The wrapped key integrity check failed.
238    IntegrityCheckFailed,
239    /// The backend reported an unspecified internal failure.
240    BackendFailure,
241}
242
243impl core::fmt::Display for KeyWrapFailureKind {
244    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
245        let detail = match self {
246            KeyWrapFailureKind::InvalidKekLength => "invalid key-encryption key length",
247            KeyWrapFailureKind::InvalidPlaintextLength => "invalid plaintext key length",
248            KeyWrapFailureKind::InvalidWrappedLength => "invalid wrapped key length",
249            KeyWrapFailureKind::LengthOverflow => "length overflow",
250            KeyWrapFailureKind::IntegrityCheckFailed => "integrity check failed",
251            KeyWrapFailureKind::BackendFailure => "backend failure",
252        };
253        write!(f, "{detail}")
254    }
255}
256
257/// Password-based key derivation algorithm identifier.
258#[derive(Debug, Clone, Copy, PartialEq, Eq)]
259pub enum KdfAlgorithm {
260    /// Argon2id memory-hard KDF.
261    Argon2id,
262    /// PBKDF2 password-based KDF for legacy interop.
263    Pbkdf2,
264}
265
266impl core::fmt::Display for KdfAlgorithm {
267    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
268        let detail = match self {
269            KdfAlgorithm::Argon2id => "Argon2id",
270            KdfAlgorithm::Pbkdf2 => "PBKDF2",
271        };
272        write!(f, "{detail}")
273    }
274}
275
276/// Versioned parameter profile for the KDF.
277#[derive(Debug, Clone, Copy, PartialEq, Eq)]
278pub enum KdfProfile {
279    /// Argon2id parameter profile version 1.
280    Argon2idV1,
281    /// Argon2id parameter profile version 2.
282    Argon2idV2,
283    /// PBKDF2 using HMAC-SHA-256 as the PRF.
284    Pbkdf2HmacSha256,
285    /// PBKDF2 using HMAC-SHA-512 as the PRF.
286    Pbkdf2HmacSha512,
287}
288
289impl core::fmt::Display for KdfProfile {
290    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
291        let detail = match self {
292            KdfProfile::Argon2idV1 => "Argon2id v1",
293            KdfProfile::Argon2idV2 => "Argon2id v2",
294            KdfProfile::Pbkdf2HmacSha256 => "PBKDF2-HMAC-SHA-256",
295            KdfProfile::Pbkdf2HmacSha512 => "PBKDF2-HMAC-SHA-512",
296        };
297        write!(f, "{detail}")
298    }
299}
300
301/// Specific reason a KDF operation failed.
302#[derive(Debug, Clone, Copy, PartialEq, Eq)]
303pub enum KdfFailureKind {
304    /// The secret/password input had an unacceptable length.
305    InvalidSecretLength,
306    /// The salt input had an unacceptable length.
307    InvalidSaltLength,
308    /// The requested output length was unacceptable.
309    InvalidOutputLength,
310    /// The iteration count was zero or outside this API's accepted range.
311    InvalidIterationCount,
312    /// The supplied KDF parameters were invalid.
313    InvalidParams,
314    /// The derivation itself did not succeed.
315    DerivationFailed,
316}
317
318impl core::fmt::Display for KdfFailureKind {
319    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
320        let detail = match self {
321            KdfFailureKind::InvalidSecretLength => "invalid secret length",
322            KdfFailureKind::InvalidSaltLength => "invalid salt length",
323            KdfFailureKind::InvalidOutputLength => "invalid output length",
324            KdfFailureKind::InvalidIterationCount => "invalid iteration count",
325            KdfFailureKind::InvalidParams => "invalid parameters",
326            KdfFailureKind::DerivationFailed => "derivation failed",
327        };
328        write!(f, "{detail}")
329    }
330}
331
332/// Hash function underlying an HKDF operation.
333#[derive(Debug, Clone, Copy, PartialEq, Eq)]
334pub enum HkdfHash {
335    /// HKDF using SHA-2-256.
336    Sha2_256,
337    /// HKDF using SHA-3-256.
338    Sha3_256,
339}
340
341impl core::fmt::Display for HkdfHash {
342    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
343        let detail = match self {
344            HkdfHash::Sha2_256 => "SHA2-256",
345            HkdfHash::Sha3_256 => "SHA3-256",
346        };
347        write!(f, "{detail}")
348    }
349}
350
351/// Specific reason an HKDF operation failed.
352#[derive(Debug, Clone, Copy, PartialEq, Eq)]
353pub enum HkdfFailureKind {
354    /// The input key material had an unacceptable length.
355    InvalidIkmLength,
356    /// The domain-separation tag had an unacceptable length.
357    InvalidDomainTagLength,
358    /// The domain-separation tag contained an invalid byte.
359    InvalidDomainTagByte,
360    /// An input length exceeded the representable range.
361    LengthOverflow,
362    /// The requested output length was unacceptable.
363    InvalidOutputLength,
364    /// The HKDF expand step did not succeed.
365    ExpandFailed,
366}
367
368impl core::fmt::Display for HkdfFailureKind {
369    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
370        let detail = match self {
371            HkdfFailureKind::InvalidIkmLength => "invalid input key material length",
372            HkdfFailureKind::InvalidDomainTagLength => "invalid domain tag length",
373            HkdfFailureKind::InvalidDomainTagByte => "invalid domain tag byte",
374            HkdfFailureKind::LengthOverflow => "length overflow",
375            HkdfFailureKind::InvalidOutputLength => "invalid output length",
376            HkdfFailureKind::ExpandFailed => "expand failed",
377        };
378        write!(f, "{detail}")
379    }
380}
381
382/// Hash function underlying an HMAC operation.
383#[derive(Debug, Clone, Copy, PartialEq, Eq)]
384pub enum MacHash {
385    /// HMAC using SHA-256.
386    Sha2_256,
387    /// HMAC using SHA-512.
388    Sha2_512,
389}
390
391impl core::fmt::Display for MacHash {
392    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
393        let detail = match self {
394            MacHash::Sha2_256 => "SHA-256",
395            MacHash::Sha2_512 => "SHA-512",
396        };
397        write!(f, "{detail}")
398    }
399}
400
401/// Specific reason an HMAC operation failed.
402#[derive(Debug, Clone, Copy, PartialEq, Eq)]
403pub enum MacFailureKind {
404    /// The supplied key was empty or exceeded the accepted size limit.
405    InvalidKeyLength,
406    /// The supplied authentication tag length did not match the algorithm.
407    InvalidTagLength,
408    /// Tag verification failed.
409    VerificationFailed,
410    /// The backend reported an unspecified internal failure.
411    BackendFailure,
412}
413
414impl core::fmt::Display for MacFailureKind {
415    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
416        let detail = match self {
417            MacFailureKind::InvalidKeyLength => "invalid key length",
418            MacFailureKind::InvalidTagLength => "invalid tag length",
419            MacFailureKind::VerificationFailed => "verification failed",
420            MacFailureKind::BackendFailure => "backend failure",
421        };
422        write!(f, "{detail}")
423    }
424}
425
426/// Purpose of the random output being generated.
427#[derive(Debug, Clone, Copy, PartialEq, Eq)]
428pub enum RngOutputKind {
429    /// Generic random bytes with no fixed length.
430    Generic,
431    /// A 12-byte AEAD nonce.
432    AeadNonce12,
433    /// A 16-byte Argon2 salt.
434    Argon2Salt16,
435    /// A 32-byte Argon2 salt.
436    Argon2Salt32,
437}
438
439impl core::fmt::Display for RngOutputKind {
440    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
441        let detail = match self {
442            RngOutputKind::Generic => "random bytes",
443            RngOutputKind::AeadNonce12 => "AEAD nonce",
444            RngOutputKind::Argon2Salt16 => "Argon2 16-byte salt",
445            RngOutputKind::Argon2Salt32 => "Argon2 32-byte salt",
446        };
447        write!(f, "{detail}")
448    }
449}
450
451/// Specific reason secure random generation failed.
452#[derive(Debug, Clone, Copy, PartialEq, Eq)]
453pub enum RngFailureKind {
454    /// The system entropy source was unavailable.
455    EntropyUnavailable,
456    /// The requested output length was unacceptable.
457    InvalidOutputLength,
458}
459
460impl core::fmt::Display for RngFailureKind {
461    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
462        let detail = match self {
463            RngFailureKind::EntropyUnavailable => "entropy unavailable",
464            RngFailureKind::InvalidOutputLength => "invalid output length",
465        };
466        write!(f, "{detail}")
467    }
468}
469
470/// Specific reason a constant-time comparison did not match.
471#[derive(Debug, Clone, Copy, PartialEq, Eq)]
472pub enum ConstantTimeFailureKind {
473    /// The two inputs had different lengths.
474    LengthMismatch,
475    /// The two inputs had equal length but unequal contents.
476    NotEqual,
477}
478
479impl core::fmt::Display for ConstantTimeFailureKind {
480    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
481        let detail = match self {
482            ConstantTimeFailureKind::LengthMismatch => "length mismatch",
483            ConstantTimeFailureKind::NotEqual => "not equal",
484        };
485        write!(f, "{detail}")
486    }
487}
488
489/// Typed error taxonomy for all crypto operations in the workspace.
490#[derive(Debug, Error, Clone, PartialEq, Eq)]
491pub enum CryptoError {
492    /// Supplied key material was malformed or otherwise invalid.
493    #[error("invalid key material")]
494    InvalidKey,
495
496    /// An AEAD key did not have the length the cipher requires.
497    #[error("invalid AEAD key length: expected {expected} bytes, got {actual} bytes")]
498    InvalidAeadKeyLength {
499        /// Key length in bytes the cipher requires.
500        expected: usize,
501        /// Key length in bytes that was supplied.
502        actual: usize,
503    },
504
505    /// An AEAD nonce did not have the length the cipher requires.
506    #[error("invalid AEAD nonce length: expected {expected} bytes, got {actual} bytes")]
507    InvalidAeadNonceLength {
508        /// Nonce length in bytes the cipher requires.
509        expected: usize,
510        /// Nonce length in bytes that was supplied.
511        actual: usize,
512    },
513
514    /// A ciphertext was shorter than the minimum (tag) length.
515    #[error("invalid ciphertext length: minimum {minimum} bytes, got {actual} bytes")]
516    InvalidCiphertextLength {
517        /// Minimum ciphertext length in bytes (the authentication tag length).
518        minimum: usize,
519        /// Ciphertext length in bytes that was supplied.
520        actual: usize,
521    },
522
523    /// AEAD encryption failed in the given backend for the given reason.
524    #[error("AEAD encryption failed in {backend} backend: {kind}")]
525    AeadEncrypt {
526        /// Backend lane in which the failure occurred.
527        backend: AeadBackend,
528        /// Specific encryption failure cause.
529        kind: AeadFailureKind,
530    },
531
532    /// AEAD decryption failed in the given backend for the given reason.
533    #[error("AEAD decryption failed in {backend} backend: {kind}")]
534    AeadDecrypt {
535        /// Backend lane in which the failure occurred.
536        backend: AeadBackend,
537        /// Specific decryption failure cause (includes authentication failure).
538        kind: AeadFailureKind,
539    },
540
541    /// A signature operation failed in the given backend for the given reason.
542    #[error("signature failed in {backend} backend during {operation}: {kind}")]
543    Signature {
544        /// Backend lane in which the failure occurred.
545        backend: SignatureBackend,
546        /// Operation (sign, verify, keygen, encode) that failed.
547        operation: SignatureOperation,
548        /// Specific signature failure cause.
549        kind: SignatureFailureKind,
550    },
551
552    /// A key agreement operation failed for the given reason.
553    #[error("key agreement failed: {kind}")]
554    KeyAgreementFailure {
555        /// Specific key-agreement failure cause.
556        kind: KeyAgreementFailureKind,
557    },
558
559    /// A KEM (key encapsulation) operation failed for the given reason.
560    #[error("KEM operation failed: {kind}")]
561    KemFailure {
562        /// Specific KEM failure cause.
563        kind: KemFailureKind,
564    },
565
566    /// A key-wrap operation failed for the given algorithm and reason.
567    #[error("key wrap failed for {algorithm} during {operation}: {kind}")]
568    KeyWrap {
569        /// Key-wrap algorithm that failed.
570        algorithm: KeyWrapAlgorithm,
571        /// Operation (wrap or unwrap) that failed.
572        operation: KeyWrapOperation,
573        /// Specific key-wrap failure cause.
574        kind: KeyWrapFailureKind,
575    },
576
577    /// A password-based KDF operation failed for the given algorithm/profile.
578    #[error("KDF failed for {algorithm}/{profile}: {kind}")]
579    Kdf {
580        /// KDF algorithm that failed.
581        algorithm: KdfAlgorithm,
582        /// Cost profile in effect at the time of failure.
583        profile: KdfProfile,
584        /// Specific KDF failure cause.
585        kind: KdfFailureKind,
586    },
587
588    /// An HKDF operation failed for the given hash and reason.
589    #[error("HKDF failed for {hash}: {kind}")]
590    Hkdf {
591        /// Hash suite underlying the HKDF operation.
592        hash: HkdfHash,
593        /// Specific HKDF failure cause.
594        kind: HkdfFailureKind,
595    },
596
597    /// An HMAC operation failed for the given hash and reason.
598    #[error("HMAC failed for {hash}: {kind}")]
599    Mac {
600        /// Hash suite underlying the HMAC operation.
601        hash: MacHash,
602        /// Specific HMAC failure cause.
603        kind: MacFailureKind,
604    },
605
606    /// Secure random generation failed for the given output purpose.
607    #[error("secure random generation failed for {output}: {kind}")]
608    Rng {
609        /// Purpose the requested random bytes were being generated for.
610        output: RngOutputKind,
611        /// Specific RNG failure cause.
612        kind: RngFailureKind,
613    },
614
615    /// A constant-time comparison did not match, with the two input lengths.
616    #[error("constant-time comparison failed: {kind}")]
617    ConstantTimeComparison {
618        /// Specific comparison failure cause.
619        kind: ConstantTimeFailureKind,
620        /// Length in bytes of the left-hand input.
621        left_len: usize,
622        /// Length in bytes of the right-hand input.
623        right_len: usize,
624    },
625
626    /// The requested operation is not supported.
627    #[error("unsupported operation")]
628    Unsupported,
629}