Skip to content

Commit

Permalink
rustcrypto: initial commit
Browse files Browse the repository at this point in the history
Signed-off-by: Arthur Gautier <[email protected]>
  • Loading branch information
baloo committed Nov 23, 2023
1 parent 6b81342 commit a3f5248
Show file tree
Hide file tree
Showing 9 changed files with 862 additions and 3 deletions.
334 changes: 332 additions & 2 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
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"]
24 changes: 24 additions & 0 deletions cryptoki-rustcrypto/Cargo.toml
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"
1 change: 1 addition & 0 deletions cryptoki-rustcrypto/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod rsa;
75 changes: 75 additions & 0 deletions cryptoki-rustcrypto/src/rsa/mod.rs
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
);
124 changes: 124 additions & 0 deletions cryptoki-rustcrypto/src/rsa/pkcs1v15.rs
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),
};
}
142 changes: 142 additions & 0 deletions cryptoki-rustcrypto/src/rsa/pss.rs
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)?),
})
}
Loading

0 comments on commit a3f5248

Please sign in to comment.