Skip to content

Commit ccba914

Browse files
committed
adds x509 certificate support
Signed-off-by: Arthur Gautier <[email protected]>
1 parent 7a73aad commit ccba914

File tree

5 files changed

+145
-0
lines changed

5 files changed

+145
-0
lines changed

cryptoki-rustcrypto/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ signature = { version = "2.2.0", features = ["digest"] }
2323
sha1 = { version = "0.10", features = ["oid"] }
2424
sha2 = { version = "0.10", features = ["oid"] }
2525
spki = "0.7.2"
26+
x509-cert = "0.2.4"
2627
thiserror = "1.0"
2728

2829
[dev-dependencies]

cryptoki-rustcrypto/src/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ use cryptoki::{
1010

1111
pub mod ecdsa;
1212
pub mod rsa;
13+
pub mod x509;
1314

1415
pub trait SessionLike {
16+
fn create_object(&self, template: &[Attribute]) -> Result<ObjectHandle>;
1517
fn find_objects(&self, template: &[Attribute]) -> Result<Vec<ObjectHandle>>;
1618
fn get_attributes(
1719
&self,
@@ -22,6 +24,9 @@ pub trait SessionLike {
2224
}
2325

2426
impl SessionLike for Session {
27+
fn create_object(&self, template: &[Attribute]) -> Result<ObjectHandle> {
28+
Session::create_object(self, template)
29+
}
2530
fn find_objects(&self, template: &[Attribute]) -> Result<Vec<ObjectHandle>> {
2631
Session::find_objects(self, template)
2732
}
@@ -38,6 +43,9 @@ impl SessionLike for Session {
3843
}
3944

4045
impl<'s> SessionLike for &'s Session {
46+
fn create_object(&self, template: &[Attribute]) -> Result<ObjectHandle> {
47+
Session::create_object(self, template)
48+
}
4149
fn find_objects(&self, template: &[Attribute]) -> Result<Vec<ObjectHandle>> {
4250
Session::find_objects(self, template)
4351
}

cryptoki-rustcrypto/src/x509.rs

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright 2023 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use cryptoki::object::{Attribute, AttributeType, CertificateType, ObjectClass, ObjectHandle};
5+
use thiserror::Error;
6+
use x509_cert::{
7+
certificate::{CertificateInner, Profile},
8+
der::{Decode, Encode},
9+
};
10+
11+
use crate::SessionLike;
12+
13+
#[derive(Debug, Error)]
14+
pub enum Error {
15+
#[error("Cryptoki error: {0}")]
16+
Cryptoki(#[from] cryptoki::error::Error),
17+
18+
#[error("Missing attribute: {0}")]
19+
MissingAttribute(AttributeType),
20+
21+
#[error(transparent)]
22+
Der(#[from] x509_cert::der::Error),
23+
24+
#[error("No such certificate found")]
25+
MissingCert,
26+
}
27+
28+
pub trait CertPkcs11 {
29+
fn pkcs11_store<S: SessionLike, T: Into<Vec<Attribute>>>(
30+
&self,
31+
session: &S,
32+
base_template: T,
33+
) -> Result<ObjectHandle, Error>;
34+
35+
fn pkcs11_load<S: SessionLike, T: Into<Vec<Attribute>>>(
36+
session: &S,
37+
template: T,
38+
) -> Result<Self, Error>
39+
where
40+
Self: Sized;
41+
}
42+
43+
impl<P> CertPkcs11 for CertificateInner<P>
44+
where
45+
P: Profile,
46+
{
47+
fn pkcs11_store<S: SessionLike, T: Into<Vec<Attribute>>>(
48+
&self,
49+
session: &S,
50+
base_template: T,
51+
) -> Result<ObjectHandle, Error> {
52+
let mut template = base_template.into();
53+
template.push(Attribute::Class(ObjectClass::CERTIFICATE));
54+
template.push(Attribute::CertificateType(CertificateType::X_509));
55+
template.push(Attribute::Token(true));
56+
template.push(Attribute::Value(self.to_der()?));
57+
if !self.tbs_certificate.subject.is_empty() {
58+
template.push(Attribute::Subject(self.tbs_certificate.subject.to_der()?));
59+
}
60+
61+
Ok(session.create_object(&template)?)
62+
}
63+
64+
fn pkcs11_load<S: SessionLike, T: Into<Vec<Attribute>>>(
65+
session: &S,
66+
template: T,
67+
) -> Result<Self, Error> {
68+
let mut template = template.into();
69+
template.push(Attribute::Class(ObjectClass::CERTIFICATE));
70+
template.push(Attribute::CertificateType(CertificateType::X_509));
71+
72+
let certs = session.find_objects(&template)?;
73+
if let Some(cert) = certs.first() {
74+
let attributes = session.get_attributes(*cert, &[AttributeType::Value])?;
75+
76+
let mut value = None;
77+
for attribute in attributes {
78+
match attribute {
79+
Attribute::Value(v) if value.is_none() => {
80+
value = Some(v);
81+
}
82+
_ => {}
83+
}
84+
}
85+
86+
let value = value.ok_or(Error::MissingAttribute(AttributeType::Value))?;
87+
88+
let cert = Self::from_der(&value)?;
89+
90+
Ok(cert)
91+
} else {
92+
Err(Error::MissingCert)
93+
}
94+
}
95+
}
Binary file not shown.

cryptoki-rustcrypto/tests/x509.rs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2023 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
mod common;
5+
6+
use crate::common::USER_PIN;
7+
use common::init_pins;
8+
use cryptoki::{object::Attribute, session::UserType, types::AuthPin};
9+
use cryptoki_rustcrypto::x509::CertPkcs11;
10+
use der::Decode;
11+
use serial_test::serial;
12+
use testresult::TestResult;
13+
use x509_cert::Certificate;
14+
15+
const VERISIGN_CERT: &[u8] = include_bytes!("./15b05c4865410c6b3ff76a4e8f3d87276756bd0c.der");
16+
17+
#[test]
18+
#[serial]
19+
fn test_x509() -> TestResult {
20+
let (pkcs11, slot) = init_pins();
21+
22+
// open a session
23+
let session = pkcs11.open_rw_session(slot)?;
24+
25+
// log in the session
26+
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;
27+
28+
let cert = Certificate::from_der(VERISIGN_CERT).expect("read certificate from der");
29+
30+
let base_template = vec![Attribute::Label(b"demo-cert".to_vec())];
31+
32+
cert.pkcs11_store(&session, base_template.clone())
33+
.expect("Store cert with the PKCS11 provider");
34+
35+
let new = Certificate::pkcs11_load(&session, base_template)
36+
.expect("Lookup cert from PKCS11 provider");
37+
38+
assert_eq!(cert, new);
39+
40+
Ok(())
41+
}

0 commit comments

Comments
 (0)