-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Arthur Gautier <[email protected]>
- Loading branch information
Showing
9 changed files
with
862 additions
and
3 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
[workspace] | ||
members = ["cryptoki", "cryptoki-sys"] | ||
members = ["cryptoki", "cryptoki-sys", "cryptoki-rustcrypto"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
[package] | ||
name = "cryptoki-rustcrypto" | ||
version = "0.1.0" | ||
edition = "2018" | ||
authors = ["Contributors to the Parsec project"] | ||
description = "Compatibility layer from PKCS #11 to the RustCrypto ecosystem" | ||
readme = "README.md" | ||
keywords = ["pkcs11", "cryptoki", "hsm"] | ||
categories = ["cryptography", "hardware-support"] | ||
license = "Apache-2.0" | ||
|
||
[dependencies] | ||
cryptoki = { path = "../cryptoki", version = "0.6.1" } | ||
der = "0.7.8" | ||
rsa = "0.9" | ||
signature = { version = "2.2.0", features = ["digest"] } | ||
sha1 = { version = "0.10", features = ["oid"] } | ||
sha2 = { version = "0.10", features = ["oid"] } | ||
spki = "0.7.2" | ||
thiserror = "1.0" | ||
|
||
[dev-dependencies] | ||
serial_test = "0.5.1" | ||
testresult = "0.2.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod rsa; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
use cryptoki::mechanism::{ | ||
rsa::{PkcsMgfType, PkcsPssParams}, | ||
Mechanism, MechanismType, | ||
}; | ||
use cryptoki::object::AttributeType; | ||
use der::oid::AssociatedOid; | ||
use signature::digest::Digest; | ||
use std::convert::TryInto; | ||
use thiserror::Error; | ||
|
||
pub mod pkcs1v15; | ||
|
||
pub mod pss; | ||
|
||
#[derive(Debug, Error)] | ||
pub enum Error { | ||
#[error("Cryptoki error: {0}")] | ||
Cryptoki(#[from] cryptoki::error::Error), | ||
|
||
#[error("Private key missing attribute: {0}")] | ||
MissingAttribute(AttributeType), | ||
|
||
#[error("RSA error: {0}")] | ||
Rsa(#[from] rsa::Error), | ||
} | ||
|
||
pub trait DigestSigning: Digest + AssociatedOid { | ||
fn pkcs_mechanism() -> Mechanism<'static>; | ||
|
||
fn pss_mechanism() -> Mechanism<'static>; | ||
} | ||
|
||
macro_rules! impl_digest_signing { | ||
($d:ty, $pkcs_mech:ident, $pss_mech:ident, $mt:ident, $mgf:ident) => { | ||
impl DigestSigning for $d { | ||
fn pkcs_mechanism() -> Mechanism<'static> { | ||
Mechanism::$pkcs_mech | ||
} | ||
|
||
fn pss_mechanism() -> Mechanism<'static> { | ||
Mechanism::$pss_mech(PkcsPssParams { | ||
hash_alg: MechanismType::$mt, | ||
mgf: PkcsMgfType::$mgf, | ||
// Safety: | ||
// the output_size of an hash function will not go over 2^32, | ||
// this unwrap is safe. | ||
s_len: Self::output_size().try_into().unwrap(), | ||
}) | ||
} | ||
} | ||
}; | ||
} | ||
|
||
impl_digest_signing!(sha1::Sha1, Sha1RsaPkcs, Sha1RsaPkcsPss, SHA1, MGF1_SHA1); | ||
impl_digest_signing!( | ||
sha2::Sha256, | ||
Sha256RsaPkcs, | ||
Sha256RsaPkcsPss, | ||
SHA256, | ||
MGF1_SHA256 | ||
); | ||
impl_digest_signing!( | ||
sha2::Sha384, | ||
Sha384RsaPkcs, | ||
Sha384RsaPkcsPss, | ||
SHA384, | ||
MGF1_SHA384 | ||
); | ||
impl_digest_signing!( | ||
sha2::Sha512, | ||
Sha512RsaPkcs, | ||
Sha512RsaPkcsPss, | ||
SHA512, | ||
MGF1_SHA512 | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
use cryptoki::{ | ||
object::{Attribute, AttributeType, KeyType, ObjectClass, ObjectHandle}, | ||
session::Session, | ||
}; | ||
use der::AnyRef; | ||
use rsa::{ | ||
pkcs1, | ||
pkcs1v15::{Signature, VerifyingKey}, | ||
BigUint, RsaPublicKey, | ||
}; | ||
use spki::{AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier}; | ||
use std::convert::TryFrom; | ||
|
||
use super::{DigestSigning, Error}; | ||
|
||
pub struct Signer<D: DigestSigning> { | ||
session: Session, | ||
_public_key: ObjectHandle, | ||
private_key: ObjectHandle, | ||
verifying_key: VerifyingKey<D>, | ||
} | ||
|
||
impl<D: DigestSigning> Signer<D> { | ||
pub fn new(session: Session, label: &[u8]) -> Result<Self, Error> { | ||
// First we'll lookup a private key with that label. | ||
let template = vec![ | ||
Attribute::Token(true), | ||
Attribute::Private(true), | ||
Attribute::Label(label.to_vec()), | ||
Attribute::Class(ObjectClass::PRIVATE_KEY), | ||
Attribute::KeyType(KeyType::RSA), | ||
Attribute::Sign(true), | ||
]; | ||
|
||
let private_key = session.find_objects(&template)?.remove(0); | ||
let attribute_pk = session.get_attributes( | ||
private_key, | ||
&[AttributeType::Modulus, AttributeType::PublicExponent], | ||
)?; | ||
|
||
// Second we'll lookup a public key with the same label/modulus/public exponent | ||
let mut template = vec![ | ||
Attribute::Private(false), | ||
Attribute::Label(label.to_vec()), | ||
Attribute::Class(ObjectClass::PUBLIC_KEY), | ||
Attribute::KeyType(KeyType::RSA), | ||
]; | ||
let mut modulus = None; | ||
let mut public_exponent = None; | ||
for attribute in attribute_pk { | ||
match attribute { | ||
Attribute::Modulus(m) if modulus.is_none() => { | ||
modulus = Some(m.clone()); | ||
template.push(Attribute::Modulus(m)); | ||
} | ||
Attribute::PublicExponent(e) if public_exponent.is_none() => { | ||
public_exponent = Some(e.clone()); | ||
template.push(Attribute::PublicExponent(e)); | ||
} | ||
_ => {} | ||
} | ||
} | ||
|
||
let modulus = modulus | ||
.ok_or(Error::MissingAttribute(AttributeType::Modulus)) | ||
.map(|v| BigUint::from_bytes_be(v.as_slice()))?; | ||
let public_exponent = public_exponent | ||
.ok_or(Error::MissingAttribute(AttributeType::PublicExponent)) | ||
.map(|v| BigUint::from_bytes_be(v.as_slice()))?; | ||
|
||
let public_key = session.find_objects(&template)?.remove(0); | ||
|
||
let verifying_key = VerifyingKey::new(RsaPublicKey::new(modulus, public_exponent)?); | ||
|
||
Ok(Self { | ||
session, | ||
private_key, | ||
_public_key: public_key, | ||
verifying_key, | ||
}) | ||
} | ||
|
||
pub fn into_session(self) -> Session { | ||
self.session | ||
} | ||
} | ||
|
||
impl<D: DigestSigning> AssociatedAlgorithmIdentifier for Signer<D> { | ||
type Params = AnyRef<'static>; | ||
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; | ||
} | ||
|
||
impl<D: DigestSigning> signature::Keypair for Signer<D> { | ||
type VerifyingKey = VerifyingKey<D>; | ||
|
||
fn verifying_key(&self) -> Self::VerifyingKey { | ||
self.verifying_key.clone() | ||
} | ||
} | ||
|
||
impl<D: DigestSigning> signature::Signer<Signature> for Signer<D> { | ||
fn try_sign(&self, msg: &[u8]) -> Result<Signature, signature::Error> { | ||
let bytes = self | ||
.session | ||
.sign(&D::pkcs_mechanism(), self.private_key, msg) | ||
.map_err(Error::Cryptoki) | ||
.map_err(Box::new) | ||
.map_err(signature::Error::from_source)?; | ||
|
||
let signature = Signature::try_from(bytes.as_slice())?; | ||
|
||
Ok(signature) | ||
} | ||
} | ||
|
||
impl<D: DigestSigning> SignatureAlgorithmIdentifier for Signer<D> { | ||
type Params = AnyRef<'static>; | ||
|
||
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = | ||
AlgorithmIdentifierRef { | ||
oid: D::OID, | ||
parameters: Some(AnyRef::NULL), | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
use cryptoki::{ | ||
object::{Attribute, AttributeType, KeyType, ObjectClass, ObjectHandle}, | ||
session::Session, | ||
}; | ||
use der::{asn1::ObjectIdentifier, oid::AssociatedOid, Any, AnyRef}; | ||
use rsa::{ | ||
pkcs1::{self, RsaPssParams}, | ||
pkcs8::{self}, | ||
pss::{Signature, VerifyingKey}, | ||
BigUint, RsaPublicKey, | ||
}; | ||
use signature::digest::Digest; | ||
use spki::{ | ||
AlgorithmIdentifierOwned, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, | ||
DynSignatureAlgorithmIdentifier, | ||
}; | ||
use std::convert::TryFrom; | ||
|
||
use super::{DigestSigning, Error}; | ||
|
||
pub struct Signer<D: DigestSigning> { | ||
session: Session, | ||
_public_key: ObjectHandle, | ||
private_key: ObjectHandle, | ||
verifying_key: VerifyingKey<D>, | ||
salt_len: usize, | ||
} | ||
|
||
impl<D: DigestSigning> Signer<D> { | ||
pub fn new(session: Session, label: &[u8]) -> Result<Self, Error> { | ||
// First we'll lookup a private key with that label. | ||
let template = vec![ | ||
Attribute::Token(true), | ||
Attribute::Private(true), | ||
Attribute::Label(label.to_vec()), | ||
Attribute::Class(ObjectClass::PRIVATE_KEY), | ||
Attribute::KeyType(KeyType::RSA), | ||
Attribute::Sign(true), | ||
]; | ||
|
||
let private_key = session.find_objects(&template)?.remove(0); | ||
let attribute_pk = session.get_attributes( | ||
private_key, | ||
&[AttributeType::Modulus, AttributeType::PublicExponent], | ||
)?; | ||
|
||
// Second we'll lookup a public key with the same label/modulus/public exponent | ||
let mut template = vec![ | ||
Attribute::Private(false), | ||
Attribute::Label(label.to_vec()), | ||
Attribute::Class(ObjectClass::PUBLIC_KEY), | ||
Attribute::KeyType(KeyType::RSA), | ||
]; | ||
let mut modulus = None; | ||
let mut public_exponent = None; | ||
for attribute in attribute_pk { | ||
match attribute { | ||
Attribute::Modulus(m) if modulus.is_none() => { | ||
modulus = Some(m.clone()); | ||
template.push(Attribute::Modulus(m)); | ||
} | ||
Attribute::PublicExponent(e) if public_exponent.is_none() => { | ||
public_exponent = Some(e.clone()); | ||
template.push(Attribute::PublicExponent(e)); | ||
} | ||
_ => {} | ||
} | ||
} | ||
|
||
let modulus = modulus | ||
.ok_or(Error::MissingAttribute(AttributeType::Modulus)) | ||
.map(|v| BigUint::from_bytes_be(v.as_slice()))?; | ||
let public_exponent = public_exponent | ||
.ok_or(Error::MissingAttribute(AttributeType::PublicExponent)) | ||
.map(|v| BigUint::from_bytes_be(v.as_slice()))?; | ||
|
||
let public_key = session.find_objects(&template)?.remove(0); | ||
|
||
let verifying_key = VerifyingKey::new(RsaPublicKey::new(modulus, public_exponent)?); | ||
let salt_len = <D as Digest>::output_size(); | ||
|
||
Ok(Self { | ||
session, | ||
private_key, | ||
_public_key: public_key, | ||
verifying_key, | ||
salt_len, | ||
}) | ||
} | ||
|
||
pub fn into_session(self) -> Session { | ||
self.session | ||
} | ||
} | ||
|
||
impl<D: DigestSigning> AssociatedAlgorithmIdentifier for Signer<D> { | ||
type Params = AnyRef<'static>; | ||
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; | ||
} | ||
|
||
impl<D: DigestSigning> signature::Keypair for Signer<D> { | ||
type VerifyingKey = VerifyingKey<D>; | ||
|
||
fn verifying_key(&self) -> Self::VerifyingKey { | ||
self.verifying_key.clone() | ||
} | ||
} | ||
|
||
impl<D: DigestSigning> signature::Signer<Signature> for Signer<D> { | ||
fn try_sign(&self, msg: &[u8]) -> Result<Signature, signature::Error> { | ||
let bytes = self | ||
.session | ||
.sign(&D::pss_mechanism(), self.private_key, msg) | ||
.map_err(Error::Cryptoki) | ||
.map_err(Box::new) | ||
.map_err(signature::Error::from_source)?; | ||
|
||
let signature = Signature::try_from(bytes.as_slice())?; | ||
|
||
Ok(signature) | ||
} | ||
} | ||
|
||
impl<D: DigestSigning> DynSignatureAlgorithmIdentifier for Signer<D> { | ||
fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result<AlgorithmIdentifierOwned> { | ||
get_pss_signature_algo_id::<D>(self.salt_len as u8) | ||
} | ||
} | ||
|
||
fn get_pss_signature_algo_id<D>(salt_len: u8) -> pkcs8::spki::Result<AlgorithmIdentifierOwned> | ||
where | ||
D: Digest + AssociatedOid, | ||
{ | ||
const ID_RSASSA_PSS: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10"); | ||
|
||
let pss_params = RsaPssParams::new::<D>(salt_len); | ||
|
||
Ok(AlgorithmIdentifierOwned { | ||
oid: ID_RSASSA_PSS, | ||
parameters: Some(Any::encode_from(&pss_params)?), | ||
}) | ||
} |
Oops, something went wrong.