Skip to main content

lib_q_core/
error.rs

1//! Error handling for lib-Q
2//!
3//! This module defines the error types used throughout the library.
4
5use core::fmt;
6
7#[cfg(feature = "alloc")]
8extern crate alloc;
9#[cfg(feature = "alloc")]
10#[allow(unused_imports)]
11use alloc::{
12    string::{
13        String,
14        ToString,
15    },
16    vec::Vec,
17};
18
19/// Why hexadecimal decoding failed ([`Error::HexDecode`], [`crate::api::Utils::hex_to_bytes`]).
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub enum HexDecodeError {
23    /// After trimming, the string has an odd number of hexadecimal characters.
24    OddLength {
25        /// Number of UTF-8 bytes in the trimmed hex string (not decoded byte length).
26        char_count: usize,
27    },
28    /// A two-character slice is not a valid hexadecimal byte.
29    InvalidDigit {
30        /// UTF-8 byte index in the trimmed string where the invalid pair starts.
31        pair_start: usize,
32        /// Total UTF-8 byte length of the trimmed hex string.
33        char_count: usize,
34    },
35}
36
37impl fmt::Display for HexDecodeError {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        match self {
40            HexDecodeError::OddLength { char_count } => write!(
41                f,
42                "odd hex length ({char_count} characters); length must be even"
43            ),
44            HexDecodeError::InvalidDigit {
45                pair_start,
46                char_count,
47            } => write!(
48                f,
49                "invalid hex digit at index {pair_start} (trimmed length {char_count})"
50            ),
51        }
52    }
53}
54
55/// The error type for lib-Q operations
56///
57/// This enum represents all possible errors that can occur during cryptographic operations
58/// across all libQ libraries. Each variant includes context about when the error occurs.
59#[derive(Debug, Clone, PartialEq, Eq)]
60#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
61// #[cfg_attr(
62//     feature = "wasm",
63//     derive(wasm_bindgen::FromJsValue, wasm_bindgen::IntoJsValue)
64// )]
65pub enum Error {
66    /// Invalid key size
67    ///
68    /// **When it occurs:** A key (public or secret) has an incorrect size for the algorithm.
69    /// **Cause:** The key data provided doesn't match the expected size for the algorithm variant.
70    /// **Resolution:** Ensure the key size matches the algorithm's requirements. Check algorithm documentation for expected key sizes.
71    InvalidKeySize { expected: usize, actual: usize },
72
73    /// Invalid signature size
74    ///
75    /// **When it occurs:** A signature has an incorrect size for the algorithm.
76    /// **Cause:** The signature data doesn't match the expected size for verification.
77    /// **Resolution:** Ensure the signature was generated for the same algorithm variant and hasn't been corrupted.
78    InvalidSignatureSize { expected: usize, actual: usize },
79
80    /// Invalid nonce size
81    ///
82    /// **When it occurs:** A nonce has an incorrect size for the operation.
83    /// **Cause:** The nonce doesn't meet the size requirements for the cryptographic operation.
84    /// **Resolution:** Ensure the nonce size matches the algorithm's requirements (typically 12-16 bytes for AEAD).
85    InvalidNonceSize { expected: usize, actual: usize },
86
87    /// Invalid message size
88    ///
89    /// **When it occurs:** A caller-supplied message or payload exceeds a configured policy limit
90    /// (for example AEAD or hash absorb bounds from [`crate::security::SecurityValidator`]).
91    /// **Cause:** The input is too large for the operation under current security constants.
92    /// **Resolution:** Split the message into smaller chunks or use a different approach that supports larger messages.
93    ///
94    /// For fixed internal or stack buffers, use [`Error::BufferTooSmall`] instead.
95    InvalidMessageSize { max: usize, actual: usize },
96
97    /// Invalid ciphertext size
98    ///
99    /// **When it occurs:** A ciphertext has an incorrect size for decryption.
100    /// **Cause:** The ciphertext data doesn't match the expected size for the algorithm.
101    /// **Resolution:** Ensure the ciphertext was generated for the same algorithm and hasn't been corrupted.
102    InvalidCiphertextSize { expected: usize, actual: usize },
103
104    /// Invalid plaintext size
105    ///
106    /// **When it occurs:** A plaintext has an incorrect size for the operation.
107    /// **Cause:** The plaintext doesn't meet the size requirements for encryption.
108    /// **Resolution:** Verify the plaintext size matches the algorithm's requirements.
109    InvalidPlaintextSize { expected: usize, actual: usize },
110
111    /// Invalid associated data size
112    ///
113    /// **When it occurs:** Associated data exceeds the maximum allowed size.
114    /// **Cause:** The associated data is too large for the AEAD operation.
115    /// **Resolution:** Reduce the associated data size or use a different approach.
116    InvalidAssociatedDataSize { max: usize, actual: usize },
117
118    /// Invalid tag size
119    ///
120    /// **When it occurs:** An authentication tag has an incorrect size.
121    /// **Cause:** The tag doesn't match the expected size for the AEAD algorithm.
122    /// **Resolution:** Ensure the tag size matches the algorithm's requirements (typically 16 bytes).
123    InvalidTagSize { expected: usize, actual: usize },
124
125    /// Invalid hash size
126    ///
127    /// **When it occurs:** A hash output has an incorrect size.
128    /// **Cause:** The hash size doesn't match the expected output length for the hash function.
129    /// **Resolution:** Ensure the hash size matches the algorithm's output length (e.g., SHA-256 = 32 bytes).
130    InvalidHashSize { expected: usize, actual: usize },
131
132    /// Invalid algorithm
133    ///
134    /// **When it occurs:** An unsupported or invalid algorithm is specified.
135    /// **Cause:** The algorithm identifier doesn't match any supported algorithm, or the algorithm isn't available in the current configuration.
136    /// **Resolution:** Check that the algorithm is supported and that required features are enabled.
137    InvalidAlgorithm { algorithm: &'static str },
138
139    /// Invalid security level
140    #[cfg(feature = "alloc")]
141    InvalidSecurityLevel { level: u32, supported: Vec<u32> },
142    #[cfg(not(feature = "alloc"))]
143    InvalidSecurityLevel {
144        level: u32,
145        supported: &'static [u32],
146    },
147
148    /// Verification failed
149    ///
150    /// **When it occurs:** Signature or message authentication verification fails.
151    /// **Cause:** The signature is invalid, the message was tampered with, or the verification key is incorrect.
152    /// **Resolution:** Verify the signature, message, and key are correct and correspond to each other.
153    #[cfg(feature = "alloc")]
154    VerificationFailed { operation: String },
155    #[cfg(not(feature = "alloc"))]
156    VerificationFailed { operation: &'static str },
157
158    /// Encryption failed
159    ///
160    /// **When it occurs:** Encryption or encapsulation fails to produce a valid ciphertext.
161    /// **Cause:** Random number generation may have failed, or internal computation encountered an error.
162    /// **Resolution:** Ensure a secure random number generator is available and functioning correctly.
163    #[cfg(feature = "alloc")]
164    EncryptionFailed { operation: String },
165    #[cfg(not(feature = "alloc"))]
166    EncryptionFailed { operation: &'static str },
167
168    /// Decryption failed
169    ///
170    /// **When it occurs:** Decryption or decapsulation fails to recover the plaintext or shared secret.
171    /// **Cause:** The ciphertext may be corrupted, the key may be incorrect, or authentication failed.
172    /// **Resolution:** Verify the ciphertext and key are valid and correspond to each other.
173    #[cfg(feature = "alloc")]
174    DecryptionFailed { operation: String },
175    #[cfg(not(feature = "alloc"))]
176    DecryptionFailed { operation: &'static str },
177
178    /// Key generation failed
179    ///
180    /// **When it occurs:** Key pair generation fails.
181    /// **Cause:** Random number generation may have failed, or internal computation encountered an error.
182    /// **Resolution:** Ensure a secure random number generator is available and functioning correctly.
183    #[cfg(feature = "alloc")]
184    KeyGenerationFailed { operation: String },
185    #[cfg(not(feature = "alloc"))]
186    KeyGenerationFailed { operation: &'static str },
187
188    /// Random number generation failed
189    ///
190    /// **When it occurs:** The random number generator fails to produce random bytes.
191    /// **Cause:** The underlying RNG implementation encountered an error or is unavailable.
192    /// **Resolution:** Check RNG initialization and ensure a secure random source is available.
193    #[cfg(feature = "alloc")]
194    RandomGenerationFailed { operation: String },
195    #[cfg(not(feature = "alloc"))]
196    RandomGenerationFailed { operation: &'static str },
197
198    /// Signing failed
199    ///
200    /// **When it occurs:** Digital signature generation fails.
201    /// **Cause:** Random number generation may have failed, or internal computation encountered an error.
202    /// **Resolution:** Ensure a secure random number generator is available and functioning correctly.
203    #[cfg(feature = "alloc")]
204    SigningFailed { operation: String },
205    #[cfg(not(feature = "alloc"))]
206    SigningFailed { operation: &'static str },
207
208    /// Memory allocation failed
209    ///
210    /// **When it occurs:** Dynamic memory allocation fails during an operation.
211    /// **Cause:** Insufficient memory is available, or allocation is not supported in the current environment.
212    /// **Resolution:** Ensure sufficient memory is available, or use a no_std-compatible configuration.
213    #[cfg(feature = "alloc")]
214    MemoryAllocationFailed { operation: String },
215    #[cfg(not(feature = "alloc"))]
216    MemoryAllocationFailed { operation: &'static str },
217
218    /// Internal error
219    ///
220    /// **When it occurs:** An unexpected internal error occurs during cryptographic operations.
221    /// **Cause:** This typically indicates a bug in the implementation or corrupted internal state.
222    /// **Resolution:** Report this error as it may indicate a software bug. Check inputs and system state.
223    #[cfg(feature = "alloc")]
224    InternalError { operation: String, details: String },
225    #[cfg(not(feature = "alloc"))]
226    InternalError {
227        operation: &'static str,
228        details: &'static str,
229    },
230
231    /// Not implemented
232    ///
233    /// **When it occurs:** A requested feature or operation is not yet implemented.
234    /// **Cause:** The operation is not available in the current implementation.
235    /// **Resolution:** Check if an alternative approach is available, or wait for the feature to be implemented.
236    #[cfg(feature = "alloc")]
237    NotImplemented { feature: String },
238    #[cfg(not(feature = "alloc"))]
239    NotImplemented { feature: &'static str },
240
241    /// Unsupported operation
242    #[cfg(feature = "alloc")]
243    UnsupportedOperation { operation: String },
244    #[cfg(not(feature = "alloc"))]
245    UnsupportedOperation { operation: &'static str },
246
247    /// No `CryptoProvider` configured on the context (distinct from stub `NotImplemented`)
248    #[cfg(feature = "alloc")]
249    ProviderNotConfigured { operation: String },
250    #[cfg(not(feature = "alloc"))]
251    ProviderNotConfigured { operation: &'static str },
252
253    /// Invalid state
254    #[cfg(feature = "alloc")]
255    InvalidState { operation: String, reason: String },
256    #[cfg(not(feature = "alloc"))]
257    InvalidState {
258        operation: &'static str,
259        reason: &'static str,
260    },
261
262    /// Plugin dependency error
263    #[cfg(feature = "alloc")]
264    PluginDependencyError {
265        plugin: String,
266        dependency: String,
267        required_version: String,
268        available_version: Option<String>,
269    },
270    #[cfg(not(feature = "alloc"))]
271    PluginDependencyError {
272        plugin: &'static str,
273        dependency: &'static str,
274        required_version: &'static str,
275        available_version: Option<&'static str>,
276    },
277
278    /// Plugin version incompatibility
279    #[cfg(feature = "alloc")]
280    PluginVersionIncompatible {
281        plugin: String,
282        required_version: String,
283        available_version: String,
284    },
285    #[cfg(not(feature = "alloc"))]
286    PluginVersionIncompatible {
287        plugin: &'static str,
288        required_version: &'static str,
289        available_version: &'static str,
290    },
291
292    /// Invalid key format
293    InvalidKeyFormat,
294
295    /// Invalid key with specific reason
296    #[cfg(feature = "alloc")]
297    InvalidKey { key_type: String, reason: String },
298    #[cfg(not(feature = "alloc"))]
299    InvalidKey {
300        key_type: &'static str,
301        reason: &'static str,
302    },
303
304    /// Unsupported algorithm
305    #[cfg(feature = "alloc")]
306    UnsupportedAlgorithm { algorithm: String },
307    #[cfg(not(feature = "alloc"))]
308    UnsupportedAlgorithm { algorithm: &'static str },
309
310    /// Authentication failed
311    #[cfg(feature = "alloc")]
312    AuthenticationFailed { operation: String },
313    #[cfg(not(feature = "alloc"))]
314    AuthenticationFailed { operation: &'static str },
315
316    /// Invalid randomness size
317    InvalidRandomnessSize { expected: usize, actual: usize },
318
319    /// Requested output length for [`crate::api::Utils::random_bytes`] is outside the supported range.
320    ///
321    /// **When it occurs:** `length` is zero or exceeds the platform-specific maximum for this helper.
322    /// **Resolution:** Use a length in the inclusive range given by `min` and `max`.
323    RandomBytesLengthInvalid {
324        min: usize,
325        max: usize,
326        requested: usize,
327    },
328
329    /// Hexadecimal decoding failed ([`crate::api::Utils::hex_to_bytes`]).
330    HexDecode(HexDecodeError),
331
332    /// A fixed-capacity buffer (stack or internal) cannot satisfy the requested byte length.
333    ///
334    /// **When it occurs:** An implementation uses a bounded temporary buffer and the requested
335    /// length exceeds that capacity. This is distinct from [`Error::InvalidMessageSize`], which
336    /// reflects configured cryptographic policy limits on caller-supplied payloads.
337    BufferTooSmall { capacity: usize, requested: usize },
338}
339
340impl Error {
341    /// AEAD ciphertext is shorter than the minimum length required to hold an authentication tag.
342    ///
343    /// This is an **operational** input error (Layer A / pre-decrypt validation). It must not be
344    /// used for tag mismatch after the decrypt/verify schedule; that path uses
345    /// [`Error::VerificationFailed`] or [`DecryptSemanticOutcome::AuthenticationFailed`](crate::DecryptSemanticOutcome::AuthenticationFailed).
346    #[must_use]
347    pub const fn aead_ciphertext_shorter_than_tag(tag_len: usize, actual_len: usize) -> Self {
348        Self::InvalidCiphertextSize {
349            expected: tag_len,
350            actual: actual_len,
351        }
352    }
353}
354
355impl fmt::Display for Error {
356    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357        match self {
358            Error::InvalidKeySize { expected, actual } => {
359                write!(f, "Invalid key size: expected {expected}, got {actual}")
360            }
361            Error::InvalidSignatureSize { expected, actual } => {
362                write!(
363                    f,
364                    "Invalid signature size: expected {expected}, got {actual}"
365                )
366            }
367            Error::InvalidNonceSize { expected, actual } => {
368                write!(f, "Invalid nonce size: expected {expected}, got {actual}")
369            }
370            Error::InvalidMessageSize { max, actual } => {
371                write!(f, "Invalid message size: maximum {max}, got {actual}")
372            }
373            Error::InvalidCiphertextSize { expected, actual } => {
374                write!(
375                    f,
376                    "Invalid ciphertext size: expected {expected}, got {actual}"
377                )
378            }
379            Error::InvalidPlaintextSize { expected, actual } => {
380                write!(
381                    f,
382                    "Invalid plaintext size: expected {expected}, got {actual}"
383                )
384            }
385            Error::InvalidAssociatedDataSize { max, actual } => {
386                write!(
387                    f,
388                    "Invalid associated data size: maximum {max}, got {actual}"
389                )
390            }
391            Error::InvalidTagSize { expected, actual } => {
392                write!(f, "Invalid tag size: expected {expected}, got {actual}")
393            }
394            Error::InvalidHashSize { expected, actual } => {
395                write!(f, "Invalid hash size: expected {expected}, got {actual}")
396            }
397            Error::InvalidAlgorithm { algorithm } => {
398                write!(f, "Invalid algorithm: {algorithm}")
399            }
400            Error::InvalidSecurityLevel { level, supported } => {
401                write!(
402                    f,
403                    "Invalid security level: {level} (supported: {supported:?})"
404                )
405            }
406            Error::VerificationFailed { operation } => {
407                write!(f, "Verification failed: {operation}")
408            }
409            Error::EncryptionFailed { operation } => {
410                write!(f, "Encryption failed: {operation}")
411            }
412            Error::DecryptionFailed { operation } => {
413                write!(f, "Decryption failed: {operation}")
414            }
415            Error::KeyGenerationFailed { operation } => {
416                write!(f, "Key generation failed: {operation}")
417            }
418            Error::RandomGenerationFailed { operation } => {
419                write!(f, "Random generation failed: {operation}")
420            }
421            Error::SigningFailed { operation } => {
422                write!(f, "Signing failed: {operation}")
423            }
424            Error::MemoryAllocationFailed { operation } => {
425                write!(f, "Memory allocation failed: {operation}")
426            }
427            Error::InternalError { operation, details } => {
428                write!(f, "Internal error in {operation}: {details}")
429            }
430            Error::NotImplemented { feature } => {
431                write!(f, "Feature not implemented: {feature}")
432            }
433            Error::ProviderNotConfigured { operation } => {
434                write!(
435                    f,
436                    "Cryptographic provider not configured for {operation}; set a provider on the context"
437                )
438            }
439            Error::UnsupportedOperation { operation } => {
440                write!(f, "Unsupported operation: {operation}")
441            }
442            Error::InvalidState { operation, reason } => {
443                write!(f, "Invalid state in {operation}: {reason}")
444            }
445            #[cfg(feature = "alloc")]
446            Error::PluginDependencyError {
447                plugin,
448                dependency,
449                required_version,
450                available_version,
451            } => {
452                if let Some(available) = available_version {
453                    write!(
454                        f,
455                        "Plugin '{plugin}' requires dependency '{dependency}' version {required_version}, but version {available} is available"
456                    )
457                } else {
458                    write!(
459                        f,
460                        "Plugin '{plugin}' requires dependency '{dependency}' version {required_version}, but it is not available"
461                    )
462                }
463            }
464            #[cfg(not(feature = "alloc"))]
465            Error::PluginDependencyError {
466                plugin,
467                dependency,
468                required_version,
469                available_version,
470            } => {
471                if let Some(available) = available_version {
472                    write!(
473                        f,
474                        "Plugin '{}' requires dependency '{}' version {}, but version {} is available",
475                        plugin, dependency, required_version, available
476                    )
477                } else {
478                    write!(
479                        f,
480                        "Plugin '{}' requires dependency '{}' version {}, but it is not available",
481                        plugin, dependency, required_version
482                    )
483                }
484            }
485            #[cfg(feature = "alloc")]
486            Error::PluginVersionIncompatible {
487                plugin,
488                required_version,
489                available_version,
490            } => {
491                write!(
492                    f,
493                    "Plugin '{plugin}' version {available_version} is incompatible with required version {required_version}"
494                )
495            }
496            #[cfg(not(feature = "alloc"))]
497            Error::PluginVersionIncompatible {
498                plugin,
499                required_version,
500                available_version,
501            } => {
502                write!(
503                    f,
504                    "Plugin '{}' version {} is incompatible with required version {}",
505                    plugin, available_version, required_version
506                )
507            }
508            Error::InvalidKeyFormat => {
509                write!(f, "Invalid key format")
510            }
511            Error::InvalidKey { key_type, reason } => {
512                write!(f, "Invalid {key_type}: {reason}")
513            }
514            Error::UnsupportedAlgorithm { algorithm } => {
515                write!(f, "Unsupported algorithm: {algorithm}")
516            }
517            Error::AuthenticationFailed { operation } => {
518                write!(f, "Authentication failed: {operation}")
519            }
520            Error::InvalidRandomnessSize { expected, actual } => {
521                write!(
522                    f,
523                    "Invalid randomness size: expected {expected}, got {actual}"
524                )
525            }
526            Error::RandomBytesLengthInvalid {
527                min,
528                max,
529                requested,
530            } => {
531                write!(
532                    f,
533                    "Invalid random_bytes length: requested {requested}, allowed inclusive range [{min}, {max}]"
534                )
535            }
536            Error::HexDecode(e) => {
537                write!(f, "Hex decode failed: {e}")
538            }
539            Error::BufferTooSmall {
540                capacity,
541                requested,
542            } => {
543                write!(
544                    f,
545                    "Insufficient fixed buffer capacity: capacity {capacity}, requested {requested}"
546                )
547            }
548        }
549    }
550}
551
552#[cfg(feature = "std")]
553impl std::error::Error for Error {}
554
555/// WASM error conversion
556#[cfg(feature = "wasm")]
557impl From<Error> for wasm_bindgen::JsValue {
558    fn from(error: Error) -> Self {
559        use crate::wasm::error::error_to_js_value;
560        error_to_js_value(error)
561    }
562}
563
564/// Result type for lib-Q operations
565pub type Result<T> = core::result::Result<T, Error>;
566
567/// WASM-friendly error handling
568#[cfg(feature = "wasm")]
569impl Error {
570    /// Get error message for WASM
571    pub fn message(&self) -> String {
572        self.to_string()
573    }
574
575    /// Get error type name for WASM
576    pub fn error_type(&self) -> String {
577        match self {
578            Error::InvalidKeySize { .. } => "InvalidKeySize".to_string(),
579            Error::InvalidSignatureSize { .. } => "InvalidSignatureSize".to_string(),
580            Error::InvalidNonceSize { .. } => "InvalidNonceSize".to_string(),
581            Error::InvalidMessageSize { .. } => "InvalidMessageSize".to_string(),
582            Error::InvalidCiphertextSize { .. } => "InvalidCiphertextSize".to_string(),
583            Error::InvalidPlaintextSize { .. } => "InvalidPlaintextSize".to_string(),
584            Error::InvalidHashSize { .. } => "InvalidHashSize".to_string(),
585            Error::InvalidAlgorithm { .. } => "InvalidAlgorithm".to_string(),
586            Error::InvalidSecurityLevel { .. } => "InvalidSecurityLevel".to_string(),
587            Error::VerificationFailed { .. } => "VerificationFailed".to_string(),
588            Error::EncryptionFailed { .. } => "EncryptionFailed".to_string(),
589            Error::DecryptionFailed { .. } => "DecryptionFailed".to_string(),
590            Error::KeyGenerationFailed { .. } => "KeyGenerationFailed".to_string(),
591            Error::RandomGenerationFailed { .. } => "RandomGenerationFailed".to_string(),
592            Error::SigningFailed { .. } => "SigningFailed".to_string(),
593            Error::MemoryAllocationFailed { .. } => "MemoryAllocationFailed".to_string(),
594            Error::InternalError { .. } => "InternalError".to_string(),
595            Error::NotImplemented { .. } => "NotImplemented".to_string(),
596            Error::ProviderNotConfigured { .. } => "ProviderNotConfigured".to_string(),
597            Error::UnsupportedOperation { .. } => "UnsupportedOperation".to_string(),
598            Error::InvalidState { .. } => "InvalidState".to_string(),
599            Error::InvalidAssociatedDataSize { .. } => "InvalidAssociatedDataSize".to_string(),
600            Error::InvalidTagSize { .. } => "InvalidTagSize".to_string(),
601            Error::PluginDependencyError { .. } => "PluginDependencyError".to_string(),
602            Error::PluginVersionIncompatible { .. } => "PluginVersionIncompatible".to_string(),
603            Error::InvalidKeyFormat => "InvalidKeyFormat".to_string(),
604            Error::InvalidKey { .. } => "InvalidKey".to_string(),
605            Error::UnsupportedAlgorithm { .. } => "UnsupportedAlgorithm".to_string(),
606            Error::AuthenticationFailed { .. } => "AuthenticationFailed".to_string(),
607            Error::InvalidRandomnessSize { .. } => "InvalidRandomnessSize".to_string(),
608            Error::RandomBytesLengthInvalid { .. } => "RandomBytesLengthInvalid".to_string(),
609            Error::HexDecode(..) => "HexDecode".to_string(),
610            Error::BufferTooSmall { .. } => "BufferTooSmall".to_string(),
611        }
612    }
613}
614
615#[cfg(feature = "wasm")]
616impl From<wasm_bindgen::JsValue> for Error {
617    fn from(_js_value: wasm_bindgen::JsValue) -> Self {
618        // Convert JsValue to a generic internal error
619        // This is used when WASM code needs to convert JsValue errors back to Error
620        Error::InternalError {
621            operation: "WASM operation".to_string(),
622            details: "WASM error conversion".to_string(),
623        }
624    }
625}
626
627/// Security levels supported by lib-Q
628pub const SECURITY_LEVELS: &[u32] = &[1, 3, 4, 5];
629
630/// Get supported security levels as a Vec (for Error construction)
631#[cfg(feature = "alloc")]
632pub fn supported_security_levels() -> Vec<u32> {
633    SECURITY_LEVELS.to_vec()
634}
635
636#[cfg(not(feature = "alloc"))]
637pub fn supported_security_levels() -> &'static [u32] {
638    SECURITY_LEVELS
639}
640
641/// Check if a security level is supported
642pub fn is_supported_security_level(level: u32) -> bool {
643    SECURITY_LEVELS.contains(&level)
644}
645
646#[cfg(test)]
647mod tests {
648    use super::*;
649
650    #[test]
651    fn test_error_display() {
652        #[cfg(not(feature = "std"))]
653        use alloc::string::ToString;
654
655        let error = Error::InvalidKeySize {
656            expected: 32,
657            actual: 16,
658        };
659        assert_eq!(error.to_string(), "Invalid key size: expected 32, got 16");
660    }
661
662    #[test]
663    fn test_invalid_key_error_display() {
664        #[cfg(feature = "alloc")]
665        {
666            let error = Error::InvalidKey {
667                key_type: "public key".to_string(),
668                reason: "cannot be used for encapsulation".to_string(),
669            };
670            assert_eq!(
671                error.to_string(),
672                "Invalid public key: cannot be used for encapsulation"
673            );
674        }
675        #[cfg(not(feature = "alloc"))]
676        {
677            #[cfg(not(feature = "std"))]
678            use alloc::string::ToString;
679
680            let error = Error::InvalidKey {
681                key_type: "public key",
682                reason: "cannot be used for encapsulation",
683            };
684            assert_eq!(
685                error.to_string(),
686                "Invalid public key: cannot be used for encapsulation"
687            );
688        }
689    }
690
691    #[test]
692    fn test_security_levels() {
693        assert!(is_supported_security_level(1));
694        assert!(is_supported_security_level(3));
695        assert!(is_supported_security_level(4));
696        assert!(is_supported_security_level(5));
697        assert!(!is_supported_security_level(2));
698        assert!(!is_supported_security_level(6));
699    }
700}