Skip to main content

identity_kit/keys/
pkcs11.rs

1//! `Pkcs11KeyStore`: keys in a PKCS#11 token, an HSM or a software token such as SoftHSM2.
2
3use core::fmt;
4use std::error::Error;
5use std::path::{Path, PathBuf};
6use std::sync::atomic::{AtomicU64, Ordering};
7use std::sync::{Arc, Condvar, Mutex, MutexGuard, PoisonError};
8
9use cryptoki::context::{CInitializeArgs, CInitializeFlags, Pkcs11};
10use cryptoki::error::{Error as CryptokiError, RvError};
11use cryptoki::mechanism::eddsa::{EddsaParams, EddsaSignatureScheme};
12use cryptoki::mechanism::rsa::{PkcsMgfType, PkcsPssParams};
13use cryptoki::mechanism::{Mechanism, MechanismType};
14use cryptoki::object::{Attribute, AttributeType, KeyType as CkKeyType, ObjectClass, ObjectHandle};
15use cryptoki::session::{Session, UserType};
16use cryptoki::slot::Slot;
17use cryptoki::types::AuthPin;
18use sha2::{Digest as _, Sha256};
19
20use super::{KeyId, KeySpec, KeyStore, KeyStoreCapabilities, KeyStoreError};
21use crate::jose::{Jwk, JwsAlgorithm, KeyType, normalize_ecdsa_signature};
22
23/// DER `OBJECT IDENTIFIER` 1.2.840.10045.3.1.7 (`prime256v1`), the `CKA_EC_PARAMS` of a P-256 key.
24const P256_PARAMS: &[u8] = &[0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07];
25/// DER `OBJECT IDENTIFIER` 1.3.132.0.10 (`secp256k1`).
26const SECP256K1_PARAMS: &[u8] = &[0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x0a];
27/// DER `OBJECT IDENTIFIER` 1.3.101.112 (`id-Ed25519`, RFC 8410), the `CKA_EC_PARAMS` this store
28/// writes for an Ed25519 key.
29const ED25519_PARAMS: &[u8] = &[0x06, 0x03, 0x2b, 0x65, 0x70];
30/// DER `PrintableString "edwards25519"`, the other `CKA_EC_PARAMS` form PKCS #11 v3.1 §2.3.3
31/// allows for Ed25519 and the one `pkcs11-tool` writes.
32const ED25519_NAME_PARAMS: &[u8] = b"\x13\x0cedwards25519";
33/// The label a key pair carries between its generation and its thumbprint being known.
34pub const STAGING_LABEL_PREFIX: &str = "identity-kit-staging-";
35/// The RSA public exponent of generated keys, 65537.
36const RSA_PUBLIC_EXPONENT: &[u8] = &[0x01, 0x00, 0x01];
37/// The DER `DigestInfo` prefix for SHA-256 (RFC 8017 §9.2, note 1): `CKM_RSA_PKCS` takes the
38/// whole `DigestInfo`, so the store builds it around the hash it computes.
39const SHA256_DIGEST_INFO_PREFIX: &[u8] = &[
40    0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
41    0x00, 0x04, 0x20,
42];
43
44/// Which token of a PKCS#11 module a [`Pkcs11KeyStore`] uses.
45///
46/// `&str`, `String` and `u64` convert into it.
47///
48/// # Examples
49///
50/// ```
51/// use identity_kit::keys::Pkcs11Token;
52///
53/// assert_eq!(Pkcs11Token::from("issuer"), Pkcs11Token::Label("issuer".to_owned()));
54/// assert_eq!(Pkcs11Token::from(2_108_653_770), Pkcs11Token::Slot(2_108_653_770));
55/// ```
56#[derive(Debug, Clone, PartialEq, Eq, Hash)]
57#[non_exhaustive]
58pub enum Pkcs11Token {
59    /// The token whose label (`CK_TOKEN_INFO.label`, as set when the token was initialized)
60    /// is this string. Trailing blanks are ignored on both sides.
61    Label(String),
62    /// The token in the slot with this id. Slot ids are assigned by the module; SoftHSM2
63    /// assigns a new one whenever a token is initialized, so a label is usually the better
64    /// choice.
65    Slot(u64),
66}
67
68impl From<&str> for Pkcs11Token {
69    fn from(label: &str) -> Self {
70        Self::Label(label.to_owned())
71    }
72}
73
74impl From<String> for Pkcs11Token {
75    fn from(label: String) -> Self {
76        Self::Label(label)
77    }
78}
79
80impl From<u64> for Pkcs11Token {
81    fn from(slot: u64) -> Self {
82        Self::Slot(slot)
83    }
84}
85
86impl fmt::Display for Pkcs11Token {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        match self {
89            Self::Label(label) => write!(f, "token `{label}`"),
90            Self::Slot(slot) => write!(f, "slot {slot}"),
91        }
92    }
93}
94
95/// How a [`Pkcs11KeyStore`] uses its token.
96///
97/// # Examples
98///
99/// ```
100/// use identity_kit::jose::JwsAlgorithm;
101/// use identity_kit::keys::Pkcs11KeyStoreOptions;
102///
103/// let options = Pkcs11KeyStoreOptions::new()
104///     .sessions(2)
105///     .rsa_algorithm(JwsAlgorithm::PS256)
106///     .algorithms([JwsAlgorithm::ES256, JwsAlgorithm::PS256]);
107/// assert_eq!(options.sessions, 2);
108/// assert_eq!(options.rsa_algorithm, JwsAlgorithm::PS256);
109/// assert_eq!(
110///     options.algorithms,
111///     Some(vec![JwsAlgorithm::ES256, JwsAlgorithm::PS256])
112/// );
113/// ```
114#[derive(Debug, Clone, PartialEq, Eq)]
115#[non_exhaustive]
116pub struct Pkcs11KeyStoreOptions {
117    /// How many sessions the store keeps open at most, `4` by default.
118    ///
119    /// Sessions are opened as needed, logged in and reused; an operation waits for a session
120    /// when all of them are in use. At least `1`.
121    pub sessions: usize,
122    /// The algorithm RSA keys sign with, `RS256` by default: `RS256` (`CKM_RSA_PKCS`) or
123    /// `PS256` (`CKM_RSA_PKCS_PSS`).
124    ///
125    /// A PKCS#11 RSA key does not record which padding it is for, and a key signs with one
126    /// algorithm only, so the store settles it for all RSA keys of the token: the public JWK
127    /// of every RSA key carries this `alg`, `sign` accepts only this algorithm for an RSA key
128    /// and `generate` makes RSA keys for it.
129    pub rsa_algorithm: JwsAlgorithm,
130    /// The algorithms the store offers, out of those the module signs with; `None` for all of
131    /// them.
132    ///
133    /// The store derives what it can sign with from the module's mechanism list (see
134    /// [`Pkcs11KeyStore`]); this narrows that list, for a module whose `CKM_ECDSA` cannot take
135    /// secp256k1 for instance. Algorithms the module does not sign with are left out.
136    pub algorithms: Option<Vec<JwsAlgorithm>>,
137}
138
139impl Default for Pkcs11KeyStoreOptions {
140    fn default() -> Self {
141        Self {
142            sessions: 4,
143            rsa_algorithm: JwsAlgorithm::RS256,
144            algorithms: None,
145        }
146    }
147}
148
149impl Pkcs11KeyStoreOptions {
150    /// The defaults: four sessions, `RS256` for RSA keys, every algorithm the module signs with.
151    pub fn new() -> Self {
152        Self::default()
153    }
154
155    /// Sets [`sessions`](Self::sessions).
156    #[must_use]
157    pub fn sessions(mut self, sessions: usize) -> Self {
158        self.sessions = sessions;
159        self
160    }
161
162    /// Sets [`rsa_algorithm`](Self::rsa_algorithm).
163    #[must_use]
164    pub fn rsa_algorithm(mut self, alg: JwsAlgorithm) -> Self {
165        self.rsa_algorithm = alg;
166        self
167    }
168
169    /// Sets [`algorithms`](Self::algorithms).
170    #[must_use]
171    pub fn algorithms(mut self, algorithms: impl IntoIterator<Item = JwsAlgorithm>) -> Self {
172        self.algorithms = Some(algorithms.into_iter().collect());
173        self
174    }
175}
176
177/// What went wrong with the PKCS#11 module, carried as [`KeyStoreError::Backend`].
178#[derive(Debug, thiserror::Error)]
179#[non_exhaustive]
180pub enum Pkcs11Error {
181    /// The module could not be loaded or initialized.
182    #[error("PKCS#11 module `{}`: {source}", module.display())]
183    Module {
184        /// The module's path.
185        module: PathBuf,
186        /// What the module reported.
187        #[source]
188        source: CryptokiError,
189    },
190    /// No slot of the module holds the requested token.
191    #[error("PKCS#11 module has no {0}")]
192    TokenNotFound(Pkcs11Token),
193    /// An option is not usable.
194    #[error("PKCS#11 key store options: {0}")]
195    InvalidOptions(String),
196    /// A PKCS#11 function failed.
197    #[error("PKCS#11 {function} failed: {source}")]
198    Call {
199        /// The operation, as `C_...` names it.
200        function: &'static str,
201        /// What the module reported.
202        #[source]
203        source: CryptokiError,
204    },
205    /// The module returned a signature of another length than the algorithm's JWS form has.
206    #[error("PKCS#11 module returned a {actual}-byte signature for {alg}, expected {expected}")]
207    SignatureLength {
208        /// The algorithm.
209        alg: JwsAlgorithm,
210        /// The length the JWS form has.
211        expected: usize,
212        /// The length the module returned.
213        actual: usize,
214    },
215}
216
217impl Pkcs11Error {
218    /// Wraps the error of the PKCS#11 function `function` as [`KeyStoreError::Backend`].
219    fn call(function: &'static str) -> impl FnOnce(CryptokiError) -> KeyStoreError {
220        move |source| KeyStoreError::backend(Self::Call { function, source })
221    }
222
223    /// Whether the session the error came from is unusable from now on.
224    fn closes_session(&self) -> bool {
225        matches!(
226            self,
227            Self::Call {
228                source: CryptokiError::Pkcs11(
229                    RvError::SessionHandleInvalid
230                        | RvError::SessionClosed
231                        | RvError::DeviceRemoved
232                        | RvError::DeviceError
233                        | RvError::TokenNotPresent,
234                    _
235                ),
236                ..
237            }
238        )
239    }
240}
241
242/// A [`KeyStore`] over a token of a PKCS#11 module: an HSM, a smart card or a software token
243/// such as SoftHSM2.
244///
245/// The key id is the object label (`CKA_LABEL`): a key is the private key object and the
246/// public key object that share a label. Keys are referenced by label, so a label must name one
247/// key; the store refuses a label that names several.
248///
249/// Signing happens in the module. The store hashes in-process and signs the hash with
250/// `CKM_ECDSA` (ES256, ES256K), `CKM_RSA_PKCS` over a SHA-256 `DigestInfo` (RS256) or
251/// `CKM_RSA_PKCS_PSS` with SHA-256, MGF1-SHA-256 and a 32-byte salt (PS256); EdDSA signs the
252/// message itself with `CKM_EDDSA`. ECDSA signatures come back as raw `r || s`, the JWS form.
253///
254/// What the store signs with is read from the module's mechanism list when it is opened:
255/// `EdDSA` when the module has `CKM_EDDSA`, `ES256` and (feature `secp256k1`) `ES256K` when it
256/// has `CKM_ECDSA`, and [`rsa_algorithm`](Pkcs11KeyStoreOptions::rsa_algorithm) when it has the
257/// matching RSA mechanism. [`Pkcs11KeyStoreOptions::algorithms`] narrows the list. The store
258/// generates and deletes keys unless the token is write-protected; a generated key is an
259/// Ed25519, P-256, secp256k1 or RSA (65537) key pair stored on the token, the private key
260/// sensitive and not extractable. The key id of a generated key is the spec's label when set
261/// and the RFC 7638 thumbprint of the public key otherwise; such a key is generated under a
262/// label starting with [`STAGING_LABEL_PREFIX`] and relabelled once its thumbprint is known,
263/// so a generation interrupted in between leaves a key with that prefix on the token, to be
264/// removed with the module's tools.
265///
266/// The module's functions block, so every operation runs on `tokio`'s blocking thread pool;
267/// the store needs a `tokio` runtime. It keeps a pool of logged-in sessions
268/// ([`Pkcs11KeyStoreOptions::sessions`]) and an operation that finds all of them busy waits
269/// for one. A module is loaded and initialized when the first store on it is opened, shared
270/// by every later store on the same path, and never finalized or unloaded before the process
271/// ends. The PIN is kept in memory for logging in
272/// new sessions and zeroized when the store is dropped.
273///
274/// # Examples
275///
276/// ```no_run
277/// # tokio::runtime::Builder::new_current_thread().build()?.block_on(async {
278/// use identity_kit::jose::{JwsAlgorithm, KeyType};
279/// use identity_kit::keys::{KeySpec, KeyStore, Pkcs11KeyStore};
280///
281/// let store = Pkcs11KeyStore::open("/usr/lib/softhsm/libsofthsm2.so", "issuer", Some("1234"))?;
282/// let key = store
283///     .generate(KeySpec::new(KeyType::P256, JwsAlgorithm::ES256).with_label("issuer-2026"))
284///     .await?;
285/// let signature = store.sign(&key, JwsAlgorithm::ES256, b"payload").await?;
286/// assert_eq!(signature.len(), 64);
287/// # Ok::<(), Box<dyn std::error::Error>>(())
288/// # })?;
289/// # Ok::<(), Box<dyn std::error::Error>>(())
290/// ```
291#[derive(Clone)]
292pub struct Pkcs11KeyStore {
293    inner: Arc<Inner>,
294}
295
296/// The state behind the `Arc`, so that blocking tasks can own a reference to it.
297struct Inner {
298    module: PathBuf,
299    context: Pkcs11,
300    slot: Slot,
301    token_label: String,
302    pin: Option<AuthPin>,
303    options: Pkcs11KeyStoreOptions,
304    /// The algorithms the store signs with.
305    algorithms: Vec<JwsAlgorithm>,
306    /// The key pair generation mechanisms the module has.
307    key_pair_generation: Vec<MechanismType>,
308    write_protected: bool,
309    pool: Pool,
310    /// Held while this store generates or deletes a key; see [`TOKEN_WRITES`].
311    writes: Arc<Mutex<()>>,
312}
313
314/// The logged-in sessions.
315struct Pool {
316    state: Mutex<PoolState>,
317    /// Signalled when a session is returned or given up.
318    available: Condvar,
319    /// The most sessions open at once.
320    max: usize,
321}
322
323#[derive(Default)]
324struct PoolState {
325    idle: Vec<Session>,
326    /// Sessions open, idle or in use.
327    open: usize,
328}
329
330impl Pkcs11KeyStore {
331    /// Opens `token` of the PKCS#11 module at `module` with the default options.
332    ///
333    /// Loads the module, initializes it if needed, finds the token, reads its mechanism list and
334    /// opens one session, logging in as the user with `pin` when given. A token that needs no
335    /// login, or one with a protected authentication path, is opened with `None`. This blocks
336    /// while it talks to the module.
337    ///
338    /// PKCS#11 logs the application in, not the session: when the process is logged in to the
339    /// token already (through another store, say), the module reports that instead of checking
340    /// `pin`, and a wrong PIN goes unnoticed until the process logs out.
341    ///
342    /// # Errors
343    ///
344    /// [`KeyStoreError::Backend`] carrying a [`Pkcs11Error`]: the module does not load, the token
345    /// is not there, the PIN is wrong, or the options are not usable.
346    pub fn open(
347        module: impl AsRef<Path>,
348        token: impl Into<Pkcs11Token>,
349        pin: Option<&str>,
350    ) -> Result<Self, KeyStoreError> {
351        Self::open_with(module, token, pin, Pkcs11KeyStoreOptions::default())
352    }
353
354    /// Opens `token` of the PKCS#11 module at `module` as `options` say.
355    ///
356    /// # Errors
357    ///
358    /// As [`Pkcs11KeyStore::open`].
359    pub fn open_with(
360        module: impl AsRef<Path>,
361        token: impl Into<Pkcs11Token>,
362        pin: Option<&str>,
363        options: Pkcs11KeyStoreOptions,
364    ) -> Result<Self, KeyStoreError> {
365        let module = module.as_ref().to_path_buf();
366        let token = token.into();
367        if options.sessions == 0 {
368            return Err(invalid_options("`sessions` must be at least 1"));
369        }
370        if !matches!(
371            options.rsa_algorithm,
372            JwsAlgorithm::RS256 | JwsAlgorithm::PS256
373        ) {
374            return Err(invalid_options(format!(
375                "`rsa_algorithm` must be RS256 or PS256, not {}",
376                options.rsa_algorithm
377            )));
378        }
379
380        let context = load_module(&module)?;
381
382        let (slot, token_label, write_protected) = find_token(&context, &token)?;
383        let mechanisms = context
384            .get_mechanism_list(slot)
385            .map_err(Pkcs11Error::call("C_GetMechanismList"))?;
386        let mut algorithms = Vec::new();
387        if mechanisms.contains(&MechanismType::EDDSA) {
388            algorithms.push(JwsAlgorithm::EdDSA);
389        }
390        if mechanisms.contains(&MechanismType::ECDSA) {
391            algorithms.push(JwsAlgorithm::ES256);
392            #[cfg(feature = "secp256k1")]
393            algorithms.push(JwsAlgorithm::ES256K);
394        }
395        let rsa_mechanism = match options.rsa_algorithm {
396            JwsAlgorithm::PS256 => MechanismType::RSA_PKCS_PSS,
397            _ => MechanismType::RSA_PKCS,
398        };
399        if mechanisms.contains(&rsa_mechanism) {
400            algorithms.push(options.rsa_algorithm);
401        }
402        if let Some(wanted) = &options.algorithms {
403            algorithms.retain(|alg| wanted.contains(alg));
404        }
405        let key_pair_generation = [
406            MechanismType::ECC_EDWARDS_KEY_PAIR_GEN,
407            MechanismType::ECC_KEY_PAIR_GEN,
408            MechanismType::RSA_PKCS_KEY_PAIR_GEN,
409        ]
410        .into_iter()
411        .filter(|mechanism| mechanisms.contains(mechanism))
412        .collect();
413
414        let writes = token_writes(&module, slot.id());
415        let inner = Inner {
416            writes,
417            module,
418            context,
419            slot,
420            token_label,
421            pin: pin.map(|pin| AuthPin::from(pin.to_owned())),
422            pool: Pool {
423                state: Mutex::new(PoolState::default()),
424                available: Condvar::new(),
425                max: options.sessions,
426            },
427            options,
428            algorithms,
429            key_pair_generation,
430            write_protected,
431        };
432        // Open and log in one session now, so that a wrong PIN fails here and not on first use.
433        let session = inner.open_session()?;
434        inner.pool.release(session);
435        Ok(Self {
436            inner: Arc::new(inner),
437        })
438    }
439
440    /// The module's path.
441    pub fn module(&self) -> &Path {
442        &self.inner.module
443    }
444
445    /// The id of the slot the token is in.
446    pub fn slot(&self) -> u64 {
447        self.inner.slot.id()
448    }
449
450    /// The token's label.
451    pub fn token_label(&self) -> &str {
452        &self.inner.token_label
453    }
454
455    /// The options the store was opened with.
456    pub fn options(&self) -> &Pkcs11KeyStoreOptions {
457        &self.inner.options
458    }
459
460    /// Runs `f` on the blocking pool with a reference to the store's state.
461    async fn blocking<T, F>(&self, f: F) -> Result<T, KeyStoreError>
462    where
463        T: Send + 'static,
464        F: FnOnce(&Inner) -> Result<T, KeyStoreError> + Send + 'static,
465    {
466        let inner = Arc::clone(&self.inner);
467        match tokio::task::spawn_blocking(move || f(&inner)).await {
468            Ok(result) => result,
469            Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
470            Err(e) => Err(KeyStoreError::backend(e)),
471        }
472    }
473}
474
475impl fmt::Debug for Pkcs11KeyStore {
476    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477        f.debug_struct("Pkcs11KeyStore")
478            .field("module", &self.inner.module)
479            .field("slot", &self.inner.slot.id())
480            .field("token_label", &self.inner.token_label)
481            .field("algorithms", &self.inner.algorithms)
482            .field("options", &self.inner.options)
483            .finish_non_exhaustive()
484    }
485}
486
487#[async_trait::async_trait]
488impl KeyStore for Pkcs11KeyStore {
489    fn capabilities(&self) -> KeyStoreCapabilities {
490        let capabilities =
491            KeyStoreCapabilities::new(self.inner.algorithms.iter().copied()).remote();
492        if self.inner.write_protected {
493            capabilities
494        } else {
495            capabilities.with_generate().with_delete()
496        }
497    }
498
499    async fn public_jwk(&self, key: &KeyId) -> Result<Jwk, KeyStoreError> {
500        let key = key.clone();
501        self.blocking(move |inner| inner.with_session(|session| inner.public_jwk(session, &key)))
502            .await
503    }
504
505    async fn sign(
506        &self,
507        key: &KeyId,
508        alg: JwsAlgorithm,
509        data: &[u8],
510    ) -> Result<Vec<u8>, KeyStoreError> {
511        let key = key.clone();
512        let data = data.to_vec();
513        self.blocking(move |inner| {
514            inner.with_session(|session| inner.sign(session, &key, alg, &data))
515        })
516        .await
517    }
518
519    async fn exists(&self, key: &KeyId) -> Result<bool, KeyStoreError> {
520        let key = key.clone();
521        self.blocking(move |inner| {
522            inner.with_session(|session| {
523                Ok(find_key(session, ObjectClass::PRIVATE_KEY, &key)?.is_some())
524            })
525        })
526        .await
527    }
528
529    async fn generate(&self, spec: KeySpec) -> Result<KeyId, KeyStoreError> {
530        if self.inner.write_protected {
531            return Err(KeyStoreError::Unsupported("generate"));
532        }
533        self.blocking(move |inner| {
534            let _writing = inner.writes.lock().unwrap_or_else(PoisonError::into_inner);
535            inner.with_session(|session| inner.generate(session, &spec))
536        })
537        .await
538    }
539
540    async fn delete(&self, key: &KeyId) -> Result<(), KeyStoreError> {
541        if self.inner.write_protected {
542            return Err(KeyStoreError::Unsupported("delete"));
543        }
544        let key = key.clone();
545        self.blocking(move |inner| {
546            let _writing = inner.writes.lock().unwrap_or_else(PoisonError::into_inner);
547            inner.with_session(|session| delete(session, &key))
548        })
549        .await
550    }
551}
552
553impl Inner {
554    /// Opens a session on the token and logs in when the store has a PIN.
555    ///
556    /// A write-protected token refuses read/write sessions (`CKR_TOKEN_WRITE_PROTECTED`), and
557    /// signing needs none, so such a token gets read-only sessions.
558    fn open_session(&self) -> Result<Session, KeyStoreError> {
559        let session = if self.write_protected {
560            self.context.open_ro_session(self.slot)
561        } else {
562            self.context.open_rw_session(self.slot)
563        }
564        .map_err(Pkcs11Error::call("C_OpenSession"))?;
565        if let Some(pin) = &self.pin {
566            match session.login(UserType::User, Some(pin)) {
567                // The login state is the application's, not the session's: once one session
568                // of this process is logged in, so are the others.
569                Ok(()) | Err(CryptokiError::Pkcs11(RvError::UserAlreadyLoggedIn, _)) => {}
570                Err(source) => return Err(Pkcs11Error::call("C_Login")(source)),
571            }
572        }
573        Ok(session)
574    }
575
576    /// Runs `f` with a session from the pool, opening one if none is idle and fewer than the
577    /// maximum are open, and waiting otherwise. A session an error made unusable is dropped
578    /// instead of returned.
579    fn with_session<T>(
580        &self,
581        f: impl FnOnce(&Session) -> Result<T, KeyStoreError>,
582    ) -> Result<T, KeyStoreError> {
583        let session = self.pool.acquire(|| self.open_session())?;
584        let result = f(&session);
585        let dead = matches!(
586            &result,
587            Err(KeyStoreError::Backend(e))
588                if e.downcast_ref::<Pkcs11Error>().is_some_and(Pkcs11Error::closes_session)
589        );
590        if dead {
591            self.pool.discard(session);
592        } else {
593            self.pool.release(session);
594        }
595        result
596    }
597
598    /// The algorithm a key of `kind` signs with.
599    fn algorithm_of(&self, kind: KeyKind) -> JwsAlgorithm {
600        match kind {
601            KeyKind::Ed25519 => JwsAlgorithm::EdDSA,
602            KeyKind::P256 => JwsAlgorithm::ES256,
603            KeyKind::Secp256k1 => JwsAlgorithm::ES256K,
604            KeyKind::Rsa => self.options.rsa_algorithm,
605        }
606    }
607
608    /// [`KeyStore::public_jwk`] on `session`.
609    fn public_jwk(&self, session: &Session, key: &KeyId) -> Result<Jwk, KeyStoreError> {
610        let Some(public) = find_key(session, ObjectClass::PUBLIC_KEY, key)? else {
611            return Err(
612                if find_key(session, ObjectClass::PRIVATE_KEY, key)?.is_some() {
613                    KeyStoreError::invalid_key(
614                        key.clone(),
615                        "the token has no public key object with this label",
616                    )
617                } else {
618                    KeyStoreError::NotFound(key.clone())
619                },
620            );
621        };
622        self.public_jwk_of(session, key, public)
623    }
624
625    /// The public JWK of the public key object `public`, with `kid` and `alg` set.
626    fn public_jwk_of(
627        &self,
628        session: &Session,
629        key: &KeyId,
630        public: ObjectHandle,
631    ) -> Result<Jwk, KeyStoreError> {
632        let kind = key_kind(session, key, public)?;
633        let invalid = |reason: &str| KeyStoreError::invalid_key(key.clone(), reason);
634        let not_a_jwk = |error: crate::jose::JwkError| KeyStoreError::InvalidKey {
635            key: key.clone(),
636            reason: "the public key does not convert to a JWK".to_owned(),
637            source: Some(Box::new(error)),
638        };
639        let jwk = match kind {
640            KeyKind::Rsa => {
641                let attributes = session
642                    .get_attributes(
643                        public,
644                        &[AttributeType::Modulus, AttributeType::PublicExponent],
645                    )
646                    .map_err(Pkcs11Error::call("C_GetAttributeValue"))?;
647                let mut modulus = None;
648                let mut exponent = None;
649                for attribute in attributes {
650                    match attribute {
651                        Attribute::Modulus(n) => modulus = Some(n),
652                        Attribute::PublicExponent(e) => exponent = Some(e),
653                        _ => {}
654                    }
655                }
656                let (Some(n), Some(e)) = (modulus, exponent) else {
657                    return Err(invalid(
658                        "the public key object has no modulus or public exponent",
659                    ));
660                };
661                Jwk::from_rsa_public(&n, &e).map_err(not_a_jwk)?
662            }
663            KeyKind::Ed25519 | KeyKind::P256 | KeyKind::Secp256k1 => {
664                let point = ec_point(session, key, public, kind)?;
665                match kind {
666                    KeyKind::Ed25519 => Jwk::from_ed25519_public(&point).map_err(not_a_jwk)?,
667                    KeyKind::P256 => Jwk::from_p256_public(&point).map_err(not_a_jwk)?,
668                    #[cfg(feature = "secp256k1")]
669                    KeyKind::Secp256k1 => Jwk::from_secp256k1_public(&point).map_err(not_a_jwk)?,
670                    #[cfg(not(feature = "secp256k1"))]
671                    KeyKind::Secp256k1 => {
672                        return Err(invalid("secp256k1 keys need the `secp256k1` feature"));
673                    }
674                    KeyKind::Rsa => unreachable!("matched above"),
675                }
676            }
677        };
678        Ok(jwk
679            .with_kid(key.as_str())
680            .with_alg(self.algorithm_of(kind).as_str()))
681    }
682
683    /// [`KeyStore::sign`] on `session`.
684    fn sign(
685        &self,
686        session: &Session,
687        key: &KeyId,
688        alg: JwsAlgorithm,
689        data: &[u8],
690    ) -> Result<Vec<u8>, KeyStoreError> {
691        let private = find_key(session, ObjectClass::PRIVATE_KEY, key)?
692            .ok_or_else(|| KeyStoreError::NotFound(key.clone()))?;
693        let kind = key_kind(session, key, private)?;
694        let key_alg = self.algorithm_of(kind);
695        if alg != key_alg {
696            return Err(KeyStoreError::AlgorithmMismatch {
697                key: key.clone(),
698                key_alg,
699                requested: alg,
700            });
701        }
702        if !self.algorithms.contains(&alg) {
703            return Err(KeyStoreError::UnsupportedAlgorithm(alg));
704        }
705        let signature = match kind {
706            KeyKind::Ed25519 => session
707                .sign(
708                    &Mechanism::Eddsa(EddsaParams::new(EddsaSignatureScheme::Pure)),
709                    private,
710                    data,
711                )
712                .map_err(Pkcs11Error::call("C_Sign"))?,
713            KeyKind::P256 => session
714                .sign(&Mechanism::Ecdsa, private, &Sha256::digest(data))
715                .map_err(Pkcs11Error::call("C_Sign"))?,
716            KeyKind::Secp256k1 => {
717                let signature = session
718                    .sign(&Mechanism::Ecdsa, private, &Sha256::digest(data))
719                    .map_err(Pkcs11Error::call("C_Sign"))?;
720                // A module may return either of the two equivalent `s` values; `k256`, like
721                // Bitcoin, accepts only the lower one. A signature that cannot be normalized is
722                // returned as is, for the length check that follows.
723                normalize_ecdsa_signature(alg, &signature).unwrap_or(signature)
724            }
725            KeyKind::Rsa if alg == JwsAlgorithm::PS256 => {
726                let params = PkcsPssParams {
727                    hash_alg: MechanismType::SHA256,
728                    mgf: PkcsMgfType::MGF1_SHA256,
729                    s_len: 32.into(),
730                };
731                session
732                    .sign(
733                        &Mechanism::RsaPkcsPss(params),
734                        private,
735                        &Sha256::digest(data),
736                    )
737                    .map_err(Pkcs11Error::call("C_Sign"))?
738            }
739            KeyKind::Rsa => {
740                let mut digest_info = SHA256_DIGEST_INFO_PREFIX.to_vec();
741                digest_info.extend_from_slice(&Sha256::digest(data));
742                session
743                    .sign(&Mechanism::RsaPkcs, private, &digest_info)
744                    .map_err(Pkcs11Error::call("C_Sign"))?
745            }
746        };
747        if let Some(expected) = match kind {
748            KeyKind::Ed25519 | KeyKind::P256 | KeyKind::Secp256k1 => Some(64),
749            KeyKind::Rsa => None,
750        } && signature.len() != expected
751        {
752            return Err(KeyStoreError::backend(Pkcs11Error::SignatureLength {
753                alg,
754                expected,
755                actual: signature.len(),
756            }));
757        }
758        Ok(signature)
759    }
760
761    /// [`KeyStore::generate`] on `session`.
762    #[expect(
763        clippy::too_many_lines,
764        reason = "one template per key type, kept side by side to compare"
765    )]
766    fn generate(&self, session: &Session, spec: &KeySpec) -> Result<KeyId, KeyStoreError> {
767        spec.validate()?;
768        if !self.algorithms.contains(&spec.alg) {
769            return Err(KeyStoreError::UnsupportedAlgorithm(spec.alg));
770        }
771        let (mechanism, generation, mut public_template) = match spec.key_type {
772            KeyType::Ed25519 => (
773                Mechanism::EccEdwardsKeyPairGen,
774                MechanismType::ECC_EDWARDS_KEY_PAIR_GEN,
775                vec![
776                    Attribute::KeyType(CkKeyType::EC_EDWARDS),
777                    Attribute::EcParams(ED25519_PARAMS.to_vec()),
778                ],
779            ),
780            KeyType::P256 => (
781                Mechanism::EccKeyPairGen,
782                MechanismType::ECC_KEY_PAIR_GEN,
783                vec![
784                    Attribute::KeyType(CkKeyType::EC),
785                    Attribute::EcParams(P256_PARAMS.to_vec()),
786                ],
787            ),
788            KeyType::Secp256k1 => (
789                Mechanism::EccKeyPairGen,
790                MechanismType::ECC_KEY_PAIR_GEN,
791                vec![
792                    Attribute::KeyType(CkKeyType::EC),
793                    Attribute::EcParams(SECP256K1_PARAMS.to_vec()),
794                ],
795            ),
796            KeyType::Rsa2048 | KeyType::Rsa4096 => {
797                let bits: cryptoki::types::Ulong = match spec.key_type {
798                    KeyType::Rsa4096 => 4096.into(),
799                    _ => 2048.into(),
800                };
801                (
802                    Mechanism::RsaPkcsKeyPairGen,
803                    MechanismType::RSA_PKCS_KEY_PAIR_GEN,
804                    vec![
805                        Attribute::KeyType(CkKeyType::RSA),
806                        Attribute::ModulusBits(bits),
807                        Attribute::PublicExponent(RSA_PUBLIC_EXPONENT.to_vec()),
808                    ],
809                )
810            }
811        };
812        if !self.key_pair_generation.contains(&generation) {
813            return Err(KeyStoreError::UnsupportedKeyType(spec.key_type));
814        }
815        public_template.extend([
816            Attribute::Token(true),
817            Attribute::Private(false),
818            Attribute::Verify(true),
819        ]);
820        let mut private_template = vec![
821            Attribute::Token(true),
822            Attribute::Private(true),
823            Attribute::Sensitive(true),
824            Attribute::Extractable(false),
825            Attribute::Sign(true),
826        ];
827        if let Some(label) = &spec.label {
828            let id = KeyId::new(label.as_str());
829            if find_key(session, ObjectClass::PRIVATE_KEY, &id)?.is_some()
830                || find_key(session, ObjectClass::PUBLIC_KEY, &id)?.is_some()
831            {
832                return Err(KeyStoreError::AlreadyExists(id));
833            }
834            public_template.extend(label_attributes(label));
835            private_template.extend(label_attributes(label));
836        } else {
837            // The key id will be the thumbprint, known only once the key exists; until then
838            // the objects carry a staging label, so that a key left behind by an interrupted
839            // generation can be recognized and removed.
840            let staging = staging_label();
841            public_template.extend(label_attributes(&staging));
842            private_template.extend(label_attributes(&staging));
843        }
844
845        let (public, private) = session
846            .generate_key_pair(&mechanism, &public_template, &private_template)
847            .map_err(Pkcs11Error::call("C_GenerateKeyPair"))?;
848        if let Some(label) = &spec.label {
849            return Ok(KeyId::new(label.as_str()));
850        }
851        // No label: the key id is the thumbprint of the public key, so label both objects with
852        // it now that the key exists. A key that cannot be labelled is not kept.
853        let labelled = self
854            .public_jwk_of(session, &KeyId::new(""), public)
855            .and_then(|jwk| {
856                jwk.thumbprint_sha256()
857                    .map_err(|e| KeyStoreError::InvalidKey {
858                        key: KeyId::new(""),
859                        reason: "no RFC 7638 thumbprint".to_owned(),
860                        source: Some(Box::new(e)),
861                    })
862            })
863            .and_then(|thumbprint| {
864                let attributes = label_attributes(&thumbprint);
865                session
866                    .update_attributes(public, &attributes)
867                    .and_then(|()| session.update_attributes(private, &attributes))
868                    .map_err(Pkcs11Error::call("C_SetAttributeValue"))?;
869                Ok(KeyId::new(thumbprint))
870            });
871        if labelled.is_err() {
872            // Best effort: the error being returned is the one that matters.
873            let _ = session.destroy_object(private);
874            let _ = session.destroy_object(public);
875        }
876        labelled
877    }
878}
879
880impl Pool {
881    /// A session from the pool: an idle one, else a new one from `open` while fewer than the
882    /// maximum are open, else the next one returned.
883    fn acquire(
884        &self,
885        open: impl FnOnce() -> Result<Session, KeyStoreError>,
886    ) -> Result<Session, KeyStoreError> {
887        let mut state = self.lock();
888        loop {
889            if let Some(session) = state.idle.pop() {
890                return Ok(session);
891            }
892            if state.open < self.max {
893                state.open += 1;
894                drop(state);
895                return open().inspect_err(|_| {
896                    self.lock().open -= 1;
897                    self.available.notify_one();
898                });
899            }
900            state = self
901                .available
902                .wait(state)
903                .unwrap_or_else(PoisonError::into_inner);
904        }
905    }
906
907    /// Returns a usable session to the pool.
908    fn release(&self, session: Session) {
909        self.lock().idle.push(session);
910        self.available.notify_one();
911    }
912
913    /// Drops an unusable session, making room for a new one.
914    fn discard(&self, session: Session) {
915        drop(session);
916        self.lock().open -= 1;
917        self.available.notify_one();
918    }
919
920    /// The pool's state, poison or not.
921    fn lock(&self) -> MutexGuard<'_, PoolState> {
922        self.state.lock().unwrap_or_else(PoisonError::into_inner)
923    }
924}
925
926/// The modules loaded so far, by canonical path, each initialized once.
927///
928/// A PKCS#11 library must be initialized once per process, and unloading one that is still
929/// initialized is unsafe, so a loaded module stays loaded and initialized until the process
930/// ends; a store on a module loaded already shares the context.
931static MODULES: Mutex<Vec<(PathBuf, Pkcs11)>> = Mutex::new(Vec::new());
932
933/// One lock per token of the process, held while a key is generated or deleted.
934///
935/// Generation and deletion are provisioning, not the signing path, and serializing them
936/// process-wide costs nothing there; SoftHSM2's file object store fails
937/// (`CKR_GENERAL_ERROR`) when objects are created and destroyed from several sessions at
938/// once, which would otherwise make it unusable for tests that provision in parallel.
939static TOKEN_WRITES: Mutex<Vec<TokenWriteLock>> = Mutex::new(Vec::new());
940
941/// A token, as (canonical module path, slot id), and its write lock.
942type TokenWriteLock = ((PathBuf, u64), Arc<Mutex<()>>);
943
944/// The write lock of the token in `slot` of the module at `module` (canonical path).
945fn token_writes(module: &Path, slot: u64) -> Arc<Mutex<()>> {
946    let key = (
947        std::fs::canonicalize(module).unwrap_or_else(|_| module.to_path_buf()),
948        slot,
949    );
950    let mut locks = TOKEN_WRITES.lock().unwrap_or_else(PoisonError::into_inner);
951    if let Some((_, lock)) = locks.iter().find(|(k, _)| *k == key) {
952        return Arc::clone(lock);
953    }
954    let lock = Arc::new(Mutex::new(()));
955    locks.push((key, Arc::clone(&lock)));
956    lock
957}
958
959/// The initialized context for the module at `module`, loading it on first use.
960fn load_module(module: &Path) -> Result<Pkcs11, KeyStoreError> {
961    let key = std::fs::canonicalize(module).unwrap_or_else(|_| module.to_path_buf());
962    let mut modules = MODULES.lock().unwrap_or_else(PoisonError::into_inner);
963    if let Some((_, context)) = modules.iter().find(|(path, _)| *path == key) {
964        return Ok(context.clone());
965    }
966    let failed = |source| {
967        KeyStoreError::backend(Pkcs11Error::Module {
968            module: module.to_path_buf(),
969            source,
970        })
971    };
972    let context = Pkcs11::new(module).map_err(failed)?;
973    // The application itself may have initialized the module through another handle.
974    match context.initialize(CInitializeArgs::new(CInitializeFlags::OS_LOCKING_OK)) {
975        Ok(()) | Err(CryptokiError::Pkcs11(RvError::CryptokiAlreadyInitialized, _)) => {}
976        Err(source) => return Err(failed(source)),
977    }
978    modules.push((key, context.clone()));
979    Ok(context)
980}
981
982/// The kinds of key the store signs with.
983#[derive(Debug, Clone, Copy, PartialEq, Eq)]
984enum KeyKind {
985    Ed25519,
986    P256,
987    Secp256k1,
988    Rsa,
989}
990
991/// Finds the slot holding `token`, with the token's label and whether it is write-protected.
992fn find_token(
993    context: &Pkcs11,
994    token: &Pkcs11Token,
995) -> Result<(Slot, String, bool), KeyStoreError> {
996    let not_found = || KeyStoreError::backend(Pkcs11Error::TokenNotFound(token.clone()));
997    let slots = context
998        .get_slots_with_token()
999        .map_err(Pkcs11Error::call("C_GetSlotList"))?;
1000    for slot in slots {
1001        let matches = match token {
1002            Pkcs11Token::Slot(id) => slot.id() == *id,
1003            Pkcs11Token::Label(_) => true,
1004        };
1005        if !matches {
1006            continue;
1007        }
1008        let info = context
1009            .get_token_info(slot)
1010            .map_err(Pkcs11Error::call("C_GetTokenInfo"))?;
1011        let label = info.label().trim_end().to_owned();
1012        if let Pkcs11Token::Label(wanted) = token
1013            && label != wanted.trim_end()
1014        {
1015            continue;
1016        }
1017        return Ok((slot, label, info.write_protected()));
1018    }
1019    Err(not_found())
1020}
1021
1022/// The one object of `class` labelled `key`, if any.
1023fn find_key(
1024    session: &Session,
1025    class: ObjectClass,
1026    key: &KeyId,
1027) -> Result<Option<ObjectHandle>, KeyStoreError> {
1028    let template = [
1029        Attribute::Class(class),
1030        Attribute::Label(key.as_str().as_bytes().to_vec()),
1031    ];
1032    let objects = session
1033        .find_objects(&template)
1034        .map_err(Pkcs11Error::call("C_FindObjects"))?;
1035    match objects.as_slice() {
1036        [] => Ok(None),
1037        [object] => Ok(Some(*object)),
1038        several => Err(KeyStoreError::invalid_key(
1039            key.clone(),
1040            format!(
1041                "the label names {} objects of the same class",
1042                several.len()
1043            ),
1044        )),
1045    }
1046}
1047
1048/// Destroys the private and the public key object labelled `key`.
1049///
1050/// Both are attempted even when the first fails, so a failure leaves as little of the key on
1051/// the token as it can; the first error is returned.
1052fn delete(session: &Session, key: &KeyId) -> Result<(), KeyStoreError> {
1053    let private = find_key(session, ObjectClass::PRIVATE_KEY, key)?;
1054    let public = find_key(session, ObjectClass::PUBLIC_KEY, key)?;
1055    if private.is_none() && public.is_none() {
1056        return Err(KeyStoreError::NotFound(key.clone()));
1057    }
1058    let mut destroyed = Ok(());
1059    for object in [private, public].into_iter().flatten() {
1060        destroyed = destroyed.and(session.destroy_object(object));
1061    }
1062    destroyed.map_err(Pkcs11Error::call("C_DestroyObject"))
1063}
1064
1065/// A label for a key pair that is being generated without a label, unique within the process
1066/// and unlikely to repeat across processes.
1067#[expect(
1068    clippy::cast_possible_truncation,
1069    reason = "the low 64 bits of the time are plenty to keep labels apart"
1070)]
1071fn staging_label() -> String {
1072    static COUNTER: AtomicU64 = AtomicU64::new(0);
1073    let nanos = std::time::SystemTime::now()
1074        .duration_since(std::time::UNIX_EPOCH)
1075        .map_or(0, |since| since.as_nanos() as u64);
1076    format!(
1077        "{STAGING_LABEL_PREFIX}{nanos:016x}-{:x}",
1078        COUNTER.fetch_add(1, Ordering::Relaxed)
1079    )
1080}
1081
1082/// `CKA_LABEL` and `CKA_ID`, both set to `label` so that tools pairing the objects of a key by
1083/// id (`pkcs11-tool`, for one) see one key.
1084fn label_attributes(label: &str) -> [Attribute; 2] {
1085    [
1086        Attribute::Label(label.as_bytes().to_vec()),
1087        Attribute::Id(label.as_bytes().to_vec()),
1088    ]
1089}
1090
1091/// What kind of key the object `object` is, from `CKA_KEY_TYPE` and `CKA_EC_PARAMS`.
1092fn key_kind(
1093    session: &Session,
1094    key: &KeyId,
1095    object: ObjectHandle,
1096) -> Result<KeyKind, KeyStoreError> {
1097    let attributes = session
1098        .get_attributes(object, &[AttributeType::KeyType, AttributeType::EcParams])
1099        .map_err(Pkcs11Error::call("C_GetAttributeValue"))?;
1100    let mut key_type = None;
1101    let mut params = None;
1102    for attribute in attributes {
1103        match attribute {
1104            Attribute::KeyType(t) => key_type = Some(t),
1105            Attribute::EcParams(p) => params = Some(p),
1106            _ => {}
1107        }
1108    }
1109    let invalid = |reason: &str| KeyStoreError::invalid_key(key.clone(), reason);
1110    match key_type {
1111        Some(CkKeyType::RSA) => Ok(KeyKind::Rsa),
1112        Some(CkKeyType::EC) => match params.as_deref() {
1113            Some(P256_PARAMS) => Ok(KeyKind::P256),
1114            Some(SECP256K1_PARAMS) => Ok(KeyKind::Secp256k1),
1115            Some(_) => Err(invalid("the curve is not P-256 or secp256k1")),
1116            None => Err(invalid("the EC key has no CKA_EC_PARAMS")),
1117        },
1118        Some(CkKeyType::EC_EDWARDS) => match params.as_deref() {
1119            Some(ED25519_PARAMS | ED25519_NAME_PARAMS) => Ok(KeyKind::Ed25519),
1120            Some(_) => Err(invalid("the Edwards curve is not Ed25519")),
1121            None => Err(invalid("the Edwards key has no CKA_EC_PARAMS")),
1122        },
1123        Some(_) => Err(invalid("the key type is not RSA, EC or EC_EDWARDS")),
1124        None => Err(invalid("the object has no CKA_KEY_TYPE")),
1125    }
1126}
1127
1128/// The public point of the EC public key object `object`, a key of `kind`: the content of its
1129/// `CKA_EC_POINT`, a DER `OCTET STRING` holding the uncompressed SEC1 point (P-256, secp256k1)
1130/// or the 32-byte public key (Ed25519), or that content bare, as some modules store it.
1131fn ec_point(
1132    session: &Session,
1133    key: &KeyId,
1134    object: ObjectHandle,
1135    kind: KeyKind,
1136) -> Result<Vec<u8>, KeyStoreError> {
1137    let attributes = session
1138        .get_attributes(object, &[AttributeType::EcPoint])
1139        .map_err(Pkcs11Error::call("C_GetAttributeValue"))?;
1140    let invalid = |reason: &str| KeyStoreError::invalid_key(key.clone(), reason);
1141    let Some(Attribute::EcPoint(encoded)) = attributes.into_iter().next() else {
1142        return Err(invalid("the public key object has no CKA_EC_POINT"));
1143    };
1144    ec_point_bytes(&encoded, kind)
1145        .map(<[u8]>::to_vec)
1146        .ok_or_else(|| invalid("CKA_EC_POINT is neither a DER OCTET STRING nor a bare point"))
1147}
1148
1149/// The point in a `CKA_EC_POINT` value: the content of the DER `OCTET STRING` the attribute
1150/// should be, or the value itself when it is not one but is a point of the size `kind` has.
1151///
1152/// A DER content of the right size wins, so that a bare value which happens to parse as DER
1153/// (a 32-byte key starting `04 1e`) is still read correctly whenever its content would not fit.
1154fn ec_point_bytes(encoded: &[u8], kind: KeyKind) -> Option<&[u8]> {
1155    let fits = |point: &[u8]| match kind {
1156        KeyKind::Ed25519 => point.len() == 32,
1157        KeyKind::P256 | KeyKind::Secp256k1 => point.len() == 65 && point[0] == 0x04,
1158        KeyKind::Rsa => false,
1159    };
1160    match der_octet_string(encoded) {
1161        Some(content) if fits(content) => Some(content),
1162        _ if fits(encoded) => Some(encoded),
1163        other => other,
1164    }
1165}
1166
1167/// The content of a DER `OCTET STRING` that spans exactly `der`.
1168fn der_octet_string(der: &[u8]) -> Option<&[u8]> {
1169    let (&0x04, rest) = der.split_first()? else {
1170        return None;
1171    };
1172    let (&first, rest) = rest.split_first()?;
1173    let (length, content) = if first < 0x80 {
1174        (usize::from(first), rest)
1175    } else {
1176        let length_bytes = usize::from(first & 0x7f);
1177        if length_bytes == 0 || length_bytes > 2 || rest.len() < length_bytes {
1178            return None;
1179        }
1180        let (length, content) = rest.split_at(length_bytes);
1181        let length = length
1182            .iter()
1183            .fold(0usize, |n, &b| (n << 8) | usize::from(b));
1184        // DER uses the shortest form: long form only from 128 up.
1185        if length < 0x80 {
1186            return None;
1187        }
1188        (length, content)
1189    };
1190    (content.len() == length).then_some(content)
1191}
1192
1193/// [`Pkcs11Error::InvalidOptions`] as a [`KeyStoreError`].
1194fn invalid_options(reason: impl Into<String>) -> KeyStoreError {
1195    KeyStoreError::backend(Pkcs11Error::InvalidOptions(reason.into()))
1196}
1197
1198// `Pkcs11Error` is what `Backend` carries, so it must be sendable between threads.
1199const _: () = {
1200    const fn assert_send_sync<T: Send + Sync + Error + 'static>() {}
1201    assert_send_sync::<Pkcs11Error>();
1202};
1203
1204#[cfg(test)]
1205mod tests {
1206    use super::*;
1207
1208    #[test]
1209    fn der_octet_strings_decode() {
1210        // Short form, as every EC point of this crate's curves uses.
1211        assert_eq!(
1212            der_octet_string(&[0x04, 0x02, 0xaa, 0xbb]),
1213            Some(&[0xaa, 0xbb][..])
1214        );
1215        assert_eq!(der_octet_string(&[0x04, 0x00]), Some(&[][..]));
1216        // Long form.
1217        let mut long = vec![0x04, 0x81, 0x80];
1218        long.extend(std::iter::repeat_n(7u8, 128));
1219        assert_eq!(der_octet_string(&long), Some(&long[3..]));
1220        let mut longer = vec![0x04, 0x82, 0x01, 0x00];
1221        longer.extend(std::iter::repeat_n(7u8, 256));
1222        assert_eq!(der_octet_string(&longer), Some(&longer[4..]));
1223
1224        // Not an OCTET STRING, truncated, trailing bytes, non-minimal length.
1225        assert_eq!(der_octet_string(&[0x03, 0x01, 0xaa]), None);
1226        assert_eq!(der_octet_string(&[0x04]), None);
1227        assert_eq!(der_octet_string(&[0x04, 0x03, 0xaa, 0xbb]), None);
1228        assert_eq!(der_octet_string(&[0x04, 0x01, 0xaa, 0xbb]), None);
1229        assert_eq!(der_octet_string(&[0x04, 0x81, 0x01, 0xaa]), None);
1230        assert_eq!(der_octet_string(&[0x04, 0x80]), None);
1231        assert_eq!(
1232            der_octet_string(&[0x04, 0x83, 0x00, 0x00, 0x01, 0xaa]),
1233            None
1234        );
1235        assert_eq!(der_octet_string(&[]), None);
1236    }
1237
1238    #[test]
1239    fn ec_points_are_read_wrapped_or_bare() {
1240        let ed25519 = [7u8; 32];
1241        let mut wrapped = vec![0x04, 0x20];
1242        wrapped.extend_from_slice(&ed25519);
1243        assert_eq!(
1244            ec_point_bytes(&wrapped, KeyKind::Ed25519),
1245            Some(&ed25519[..])
1246        );
1247        assert_eq!(
1248            ec_point_bytes(&ed25519, KeyKind::Ed25519),
1249            Some(&ed25519[..])
1250        );
1251        // A bare key that happens to parse as DER: its DER content is 30 bytes, which does
1252        // not fit, so the bare reading wins.
1253        let mut tricky = vec![0x04, 0x1e];
1254        tricky.extend_from_slice(&[9u8; 30]);
1255        assert_eq!(ec_point_bytes(&tricky, KeyKind::Ed25519), Some(&tricky[..]));
1256
1257        let mut sec1 = vec![0x04];
1258        sec1.extend_from_slice(&[1u8; 64]);
1259        let mut wrapped = vec![0x04, 0x41];
1260        wrapped.extend_from_slice(&sec1);
1261        for kind in [KeyKind::P256, KeyKind::Secp256k1] {
1262            assert_eq!(ec_point_bytes(&wrapped, kind), Some(&sec1[..]));
1263            assert_eq!(ec_point_bytes(&sec1, kind), Some(&sec1[..]));
1264            // The wrong size for the kind: the DER content is handed on for the key
1265            // constructor to refuse.
1266            assert_eq!(ec_point_bytes(&[0x04, 0x01, 0xaa], kind), Some(&[0xaa][..]));
1267        }
1268        // A compressed point is not accepted bare, and nothing that is not DER is.
1269        let mut compressed = vec![0x02];
1270        compressed.extend_from_slice(&[1u8; 64]);
1271        assert_eq!(ec_point_bytes(&compressed, KeyKind::P256), None);
1272        assert_eq!(ec_point_bytes(&[0x05, 0x01, 0xaa], KeyKind::P256), None);
1273        assert_eq!(ec_point_bytes(&ed25519, KeyKind::Rsa), None);
1274    }
1275
1276    #[test]
1277    fn staging_labels_are_distinct() {
1278        let a = staging_label();
1279        let b = staging_label();
1280        assert!(a.starts_with(STAGING_LABEL_PREFIX) && b.starts_with(STAGING_LABEL_PREFIX));
1281        assert_ne!(a, b);
1282        assert!(a.len() <= 64, "fits the usual label limits: {a}");
1283    }
1284
1285    #[test]
1286    fn the_digest_info_prefix_is_the_one_of_rfc_8017() {
1287        // RFC 8017 §9.2, note 1: SHA-256's DigestInfo is this prefix followed by the 32-byte hash.
1288        let mut digest_info = SHA256_DIGEST_INFO_PREFIX.to_vec();
1289        digest_info.extend_from_slice(&[0u8; 32]);
1290        assert_eq!(digest_info.len(), 51);
1291        assert_eq!(digest_info[1], 0x31, "SEQUENCE length covers the rest");
1292        assert_eq!(
1293            &digest_info[4..13],
1294            &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04]
1295        );
1296        assert_eq!(&digest_info[13..15], &[0x02, 0x01], "id-sha256");
1297        assert_eq!(
1298            &digest_info[17..19],
1299            &[0x04, 0x20],
1300            "OCTET STRING of 32 bytes"
1301        );
1302    }
1303
1304    #[test]
1305    fn tokens_convert_and_display() {
1306        assert_eq!(Pkcs11Token::from("a"), Pkcs11Token::Label("a".to_owned()));
1307        assert_eq!(
1308            Pkcs11Token::from("a".to_owned()),
1309            Pkcs11Token::Label("a".to_owned())
1310        );
1311        assert_eq!(Pkcs11Token::from(7), Pkcs11Token::Slot(7));
1312        assert_eq!(Pkcs11Token::from("a").to_string(), "token `a`");
1313        assert_eq!(Pkcs11Token::from(7).to_string(), "slot 7");
1314    }
1315
1316    #[test]
1317    fn options_default_and_build() {
1318        let options = Pkcs11KeyStoreOptions::new();
1319        assert_eq!(options, Pkcs11KeyStoreOptions::default());
1320        assert_eq!(options.sessions, 4);
1321        assert_eq!(options.rsa_algorithm, JwsAlgorithm::RS256);
1322        assert_eq!(options.algorithms, None);
1323        let options = options
1324            .sessions(1)
1325            .rsa_algorithm(JwsAlgorithm::PS256)
1326            .algorithms([JwsAlgorithm::EdDSA]);
1327        assert_eq!(options.sessions, 1);
1328        assert_eq!(options.rsa_algorithm, JwsAlgorithm::PS256);
1329        assert_eq!(options.algorithms, Some(vec![JwsAlgorithm::EdDSA]));
1330    }
1331
1332    #[test]
1333    fn bad_options_are_refused_before_the_module_loads() {
1334        let no_sessions = Pkcs11KeyStore::open_with(
1335            "/nonexistent/module.so",
1336            "token",
1337            None,
1338            Pkcs11KeyStoreOptions::new().sessions(0),
1339        )
1340        .unwrap_err();
1341        assert_eq!(
1342            no_sessions.to_string(),
1343            "key store backend error: PKCS#11 key store options: `sessions` must be at least 1"
1344        );
1345        let wrong_rsa = Pkcs11KeyStore::open_with(
1346            "/nonexistent/module.so",
1347            "token",
1348            None,
1349            Pkcs11KeyStoreOptions::new().rsa_algorithm(JwsAlgorithm::ES256),
1350        )
1351        .unwrap_err();
1352        assert_eq!(
1353            wrong_rsa.to_string(),
1354            "key store backend error: PKCS#11 key store options: `rsa_algorithm` must be RS256 or PS256, not ES256"
1355        );
1356    }
1357
1358    #[test]
1359    fn a_missing_module_is_reported_with_its_path() {
1360        let error = Pkcs11KeyStore::open("/nonexistent/module.so", "token", None).unwrap_err();
1361        let KeyStoreError::Backend(source) = &error else {
1362            panic!("expected Backend, got {error:?}");
1363        };
1364        let pkcs11 = source.downcast_ref::<Pkcs11Error>().expect("a Pkcs11Error");
1365        assert!(
1366            matches!(pkcs11, Pkcs11Error::Module { module, .. } if module == Path::new("/nonexistent/module.so"))
1367        );
1368        assert!(
1369            error
1370                .to_string()
1371                .starts_with("key store backend error: PKCS#11 module `/nonexistent/module.so`: "),
1372            "{error}"
1373        );
1374        assert!(pkcs11.source().is_some());
1375    }
1376
1377    #[test]
1378    fn errors_that_close_a_session_are_known() {
1379        let call = |rv| Pkcs11Error::Call {
1380            function: "C_Sign",
1381            source: CryptokiError::Pkcs11(rv, cryptoki::context::Function::Sign),
1382        };
1383        assert!(call(RvError::SessionHandleInvalid).closes_session());
1384        assert!(call(RvError::DeviceRemoved).closes_session());
1385        assert!(!call(RvError::KeyHandleInvalid).closes_session());
1386        assert!(!Pkcs11Error::TokenNotFound(Pkcs11Token::Slot(1)).closes_session());
1387        assert_eq!(
1388            Pkcs11Error::SignatureLength {
1389                alg: JwsAlgorithm::ES256,
1390                expected: 64,
1391                actual: 70
1392            }
1393            .to_string(),
1394            "PKCS#11 module returned a 70-byte signature for ES256, expected 64"
1395        );
1396    }
1397}