Skip to content

Commit 7fb4885

Browse files
committed
rustcrypto: initial commit
Signed-off-by: Arthur Gautier <baloo@superbaloo.net>
1 parent 6b81342 commit 7fb4885

File tree

9 files changed

+876
-3
lines changed

9 files changed

+876
-3
lines changed

Cargo.lock

Lines changed: 332 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
[workspace]
2-
members = ["cryptoki", "cryptoki-sys"]
2+
members = ["cryptoki", "cryptoki-sys", "cryptoki-rustcrypto"]

cryptoki-rustcrypto/Cargo.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "cryptoki-rustcrypto"
3+
version = "0.1.0"
4+
edition = "2018"
5+
authors = ["Contributors to the Parsec project"]
6+
description = "Compatibility layer from PKCS #11 to the RustCrypto ecosystem"
7+
readme = "README.md"
8+
keywords = ["pkcs11", "cryptoki", "hsm"]
9+
categories = ["cryptography", "hardware-support"]
10+
license = "Apache-2.0"
11+
12+
[dependencies]
13+
cryptoki = { path = "../cryptoki", version = "0.6.1" }
14+
der = "0.7.8"
15+
rsa = "0.9"
16+
signature = { version = "2.2.0", features = ["digest"] }
17+
sha1 = { version = "0.10", features = ["oid"] }
18+
sha2 = { version = "0.10", features = ["oid"] }
19+
spki = "0.7.2"
20+
thiserror = "1.0"
21+
22+
[dev-dependencies]
23+
serial_test = "0.5.1"
24+
testresult = "0.2.0"

cryptoki-rustcrypto/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright 2023 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
pub mod rsa;

cryptoki-rustcrypto/src/rsa/mod.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2023 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use cryptoki::mechanism::{
5+
rsa::{PkcsMgfType, PkcsPssParams},
6+
Mechanism, MechanismType,
7+
};
8+
use cryptoki::object::AttributeType;
9+
use der::oid::AssociatedOid;
10+
use signature::digest::Digest;
11+
use std::convert::TryInto;
12+
use thiserror::Error;
13+
14+
pub mod pkcs1v15;
15+
16+
pub mod pss;
17+
18+
#[derive(Debug, Error)]
19+
pub enum Error {
20+
#[error("Cryptoki error: {0}")]
21+
Cryptoki(#[from] cryptoki::error::Error),
22+
23+
#[error("Private key missing attribute: {0}")]
24+
MissingAttribute(AttributeType),
25+
26+
#[error("RSA error: {0}")]
27+
Rsa(#[from] rsa::Error),
28+
}
29+
30+
pub trait DigestSigning: Digest + AssociatedOid {
31+
fn pkcs_mechanism() -> Mechanism<'static>;
32+
33+
fn pss_mechanism() -> Mechanism<'static>;
34+
}
35+
36+
macro_rules! impl_digest_signing {
37+
($d:ty, $pkcs_mech:ident, $pss_mech:ident, $mt:ident, $mgf:ident) => {
38+
impl DigestSigning for $d {
39+
fn pkcs_mechanism() -> Mechanism<'static> {
40+
Mechanism::$pkcs_mech
41+
}
42+
43+
fn pss_mechanism() -> Mechanism<'static> {
44+
Mechanism::$pss_mech(PkcsPssParams {
45+
hash_alg: MechanismType::$mt,
46+
mgf: PkcsMgfType::$mgf,
47+
// Safety:
48+
// the output_size of an hash function will not go over 2^32,
49+
// this unwrap is safe.
50+
s_len: Self::output_size().try_into().unwrap(),
51+
})
52+
}
53+
}
54+
};
55+
}
56+
57+
impl_digest_signing!(sha1::Sha1, Sha1RsaPkcs, Sha1RsaPkcsPss, SHA1, MGF1_SHA1);
58+
impl_digest_signing!(
59+
sha2::Sha256,
60+
Sha256RsaPkcs,
61+
Sha256RsaPkcsPss,
62+
SHA256,
63+
MGF1_SHA256
64+
);
65+
impl_digest_signing!(
66+
sha2::Sha384,
67+
Sha384RsaPkcs,
68+
Sha384RsaPkcsPss,
69+
SHA384,
70+
MGF1_SHA384
71+
);
72+
impl_digest_signing!(
73+
sha2::Sha512,
74+
Sha512RsaPkcs,
75+
Sha512RsaPkcsPss,
76+
SHA512,
77+
MGF1_SHA512
78+
);
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright 2023 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use cryptoki::{
5+
object::{Attribute, AttributeType, KeyType, ObjectClass, ObjectHandle},
6+
session::Session,
7+
};
8+
use der::AnyRef;
9+
use rsa::{
10+
pkcs1,
11+
pkcs1v15::{Signature, VerifyingKey},
12+
BigUint, RsaPublicKey,
13+
};
14+
use spki::{AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier};
15+
use std::convert::TryFrom;
16+
17+
use super::{DigestSigning, Error};
18+
19+
pub struct Signer<D: DigestSigning> {
20+
session: Session,
21+
_public_key: ObjectHandle,
22+
private_key: ObjectHandle,
23+
verifying_key: VerifyingKey<D>,
24+
}
25+
26+
impl<D: DigestSigning> Signer<D> {
27+
pub fn new(session: Session, label: &[u8]) -> Result<Self, Error> {
28+
// First we'll lookup a private key with that label.
29+
let template = vec![
30+
Attribute::Token(true),
31+
Attribute::Private(true),
32+
Attribute::Label(label.to_vec()),
33+
Attribute::Class(ObjectClass::PRIVATE_KEY),
34+
Attribute::KeyType(KeyType::RSA),
35+
Attribute::Sign(true),
36+
];
37+
38+
let private_key = session.find_objects(&template)?.remove(0);
39+
let attribute_pk = session.get_attributes(
40+
private_key,
41+
&[AttributeType::Modulus, AttributeType::PublicExponent],
42+
)?;
43+
44+
// Second we'll lookup a public key with the same label/modulus/public exponent
45+
let mut template = vec![
46+
Attribute::Private(false),
47+
Attribute::Label(label.to_vec()),
48+
Attribute::Class(ObjectClass::PUBLIC_KEY),
49+
Attribute::KeyType(KeyType::RSA),
50+
];
51+
let mut modulus = None;
52+
let mut public_exponent = None;
53+
for attribute in attribute_pk {
54+
match attribute {
55+
Attribute::Modulus(m) if modulus.is_none() => {
56+
modulus = Some(m.clone());
57+
template.push(Attribute::Modulus(m));
58+
}
59+
Attribute::PublicExponent(e) if public_exponent.is_none() => {
60+
public_exponent = Some(e.clone());
61+
template.push(Attribute::PublicExponent(e));
62+
}
63+
_ => {}
64+
}
65+
}
66+
67+
let modulus = modulus
68+
.ok_or(Error::MissingAttribute(AttributeType::Modulus))
69+
.map(|v| BigUint::from_bytes_be(v.as_slice()))?;
70+
let public_exponent = public_exponent
71+
.ok_or(Error::MissingAttribute(AttributeType::PublicExponent))
72+
.map(|v| BigUint::from_bytes_be(v.as_slice()))?;
73+
74+
let public_key = session.find_objects(&template)?.remove(0);
75+
76+
let verifying_key = VerifyingKey::new(RsaPublicKey::new(modulus, public_exponent)?);
77+
78+
Ok(Self {
79+
session,
80+
private_key,
81+
_public_key: public_key,
82+
verifying_key,
83+
})
84+
}
85+
86+
pub fn into_session(self) -> Session {
87+
self.session
88+
}
89+
}
90+
91+
impl<D: DigestSigning> AssociatedAlgorithmIdentifier for Signer<D> {
92+
type Params = AnyRef<'static>;
93+
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
94+
}
95+
96+
impl<D: DigestSigning> signature::Keypair for Signer<D> {
97+
type VerifyingKey = VerifyingKey<D>;
98+
99+
fn verifying_key(&self) -> Self::VerifyingKey {
100+
self.verifying_key.clone()
101+
}
102+
}
103+
104+
impl<D: DigestSigning> signature::Signer<Signature> for Signer<D> {
105+
fn try_sign(&self, msg: &[u8]) -> Result<Signature, signature::Error> {
106+
let bytes = self
107+
.session
108+
.sign(&D::pkcs_mechanism(), self.private_key, msg)
109+
.map_err(Error::Cryptoki)
110+
.map_err(Box::new)
111+
.map_err(signature::Error::from_source)?;
112+
113+
let signature = Signature::try_from(bytes.as_slice())?;
114+
115+
Ok(signature)
116+
}
117+
}
118+
119+
impl<D: DigestSigning> SignatureAlgorithmIdentifier for Signer<D> {
120+
type Params = AnyRef<'static>;
121+
122+
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> =
123+
AlgorithmIdentifierRef {
124+
oid: D::OID,
125+
parameters: Some(AnyRef::NULL),
126+
};
127+
}

cryptoki-rustcrypto/src/rsa/pss.rs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright 2023 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use cryptoki::{
5+
object::{Attribute, AttributeType, KeyType, ObjectClass, ObjectHandle},
6+
session::Session,
7+
};
8+
use der::{asn1::ObjectIdentifier, oid::AssociatedOid, Any, AnyRef};
9+
use rsa::{
10+
pkcs1::{self, RsaPssParams},
11+
pkcs8::{self},
12+
pss::{Signature, VerifyingKey},
13+
BigUint, RsaPublicKey,
14+
};
15+
use signature::digest::Digest;
16+
use spki::{
17+
AlgorithmIdentifierOwned, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
18+
DynSignatureAlgorithmIdentifier,
19+
};
20+
use std::convert::TryFrom;
21+
22+
use super::{DigestSigning, Error};
23+
24+
pub struct Signer<D: DigestSigning> {
25+
session: Session,
26+
_public_key: ObjectHandle,
27+
private_key: ObjectHandle,
28+
verifying_key: VerifyingKey<D>,
29+
salt_len: usize,
30+
}
31+
32+
impl<D: DigestSigning> Signer<D> {
33+
pub fn new(session: Session, label: &[u8]) -> Result<Self, Error> {
34+
// First we'll lookup a private key with that label.
35+
let template = vec![
36+
Attribute::Token(true),
37+
Attribute::Private(true),
38+
Attribute::Label(label.to_vec()),
39+
Attribute::Class(ObjectClass::PRIVATE_KEY),
40+
Attribute::KeyType(KeyType::RSA),
41+
Attribute::Sign(true),
42+
];
43+
44+
let private_key = session.find_objects(&template)?.remove(0);
45+
let attribute_pk = session.get_attributes(
46+
private_key,
47+
&[AttributeType::Modulus, AttributeType::PublicExponent],
48+
)?;
49+
50+
// Second we'll lookup a public key with the same label/modulus/public exponent
51+
let mut template = vec![
52+
Attribute::Private(false),
53+
Attribute::Label(label.to_vec()),
54+
Attribute::Class(ObjectClass::PUBLIC_KEY),
55+
Attribute::KeyType(KeyType::RSA),
56+
];
57+
let mut modulus = None;
58+
let mut public_exponent = None;
59+
for attribute in attribute_pk {
60+
match attribute {
61+
Attribute::Modulus(m) if modulus.is_none() => {
62+
modulus = Some(m.clone());
63+
template.push(Attribute::Modulus(m));
64+
}
65+
Attribute::PublicExponent(e) if public_exponent.is_none() => {
66+
public_exponent = Some(e.clone());
67+
template.push(Attribute::PublicExponent(e));
68+
}
69+
_ => {}
70+
}
71+
}
72+
73+
let modulus = modulus
74+
.ok_or(Error::MissingAttribute(AttributeType::Modulus))
75+
.map(|v| BigUint::from_bytes_be(v.as_slice()))?;
76+
let public_exponent = public_exponent
77+
.ok_or(Error::MissingAttribute(AttributeType::PublicExponent))
78+
.map(|v| BigUint::from_bytes_be(v.as_slice()))?;
79+
80+
let public_key = session.find_objects(&template)?.remove(0);
81+
82+
let verifying_key = VerifyingKey::new(RsaPublicKey::new(modulus, public_exponent)?);
83+
let salt_len = <D as Digest>::output_size();
84+
85+
Ok(Self {
86+
session,
87+
private_key,
88+
_public_key: public_key,
89+
verifying_key,
90+
salt_len,
91+
})
92+
}
93+
94+
pub fn into_session(self) -> Session {
95+
self.session
96+
}
97+
}
98+
99+
impl<D: DigestSigning> AssociatedAlgorithmIdentifier for Signer<D> {
100+
type Params = AnyRef<'static>;
101+
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
102+
}
103+
104+
impl<D: DigestSigning> signature::Keypair for Signer<D> {
105+
type VerifyingKey = VerifyingKey<D>;
106+
107+
fn verifying_key(&self) -> Self::VerifyingKey {
108+
self.verifying_key.clone()
109+
}
110+
}
111+
112+
impl<D: DigestSigning> signature::Signer<Signature> for Signer<D> {
113+
fn try_sign(&self, msg: &[u8]) -> Result<Signature, signature::Error> {
114+
let bytes = self
115+
.session
116+
.sign(&D::pss_mechanism(), self.private_key, msg)
117+
.map_err(Error::Cryptoki)
118+
.map_err(Box::new)
119+
.map_err(signature::Error::from_source)?;
120+
121+
let signature = Signature::try_from(bytes.as_slice())?;
122+
123+
Ok(signature)
124+
}
125+
}
126+
127+
impl<D: DigestSigning> DynSignatureAlgorithmIdentifier for Signer<D> {
128+
fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result<AlgorithmIdentifierOwned> {
129+
get_pss_signature_algo_id::<D>(self.salt_len as u8)
130+
}
131+
}
132+
133+
fn get_pss_signature_algo_id<D>(salt_len: u8) -> pkcs8::spki::Result<AlgorithmIdentifierOwned>
134+
where
135+
D: Digest + AssociatedOid,
136+
{
137+
const ID_RSASSA_PSS: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10");
138+
139+
let pss_params = RsaPssParams::new::<D>(salt_len);
140+
141+
Ok(AlgorithmIdentifierOwned {
142+
oid: ID_RSASSA_PSS,
143+
parameters: Some(Any::encode_from(&pss_params)?),
144+
})
145+
}

0 commit comments

Comments
 (0)