Skip to content

Commit 634d7be

Browse files
committed
refactor[pkcs11]: extract signing module
This module should have things only related to signing. The main module started to grow more to include parsing URIs, managing sessions and objects, etc. so singing-related code was extracted. Signed-off-by: Marcel Guzik <[email protected]>
1 parent a70f207 commit 634d7be

File tree

3 files changed

+273
-266
lines changed

3 files changed

+273
-266
lines changed

crates/extensions/tedge-p11-server/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ pub use proxy::TedgeP11Server;
3838
mod signer;
3939
pub use signer::signing_key;
4040

41-
/// Interfaces with the PKCS#11 dynamic module using cryptoki crate.
4241
pub mod pkcs11;
4342
pub use pkcs11::AuthPin;
4443
pub use pkcs11::CryptokiConfigDirect;

crates/extensions/tedge-p11-server/src/pkcs11/mod.rs

Lines changed: 9 additions & 265 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! rustls connector for PKCS#11 tokens.
1+
//! Interfaces with the PKCS#11 dynamic module using cryptoki crate.
22
//!
33
//! # `p11tool` quirks
44
//!
@@ -63,20 +63,14 @@
6363
//! - PKCS#11: <https://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/os/pkcs11-base-v2.40-os.html>
6464
6565
use anyhow::Context;
66-
use asn1_rs::BigInt;
6766
use asn1_rs::FromDer as _;
68-
use asn1_rs::Integer;
69-
use asn1_rs::SequenceOf;
7067
use asn1_rs::ToDer;
7168
use camino::Utf8Path;
7269
use camino::Utf8PathBuf;
7370
use cryptoki::context::CInitializeArgs;
7471
use cryptoki::context::Pkcs11;
7572
use cryptoki::error::Error;
76-
use cryptoki::mechanism::rsa::PkcsMgfType;
77-
use cryptoki::mechanism::rsa::PkcsPssParams;
7873
use cryptoki::mechanism::Mechanism;
79-
use cryptoki::mechanism::MechanismType;
8074
use cryptoki::object::Attribute;
8175
use cryptoki::object::AttributeType;
8276
use cryptoki::object::KeyType;
@@ -87,10 +81,7 @@ use cryptoki::session::UserType;
8781
use cryptoki::slot::SlotInfo;
8882
use cryptoki::slot::TokenInfo;
8983
use rsa::pkcs1::EncodeRsaPublicKey;
90-
use rustls::sign::Signer;
9184
use rustls::sign::SigningKey;
92-
use rustls::SignatureAlgorithm;
93-
use rustls::SignatureScheme;
9485
use serde::Deserialize;
9586
use serde::Serialize;
9687
use tracing::debug;
@@ -116,6 +107,10 @@ use crate::service::SignRequestWithSigScheme;
116107
use crate::service::SignResponse;
117108
use crate::service::TedgeP11Service;
118109

110+
mod signing;
111+
pub use signing::Pkcs11Signer;
112+
pub use signing::SigScheme;
113+
119114
mod uri;
120115

121116
/// Parameters used when opening a session.
@@ -503,7 +498,6 @@ impl CryptokiSession<'_> {
503498
pub fn signing_key(self) -> anyhow::Result<Pkcs11Signer> {
504499
// get the signing key
505500
let key = self.find_key_by_attributes(&self.uri_attributes, ObjectClass::PRIVATE_KEY)?;
506-
507501
let key_type = self
508502
.session
509503
.get_attributes(key, &[AttributeType::KeyType])?
@@ -515,10 +509,6 @@ impl CryptokiSession<'_> {
515509
anyhow::bail!("can't get key type");
516510
};
517511

518-
let session = Pkcs11Session {
519-
session: self.session,
520-
};
521-
522512
// we need to select a signature scheme to use with a key - each type of key can only have one signature scheme
523513
// ideally we'd simply get a cryptoki mechanism that corresponds to this sigscheme but it's not possible;
524514
// instead we have to manually parse additional attributes to select a proper sigscheme; currently don't do it
@@ -536,18 +526,18 @@ impl CryptokiSession<'_> {
536526

537527
let key = match keytype {
538528
KeyType::EC => {
539-
let sigscheme = get_ec_mechanism(&session.session, key)
540-
.unwrap_or(SigScheme::EcdsaNistp256Sha256);
529+
let sigscheme =
530+
get_ec_mechanism(&self.session, key).unwrap_or(SigScheme::EcdsaNistp256Sha256);
541531

542532
Pkcs11Signer {
543-
session: Arc::new(Mutex::new(session)),
533+
session: Arc::new(Mutex::new(self.session)),
544534
key,
545535
sigscheme,
546536
secondary_schemes: Vec::new(),
547537
}
548538
}
549539
KeyType::RSA => Pkcs11Signer {
550-
session: Arc::new(Mutex::new(session)),
540+
session: Arc::new(Mutex::new(self.session)),
551541
key,
552542
sigscheme: SigScheme::RsaPssSha256,
553543
secondary_schemes: vec![SigScheme::RsaPkcs1Sha256],
@@ -788,221 +778,6 @@ pub enum KeyTypeParams {
788778
Ec { curve: u16 },
789779
}
790780

791-
#[derive(Debug)]
792-
pub struct Pkcs11Session {
793-
pub session: Session,
794-
}
795-
796-
#[derive(Debug, Clone)]
797-
pub struct Pkcs11Signer {
798-
session: Arc<Mutex<Pkcs11Session>>,
799-
key: ObjectHandle,
800-
pub sigscheme: SigScheme,
801-
pub secondary_schemes: Vec<SigScheme>,
802-
}
803-
804-
impl Pkcs11Signer {
805-
pub fn sign(
806-
&self,
807-
message: &[u8],
808-
sigscheme: Option<SigScheme>,
809-
) -> Result<Vec<u8>, anyhow::Error> {
810-
let guard = self.session.lock().unwrap();
811-
let session = &guard.session;
812-
813-
let sigscheme = sigscheme.unwrap_or(self.sigscheme);
814-
let mechanism = sigscheme.into();
815-
let (mechanism, digest_mechanism) = match mechanism {
816-
Mechanism::EcdsaSha256 => (Mechanism::Ecdsa, Some(Mechanism::Sha256)),
817-
Mechanism::EcdsaSha384 => (Mechanism::Ecdsa, Some(Mechanism::Sha384)),
818-
Mechanism::EcdsaSha512 => (Mechanism::Ecdsa, Some(Mechanism::Sha512)),
819-
Mechanism::Sha256RsaPkcs => (Mechanism::Sha256RsaPkcs, None),
820-
Mechanism::Sha384RsaPkcs => (Mechanism::Sha384RsaPkcs, None),
821-
Mechanism::Sha512RsaPkcs => (Mechanism::Sha512RsaPkcs, None),
822-
Mechanism::Sha256RsaPkcsPss(p) => (Mechanism::Sha256RsaPkcsPss(p), None),
823-
Mechanism::Sha384RsaPkcsPss(p) => (Mechanism::Sha384RsaPkcsPss(p), None),
824-
Mechanism::Sha512RsaPkcsPss(p) => (Mechanism::Sha512RsaPkcsPss(p), None),
825-
_ => {
826-
warn!(?mechanism, "Unsupported mechanism, trying it out anyway.");
827-
(Mechanism::Ecdsa, Some(Mechanism::Sha256))
828-
}
829-
};
830-
831-
let direct_sign = digest_mechanism.is_none();
832-
833-
trace!(input_message = %String::from_utf8_lossy(message), len=message.len(), ?mechanism, direct_sign);
834-
835-
let digest;
836-
let to_sign = if direct_sign {
837-
message
838-
} else {
839-
digest = session
840-
.digest(&digest_mechanism.unwrap(), message)
841-
.context("pkcs11: Failed to digest message")?;
842-
&digest
843-
};
844-
845-
trace!(?mechanism, "Session::sign");
846-
let signature_raw = session
847-
.sign(&mechanism, self.key, to_sign)
848-
.context("pkcs11: Failed to sign message")?;
849-
850-
// Split raw signature into r and s values (assuming 32 bytes each)
851-
trace!("Signature (raw) len={:?}", signature_raw.len());
852-
let signature_asn1 = match mechanism {
853-
Mechanism::Ecdsa => {
854-
let size = signature_raw.len() / 2;
855-
let r_bytes = &signature_raw[0..size];
856-
let s_bytes = &signature_raw[size..];
857-
858-
format_asn1_ecdsa_signature(r_bytes, s_bytes)
859-
.context("pkcs11: Failed to format signature")?
860-
}
861-
862-
_ => signature_raw,
863-
};
864-
trace!(
865-
"Encoded ASN.1 Signature: len={:?} {:?}",
866-
signature_asn1.len(),
867-
signature_asn1
868-
);
869-
Ok(signature_asn1)
870-
}
871-
}
872-
873-
/// Currently supported signature schemes.
874-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
875-
pub enum SigScheme {
876-
EcdsaNistp256Sha256,
877-
EcdsaNistp384Sha384,
878-
EcdsaNistp521Sha512,
879-
RsaPssSha256,
880-
RsaPkcs1Sha256,
881-
}
882-
883-
impl From<SigScheme> for rustls::SignatureScheme {
884-
fn from(value: SigScheme) -> Self {
885-
match value {
886-
SigScheme::EcdsaNistp256Sha256 => Self::ECDSA_NISTP256_SHA256,
887-
SigScheme::EcdsaNistp384Sha384 => Self::ECDSA_NISTP384_SHA384,
888-
SigScheme::EcdsaNistp521Sha512 => Self::ECDSA_NISTP521_SHA512,
889-
SigScheme::RsaPssSha256 => Self::RSA_PSS_SHA256,
890-
SigScheme::RsaPkcs1Sha256 => Self::RSA_PKCS1_SHA256,
891-
}
892-
}
893-
}
894-
895-
impl From<SigScheme> for crate::service::SignatureScheme {
896-
fn from(value: SigScheme) -> Self {
897-
Self(rustls::SignatureScheme::from(value))
898-
}
899-
}
900-
901-
impl From<SigScheme> for rustls::SignatureAlgorithm {
902-
fn from(value: SigScheme) -> Self {
903-
match value {
904-
SigScheme::EcdsaNistp256Sha256
905-
| SigScheme::EcdsaNistp384Sha384
906-
| SigScheme::EcdsaNistp521Sha512 => Self::ECDSA,
907-
SigScheme::RsaPssSha256 | SigScheme::RsaPkcs1Sha256 => Self::RSA,
908-
}
909-
}
910-
}
911-
912-
impl From<SigScheme> for Mechanism<'_> {
913-
fn from(value: SigScheme) -> Self {
914-
match value {
915-
SigScheme::EcdsaNistp256Sha256 => Self::EcdsaSha256,
916-
SigScheme::EcdsaNistp384Sha384 => Self::EcdsaSha384,
917-
SigScheme::EcdsaNistp521Sha512 => Self::EcdsaSha512,
918-
SigScheme::RsaPkcs1Sha256 => Self::Sha256RsaPkcs,
919-
SigScheme::RsaPssSha256 => Mechanism::Sha256RsaPkcsPss(PkcsPssParams {
920-
hash_alg: MechanismType::SHA256,
921-
mgf: PkcsMgfType::MGF1_SHA256,
922-
// RFC8446 4.2.3: RSASSA-PSS PSS algorithms: [...] The length of
923-
// the Salt MUST be equal to the length of the digest algorithm
924-
// SHA256: 256 bits = 32 bytes
925-
s_len: 32.into(),
926-
}),
927-
}
928-
}
929-
}
930-
931-
impl SigningKey for Pkcs11Signer {
932-
fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> {
933-
debug!("Offered signature schemes. offered={:?}", offered);
934-
let key_scheme = self.sigscheme.into();
935-
if offered.contains(&key_scheme) {
936-
debug!("Matching scheme: {key_scheme:?}");
937-
return Some(Box::new(self.clone()));
938-
}
939-
940-
for scheme in &self.secondary_schemes {
941-
let key_scheme = (*scheme).into();
942-
if offered.contains(&key_scheme) {
943-
debug!("Matching scheme: {key_scheme:?}");
944-
let mut signer = self.clone();
945-
signer.sigscheme = *scheme;
946-
return Some(Box::new(signer));
947-
}
948-
}
949-
950-
None
951-
}
952-
953-
fn algorithm(&self) -> SignatureAlgorithm {
954-
self.sigscheme.into()
955-
}
956-
}
957-
958-
impl Signer for Pkcs11Signer {
959-
fn sign(&self, message: &[u8]) -> Result<Vec<u8>, rustls::Error> {
960-
Self::sign(self, message, Some(self.sigscheme))
961-
.map_err(|e| rustls::Error::General(e.to_string()))
962-
}
963-
964-
fn scheme(&self) -> SignatureScheme {
965-
self.sigscheme.into()
966-
}
967-
}
968-
969-
/// Formats the output of PKCS11 EC signature as an ASN.1 Ecdsa-Sig-Value.
970-
///
971-
/// This function takes the raw `r` and `s` byte slices and encodes them as ASN.1 INTEGERs,
972-
/// then wraps them in an ASN.1 SEQUENCE, and finally serializes the structure to DER.
973-
///
974-
/// PKCS#11 EC signature operations typically return a raw concatenation of the `r` and `s` values,
975-
/// each representing a big-endian positive integer of fixed length (depending on the curve).
976-
/// However, most cryptographic protocols (including TLS and X.509) expect ECDSA signatures to be
977-
/// encoded as an ASN.1 DER SEQUENCE of two INTEGERs, as described in RFC 3279 section 2.2.3.
978-
///
979-
/// - https://docs.oasis-open.org/pkcs11/pkcs11-curr/v3.0/os/pkcs11-curr-v3.0-os.html#_Toc30061178
980-
/// - https://www.ietf.org/rfc/rfc3279#section-2.2.3
981-
fn format_asn1_ecdsa_signature(r_bytes: &[u8], s_bytes: &[u8]) -> anyhow::Result<Vec<u8>> {
982-
let r = format_asn1_integer(r_bytes).to_signed_bytes_be();
983-
let r = Integer::new(&r);
984-
let s = format_asn1_integer(s_bytes).to_signed_bytes_be();
985-
let s = Integer::new(&s);
986-
987-
let seq = SequenceOf::<Integer>::from_iter([r, s]);
988-
let seq_der = seq
989-
.to_der_vec()
990-
.context("Unexpected ASN.1 error when serializing Ecdsa-Sig-Value")?;
991-
Ok(seq_der)
992-
}
993-
994-
fn format_asn1_integer(b: &[u8]) -> BigInt {
995-
let mut i = asn1_rs::BigInt::from_signed_bytes_be(b);
996-
if i.sign() == asn1_rs::Sign::Minus {
997-
// Prepend a most significant zero byte if value < 0
998-
let mut positive = b.to_vec();
999-
positive.insert(0, 0);
1000-
1001-
i = asn1_rs::BigInt::from_signed_bytes_be(&positive);
1002-
}
1003-
i
1004-
}
1005-
1006781
fn get_ec_mechanism(session: &Session, key: ObjectHandle) -> anyhow::Result<SigScheme> {
1007782
let key_params = &[AttributeType::EcParams];
1008783
let attrs = session
@@ -1060,34 +835,3 @@ fn export_session_uri(token_info: &TokenInfo) -> String {
1060835

1061836
uri
1062837
}
1063-
1064-
#[cfg(test)]
1065-
mod tests {
1066-
use super::*;
1067-
use asn1_rs::Any;
1068-
use asn1_rs::Integer;
1069-
use asn1_rs::SequenceOf;
1070-
1071-
#[test]
1072-
fn test_format_asn1_ecdsa_signature_invalid_asn1() {
1073-
// Use 32-byte r and s (as for P-256)
1074-
let r = [0x01u8; 32];
1075-
let s = [0xffu8; 32];
1076-
1077-
let der = format_asn1_ecdsa_signature(&r, &s).expect("Should encode");
1078-
1079-
// Try to parse as ASN.1 SEQUENCE of two INTEGERs
1080-
let parsed = Any::from_der(&der);
1081-
assert!(parsed.is_ok(), "Should parse as ASN.1");
1082-
1083-
// Now check that the sequence contains exactly two INTEGERs
1084-
let seq: SequenceOf<Integer> = SequenceOf::from_der(&der)
1085-
.expect("Should parse as sequence")
1086-
.1;
1087-
assert_eq!(seq.len(), 2, "ASN.1 sequence should have two items");
1088-
1089-
// make sure input is not misinterpreted as negative numbers
1090-
assert_eq!(seq[0].as_bigint().to_bytes_be().1, r);
1091-
assert_eq!(seq[1].as_bigint().to_bytes_be().1, s);
1092-
}
1093-
}

0 commit comments

Comments
 (0)