1use 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
23const P256_PARAMS: &[u8] = &[0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07];
25const SECP256K1_PARAMS: &[u8] = &[0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x0a];
27const ED25519_PARAMS: &[u8] = &[0x06, 0x03, 0x2b, 0x65, 0x70];
30const ED25519_NAME_PARAMS: &[u8] = b"\x13\x0cedwards25519";
33pub const STAGING_LABEL_PREFIX: &str = "identity-kit-staging-";
35const RSA_PUBLIC_EXPONENT: &[u8] = &[0x01, 0x00, 0x01];
37const 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
57#[non_exhaustive]
58pub enum Pkcs11Token {
59 Label(String),
62 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#[derive(Debug, Clone, PartialEq, Eq)]
115#[non_exhaustive]
116pub struct Pkcs11KeyStoreOptions {
117 pub sessions: usize,
122 pub rsa_algorithm: JwsAlgorithm,
130 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 pub fn new() -> Self {
152 Self::default()
153 }
154
155 #[must_use]
157 pub fn sessions(mut self, sessions: usize) -> Self {
158 self.sessions = sessions;
159 self
160 }
161
162 #[must_use]
164 pub fn rsa_algorithm(mut self, alg: JwsAlgorithm) -> Self {
165 self.rsa_algorithm = alg;
166 self
167 }
168
169 #[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#[derive(Debug, thiserror::Error)]
179#[non_exhaustive]
180pub enum Pkcs11Error {
181 #[error("PKCS#11 module `{}`: {source}", module.display())]
183 Module {
184 module: PathBuf,
186 #[source]
188 source: CryptokiError,
189 },
190 #[error("PKCS#11 module has no {0}")]
192 TokenNotFound(Pkcs11Token),
193 #[error("PKCS#11 key store options: {0}")]
195 InvalidOptions(String),
196 #[error("PKCS#11 {function} failed: {source}")]
198 Call {
199 function: &'static str,
201 #[source]
203 source: CryptokiError,
204 },
205 #[error("PKCS#11 module returned a {actual}-byte signature for {alg}, expected {expected}")]
207 SignatureLength {
208 alg: JwsAlgorithm,
210 expected: usize,
212 actual: usize,
214 },
215}
216
217impl Pkcs11Error {
218 fn call(function: &'static str) -> impl FnOnce(CryptokiError) -> KeyStoreError {
220 move |source| KeyStoreError::backend(Self::Call { function, source })
221 }
222
223 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#[derive(Clone)]
292pub struct Pkcs11KeyStore {
293 inner: Arc<Inner>,
294}
295
296struct Inner {
298 module: PathBuf,
299 context: Pkcs11,
300 slot: Slot,
301 token_label: String,
302 pin: Option<AuthPin>,
303 options: Pkcs11KeyStoreOptions,
304 algorithms: Vec<JwsAlgorithm>,
306 key_pair_generation: Vec<MechanismType>,
308 write_protected: bool,
309 pool: Pool,
310 writes: Arc<Mutex<()>>,
312}
313
314struct Pool {
316 state: Mutex<PoolState>,
317 available: Condvar,
319 max: usize,
321}
322
323#[derive(Default)]
324struct PoolState {
325 idle: Vec<Session>,
326 open: usize,
328}
329
330impl Pkcs11KeyStore {
331 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 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 let session = inner.open_session()?;
434 inner.pool.release(session);
435 Ok(Self {
436 inner: Arc::new(inner),
437 })
438 }
439
440 pub fn module(&self) -> &Path {
442 &self.inner.module
443 }
444
445 pub fn slot(&self) -> u64 {
447 self.inner.slot.id()
448 }
449
450 pub fn token_label(&self) -> &str {
452 &self.inner.token_label
453 }
454
455 pub fn options(&self) -> &Pkcs11KeyStoreOptions {
457 &self.inner.options
458 }
459
460 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 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 Ok(()) | Err(CryptokiError::Pkcs11(RvError::UserAlreadyLoggedIn, _)) => {}
570 Err(source) => return Err(Pkcs11Error::call("C_Login")(source)),
571 }
572 }
573 Ok(session)
574 }
575
576 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 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 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 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 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 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 #[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 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 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 let _ = session.destroy_object(private);
874 let _ = session.destroy_object(public);
875 }
876 labelled
877 }
878}
879
880impl Pool {
881 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 fn release(&self, session: Session) {
909 self.lock().idle.push(session);
910 self.available.notify_one();
911 }
912
913 fn discard(&self, session: Session) {
915 drop(session);
916 self.lock().open -= 1;
917 self.available.notify_one();
918 }
919
920 fn lock(&self) -> MutexGuard<'_, PoolState> {
922 self.state.lock().unwrap_or_else(PoisonError::into_inner)
923 }
924}
925
926static MODULES: Mutex<Vec<(PathBuf, Pkcs11)>> = Mutex::new(Vec::new());
932
933static TOKEN_WRITES: Mutex<Vec<TokenWriteLock>> = Mutex::new(Vec::new());
940
941type TokenWriteLock = ((PathBuf, u64), Arc<Mutex<()>>);
943
944fn 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
959fn 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
984enum KeyKind {
985 Ed25519,
986 P256,
987 Secp256k1,
988 Rsa,
989}
990
991fn 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
1022fn 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
1048fn 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#[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
1082fn 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
1091fn 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
1128fn 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
1149fn 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
1167fn 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 if length < 0x80 {
1186 return None;
1187 }
1188 (length, content)
1189 };
1190 (content.len() == length).then_some(content)
1191}
1192
1193fn invalid_options(reason: impl Into<String>) -> KeyStoreError {
1195 KeyStoreError::backend(Pkcs11Error::InvalidOptions(reason.into()))
1196}
1197
1198const _: () = {
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 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 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 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 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 assert_eq!(ec_point_bytes(&[0x04, 0x01, 0xaa], kind), Some(&[0xaa][..]));
1267 }
1268 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 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}