Skip to main content

rustls_pkcs11/
lib.rs

1use cryptoki::{
2    context::{CInitializeArgs, CInitializeFlags, Pkcs11},
3    mechanism::{
4        Mechanism, MechanismType,
5        eddsa::{EddsaParams, EddsaSignatureScheme},
6        rsa::{PkcsMgfType, PkcsPssParams},
7    },
8    object::{Attribute, AttributeType, CertificateType, KeyType, ObjectClass},
9    session::{Session, UserType},
10    types::AuthPin,
11};
12use log::{debug, error, info};
13use rustls::{
14    self, OtherError, SignatureAlgorithm, SignatureScheme,
15    client::ResolvesClientCert,
16    pki_types::CertificateDer,
17    sign::{CertifiedKey, Signer, SigningKey},
18};
19use std::{
20    ffi::OsStr,
21    fmt,
22    sync::{Arc, Mutex},
23};
24use x509_certificate::X509Certificate;
25
26#[derive(Debug)]
27enum PKCS11Error {
28    CertificateNotFoundError,
29    KeyHandleNotFoundError,
30    UnsupportedSignatureSchemeError(SignatureScheme),
31    SignError(cryptoki::error::Error),
32}
33
34impl std::error::Error for PKCS11Error {}
35
36impl fmt::Display for PKCS11Error {
37    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38        match self {
39            PKCS11Error::SignError(err) => write!(f, "{}: {}", self, err),
40            PKCS11Error::UnsupportedSignatureSchemeError(scheme) => {
41                write!(f, "{}: {:?}", self, scheme)
42            }
43            _ => write!(f, "{}", self),
44        }
45    }
46}
47
48#[derive(Debug)]
49struct PKCS11Signer {
50    session: Arc<Mutex<Session>>,
51    scheme: SignatureScheme,
52}
53
54/// Maps rustls schemes to cryptoki mechanisms
55///
56/// TLS 1.3 supports:
57///   ECDSA_NISTP521_SHA512, ECDSA_NISTP384_SHA384, ECDSA_NISTP256_SHA256
58///   RSA_PSS_SHA512, RSA_PSS_SHA384, RSA_PSS_SHA256
59///   ED25519
60/// TLS 1.2 additionally supports:
61///   RSA_PKCS1_SHA384, RSA_PKCS1_SHA256
62/// YubiKey (5.1) supports:
63///   ECDSA_NISTP384_SHA384, ECDSA_NISTP256_SHA256
64///   RSA_PKCS1_SHA384, RSA_PKCS1_SHA256
65/// Most HSM do not implement RSA_PSS_*, *_SHA512, and ED25519
66///
67/// # Errors
68///
69/// - yields UnsupportedSignatureSchemeError for schemes that are not TLSv1.2 or TLSv1.3
70///
71impl<'a> TryInto<Mechanism<'a>> for &PKCS11Signer {
72    type Error = rustls::Error;
73
74    fn try_into(self) -> Result<Mechanism<'a>, rustls::Error> {
75        match self.scheme {
76            // 1.2.840.10045.4.3.2
77            SignatureScheme::ECDSA_NISTP256_SHA256 => Ok(Mechanism::EcdsaSha256),
78            // 1.2.840.10045.4.3.3
79            SignatureScheme::ECDSA_NISTP384_SHA384 => Ok(Mechanism::EcdsaSha384),
80            // 1.2.840.10045.4.3.4
81            SignatureScheme::ECDSA_NISTP521_SHA512 => Ok(Mechanism::EcdsaSha512),
82            // 1.2.840.113549.1.1.11
83            SignatureScheme::RSA_PKCS1_SHA256 => Ok(Mechanism::Sha256RsaPkcs),
84            // 1.2.840.113549.1.1.12
85            SignatureScheme::RSA_PKCS1_SHA384 => Ok(Mechanism::Sha384RsaPkcs),
86            // 1.2.840.113549.1.1.13
87            SignatureScheme::RSA_PKCS1_SHA512 => Ok(Mechanism::Sha512RsaPkcs),
88            // 1.2.840.113549.1.1.10 ...
89            SignatureScheme::RSA_PSS_SHA256 => {
90                let params = PkcsPssParams {
91                    hash_alg: MechanismType::SHA256_RSA_PKCS, // 2.16.840.1.101.3.4.2.1
92                    mgf: PkcsMgfType::MGF1_SHA256, // 1.2.840.113549.1.1.8 + 2.16.840.1.101.3.4.2.1
93                    s_len: 32.into(),
94                };
95                Ok(Mechanism::Sha256RsaPkcsPss(params))
96            }
97            // 1.2.840.113549.1.1.10 ...
98            SignatureScheme::RSA_PSS_SHA384 => {
99                let params = PkcsPssParams {
100                    hash_alg: MechanismType::SHA384_RSA_PKCS, // 2.16.840.1.101.3.4.2.2
101                    mgf: PkcsMgfType::MGF1_SHA384, // 1.2.840.113549.1.1.8 + 2.16.840.1.101.3.4.2.2
102                    s_len: 48.into(),
103                };
104                Ok(Mechanism::Sha384RsaPkcsPss(params))
105            }
106            // 1.2.840.113549.1.1.10 ...
107            SignatureScheme::RSA_PSS_SHA512 => {
108                let params = PkcsPssParams {
109                    hash_alg: MechanismType::SHA512_RSA_PKCS, // 2.16.840.1.101.3.4.2.3
110                    mgf: PkcsMgfType::MGF1_SHA512, // 1.2.840.113549.1.1.8 + 2.16.840.1.101.3.4.2.3
111                    s_len: 64.into(),
112                };
113                Ok(Mechanism::Sha384RsaPkcsPss(params))
114            }
115            // TODO: which HSM really supports this?
116            SignatureScheme::ED25519 => Ok(Mechanism::Eddsa(EddsaParams::new(
117                EddsaSignatureScheme::Ed25519,
118            ))),
119            unsupported_scheme => Err(rustls::Error::Other(OtherError(Arc::new(
120                PKCS11Error::UnsupportedSignatureSchemeError(unsupported_scheme),
121            )))),
122        }
123    }
124}
125
126/// Maps rustls schemes to cryptoki keytypes
127impl Into<KeyType> for &PKCS11Signer {
128    fn into(self) -> KeyType {
129        match self.scheme {
130            SignatureScheme::ECDSA_NISTP256_SHA256 => KeyType::EC,
131            SignatureScheme::ECDSA_NISTP384_SHA384 => KeyType::EC,
132            _ => KeyType::RSA,
133        }
134    }
135}
136
137impl Signer for PKCS11Signer {
138    fn sign(&self, message: &[u8]) -> Result<Vec<u8>, rustls::Error> {
139        let session = self.session.lock().unwrap();
140
141        let key_template = vec![
142            Attribute::Token(true),
143            Attribute::Private(true),
144            Attribute::Sign(true),
145            Attribute::KeyType(self.into()),
146        ];
147
148        let key = session
149            .find_objects(&key_template)
150            .unwrap() // how and when might this fail?
151            .into_iter()
152            .next()
153            .ok_or(rustls::Error::Other(OtherError(Arc::new(
154                PKCS11Error::KeyHandleNotFoundError,
155            ))))?;
156
157        let mechanism = self.try_into()?;
158
159        let mut signed_message = session.sign(&mechanism, key, message).map_err(|err| {
160            rustls::Error::Other(OtherError(Arc::new(PKCS11Error::SignError(err))))
161        })?;
162
163        // The signed message comes out of the YubiKey in raw r,s-form for EC signatures, needs to be an ASN.1 sequence.
164        // RSA signatures pass unchanged.
165        // TODO: how do other HSMs output EC signatures?
166        let signed_message = match self.into() {
167            KeyType::EC => signed_message.to_asn1_sig().unwrap(),
168            _ => signed_message,
169        };
170
171        debug!("{:?}({:02x?})", mechanism, signed_message);
172        Ok(signed_message)
173    }
174
175    fn scheme(&self) -> SignatureScheme {
176        self.scheme
177    }
178}
179
180#[derive(Debug)]
181struct PKCS11SigningKey {
182    session: Arc<Mutex<Session>>,
183    scheme: SignatureScheme,
184}
185
186impl Into<SignatureAlgorithm> for &PKCS11SigningKey {
187    fn into(self) -> SignatureAlgorithm {
188        match self.scheme {
189            SignatureScheme::ECDSA_NISTP256_SHA256 => SignatureAlgorithm::ECDSA,
190            SignatureScheme::ECDSA_NISTP384_SHA384 => SignatureAlgorithm::ECDSA,
191            SignatureScheme::RSA_PKCS1_SHA256 => SignatureAlgorithm::RSA,
192            SignatureScheme::RSA_PKCS1_SHA384 => SignatureAlgorithm::RSA,
193            SignatureScheme::RSA_PSS_SHA256 => SignatureAlgorithm::RSA,
194            SignatureScheme::RSA_PSS_SHA384 => SignatureAlgorithm::RSA,
195            _ => SignatureAlgorithm::Unknown(0),
196        }
197    }
198}
199
200impl SigningKey for PKCS11SigningKey {
201    fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> {
202        debug!(
203            "schemes offered: {:?}, scheme supported: {:?}",
204            offered, self.scheme
205        );
206        if offered.contains(&self.scheme) {
207            return Some(Box::new(PKCS11Signer {
208                session: self.session.clone(),
209                scheme: self.scheme,
210            }));
211        }
212        None
213    }
214
215    // mandatory trait fn, but seems to be unused
216    fn algorithm(&self) -> SignatureAlgorithm {
217        debug!("algorithm called!");
218        self.into()
219    }
220}
221
222#[derive(Debug)]
223pub struct PKCS11ClientCertResolver {
224    chain: Vec<CertificateDer<'static>>,
225    signing_key: Arc<PKCS11SigningKey>,
226}
227
228impl ResolvesClientCert for PKCS11ClientCertResolver {
229    fn resolve(
230        &self,
231        _acceptable_issuers: &[&[u8]],
232        _sigschemes: &[SignatureScheme],
233    ) -> Option<Arc<CertifiedKey>> {
234        Some(Arc::new(CertifiedKey {
235            cert: self.chain.clone(),
236            key: self.signing_key.clone(),
237            ocsp: None,
238        }))
239    }
240
241    fn has_certs(&self) -> bool {
242        self.chain.len() > 0
243    }
244}
245
246impl PKCS11ClientCertResolver {
247    /// Create a new PKCS11ClientCertResolver.
248    ///
249    /// # Arguments
250    ///
251    /// * `pin` - An optional `&str` of the pin that unlocks the device. Set to `None` if device does not need a pin to unlock.
252    /// * `path` - Absolute filepath to the PKCS11 library of your device.
253    ///
254    /// # Examples
255    ///
256    /// ```
257    /// let pin = Some("123456");
258    /// let tls = rustls::ClientConfig::builder()
259    ///    .with_root_certificates(root_certs)
260    ///    .with_client_cert_resolver(Arc::new(
261    ///        PKCS11ClientCertResolver::new(pin, "/usr/lib/opensc-pkcs11.so")?
262    ///    ));
263    /// ```
264    ///
265    /// # Errors
266    ///
267    /// Authenticator devices are complicated things. A lot can go wrong along the way.
268    /// To alleviate this, most checks happen at instantiation time here, therefore errors
269    /// yielded here can have a plethora of reasons:
270    ///
271    /// - the PKCS11 library could not be loaded under the file path.
272    /// - the pin is missing or incorrect.
273    /// - the authenticator device is not present or inaccessible.
274    /// - the PKCS11 library has problems communicating with the authenticator device.
275    /// - the authenticator device cannot read an appropriate certificate from the relevant slot.
276    /// - the authenticator device's certificate signature scheme is not supported.
277    ///
278    pub fn new(pin: Option<&str>, path: &OsStr) -> Result<Self, Box<dyn std::error::Error>> {
279        let pkcs11client = Pkcs11::new(path)?;
280        pkcs11client.initialize(CInitializeArgs::new(CInitializeFlags::OS_LOCKING_OK))?;
281
282        let slot = pkcs11client.get_slots_with_token()?.remove(0); // This only ever shows slot 9a of a YubiKey
283        let session = pkcs11client.open_ro_session(slot)?;
284        match pin {
285            Some(pin) => session.login(UserType::User, Some(&AuthPin::new(pin.into())))?,
286            None => session.login(UserType::User, None)?,
287        }
288
289        let search_template = vec![
290            Attribute::Class(ObjectClass::CERTIFICATE),
291            Attribute::CertificateType(CertificateType::X_509),
292        ];
293        let handle = session.find_objects(&search_template)?.remove(0);
294        let value = session
295            .get_attributes(handle, &[AttributeType::Value])?
296            .remove(0);
297
298        let (scheme, chain) = parse_certificate(value)?;
299
300        let session = Arc::new(Mutex::new(session));
301        let signing_key = Arc::new(PKCS11SigningKey { session, scheme });
302
303        Ok(Self { chain, signing_key })
304    }
305}
306
307/// helper trait to deal with asn.1
308trait ToASN1 {
309    fn to_asn1_sig(&mut self) -> Result<Vec<u8>, asn1::WriteError>;
310}
311
312/// transforms a raw r,s-form signature to an asn1 sequence
313impl ToASN1 for Vec<u8> {
314    fn to_asn1_sig(&mut self) -> Result<Vec<u8>, asn1::WriteError> {
315        let mut mid = self.len() / 2;
316
317        // if r or s are negative (have the msb set) we need to prepend a null byte before converting to bigint
318        if self[0] >> 7 == 1 {
319            self.insert(0, 0u8);
320            mid += 1; // shift the middle
321        }
322        if self[mid] >> 7 == 1 {
323            self.insert(mid, 0u8);
324        }
325
326        asn1::write(|w| {
327            w.write_element(&asn1::SequenceWriter::new(&|w: &mut asn1::Writer| {
328                let r = asn1::BigInt::new(&self[..mid]);
329                w.write_element(&r)?;
330                let s = asn1::BigInt::new(&self[mid..]);
331                w.write_element(&s)?;
332                Ok(())
333            }))
334        })
335    }
336}
337
338/// certificate helper
339fn parse_certificate(
340    value: Attribute,
341) -> Result<(SignatureScheme, Vec<CertificateDer<'static>>), Box<dyn std::error::Error>> {
342    match value {
343        Attribute::Value(cert) => {
344            let x509 = X509Certificate::from_der(&cert).unwrap();
345            info!(
346                "CN: {}, Issuer: {}",
347                x509.subject_common_name().unwrap(),
348                x509.issuer_common_name().unwrap(),
349            );
350            debug!(
351                "Signature algorithm: {:?}",
352                x509.signature_algorithm().unwrap(),
353            );
354            debug!("Pubkey({:02x})", x509.public_key_data());
355
356            let scheme = match x509.signature_algorithm().unwrap() {
357                // TODO: most HSMs do not support RSA_PSS_* and *_SHA512
358                // Yubikey 5.7 seemingly does not support ED25519 certificates
359                x509_certificate::SignatureAlgorithm::RsaSha256 => {
360                    SignatureScheme::RSA_PKCS1_SHA256 // TODO: support both RSA_PSS_SHA256 and RSA_PKCS1_SHA256
361                }
362                x509_certificate::SignatureAlgorithm::RsaSha384 => {
363                    SignatureScheme::RSA_PKCS1_SHA384 // TODO: support both RSA_PSS_SHA384 and RSA_PKCS1_SHA384
364                }
365                x509_certificate::SignatureAlgorithm::EcdsaSha256 => {
366                    SignatureScheme::ECDSA_NISTP256_SHA256
367                }
368                x509_certificate::SignatureAlgorithm::EcdsaSha384 => {
369                    SignatureScheme::ECDSA_NISTP384_SHA384
370                }
371                unsupported_scheme => {
372                    error!("Unsupported scheme: {}", unsupported_scheme);
373                    return Err(Box::new(PKCS11Error::UnsupportedSignatureSchemeError(
374                        SignatureScheme::Unknown(0),
375                    )));
376                }
377            };
378
379            let certificate_der = CertificateDer::from_slice(&cert).into_owned();
380            Ok((scheme, vec![certificate_der]))
381        }
382        _ => Err(Box::new(PKCS11Error::CertificateNotFoundError)),
383    }
384}