confium_store_pkcs11/error.rs
1//! Error translation between `cryptoki`'s error type and the Store's
2//! [`Error`](confium_store::error::Error).
3//!
4//! The PKCS#11 backend surfaces failures through the Store's existing
5//! error variants rather than introducing a parallel PKCS#11-specific
6//! enum. The mapping is:
7//!
8//! | `cryptoki::error::Error` | Store variant |
9//! |-------------------------------------------|------------------------|
10//! | anything carrying a "value not found" | [`ValueNotFound`] |
11//! | signal (object handle lookup misses) | |
12//! | everything else | [`Wrapped`] |
13//!
14//! The `Wrapped` message carries the `cryptoki` Display string so the
15//! operator sees the underlying PKCS#11 return code.
16//!
17//! [`ValueNotFound`]: confium_store::error::Error::ValueNotFound
18//! [`Wrapped`]: confium_store::error::Error::Wrapped
19
20use confium_store::error::{Error, Result};
21
22/// Translate a `cryptoki` failure into a Store [`Error`].
23///
24/// This is a trait rather than a free function so that future PKCS#11
25/// sub-types (e.g. a session-pool wrapper) can override the mapping
26/// without rewriting call sites. Today there is a single blanket impl
27/// for `cryptoki::error::Error`.
28pub trait IntoStoreError {
29 /// Convert into a Store [`Result::Err`].
30 fn into_store_error(self, ctx: &str) -> Error;
31}
32
33impl IntoStoreError for cryptoki::error::Error {
34 /// Map a `cryptoki` error. The `ctx` string is prepended to the
35 /// message so the caller can record where in the open/operation
36 /// flow the failure occurred (e.g. "open session", "login").
37 fn into_store_error(self, ctx: &str) -> Error {
38 // PKCS#11 signals a missing object via `CKR_OBJECT_HANDLE_INVALID`
39 // (cryptoki surfaces it as `Error::Pkcs11(RvError::ObjectHandleInvalid, _)`).
40 // That is the canonical "value not found" signal from a
41 // `C_FindObjects` / `C_DestroyObject` miss, so we map it to the
42 // Store's `ValueNotFound`. Everything else is wrapped with the
43 // cryptoki Display string so the operator sees the underlying
44 // PKCS#11 return code.
45 match &self {
46 cryptoki::error::Error::Pkcs11(rv, _) => {
47 if matches!(rv, cryptoki::error::RvError::ObjectHandleInvalid) {
48 return Error::ValueNotFound;
49 }
50 Error::Wrapped {
51 message: format!("{ctx}: {self}"),
52 }
53 }
54 _ => Error::Wrapped {
55 message: format!("{ctx}: {self}"),
56 },
57 }
58 }
59}
60
61/// Convenience: map a `cryptoki` result into a Store result, tagging
62/// the failure with the supplied context string.
63pub(crate) fn map_cryptoki<T>(
64 r: std::result::Result<T, cryptoki::error::Error>,
65 ctx: &str,
66) -> Result<T> {
67 r.map_err(|e| e.into_store_error(ctx))
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use cryptoki::context::Function;
74 use cryptoki::error::{Error as CkError, RvError};
75
76 #[test]
77 fn non_found_error_becomes_wrapped_with_context() {
78 // Construct a representative error using a real cryptoki
79 // variant. `Pkcs11(RvError, Function)` is the shape every
80 // PKCS#11 return-code failure takes; pick a non-"not found"
81 // RvError so the wrapped branch is exercised.
82 let err = CkError::Pkcs11(RvError::GeneralError, Function::Login);
83 let mapped = err.into_store_error("login");
84 assert!(matches!(mapped, Error::Wrapped { .. }));
85 let msg = match mapped {
86 Error::Wrapped { message } => message,
87 _ => unreachable!(),
88 };
89 assert!(msg.starts_with("login"), "context is prepended: {msg}");
90 }
91
92 #[test]
93 fn object_handle_invalid_maps_to_value_not_found() {
94 // `CKR_OBJECT_HANDLE_INVALID` is the PKCS#11 signal for a
95 // missing object; the Store surfaces it as `ValueNotFound`.
96 let err = CkError::Pkcs11(RvError::ObjectHandleInvalid, Function::FindObjects);
97 let mapped = err.into_store_error("find object");
98 assert!(matches!(mapped, Error::ValueNotFound), "got {mapped:?}");
99 }
100
101 #[test]
102 fn map_cryptoki_ok_passthrough() {
103 let r: std::result::Result<u32, CkError> = Ok(42);
104 let mapped = map_cryptoki(r, "ctx").expect("ok passthrough");
105 assert_eq!(mapped, 42);
106 }
107}