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 83ef190
Show file tree
Hide file tree
Showing 8 changed files with 593 additions and 3 deletions.
322 changes: 320 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"]
20 changes: 20 additions & 0 deletions cryptoki-rustcrypto/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "cryptoki-rustcrypto"
version = "0.1.0"
edition = "2018"
authors = ["Contributors to the Parsec project"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
cryptoki = { path = "../cryptoki", version = "0.6.1" }
der = "0.7.8"
rsa = "0.9"
signature = { version = "2.2.0", features = ["digest"] }
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;
15 changes: 15 additions & 0 deletions cryptoki-rustcrypto/src/rsa/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use cryptoki::mechanism::Mechanism;
use der::oid::AssociatedOid;
use signature::digest::Digest;

pub mod pkcs1v15;

pub trait DigestSigning: Digest + AssociatedOid {
fn pkcs_mechanism() -> Mechanism<'static>;
}

impl DigestSigning for sha2::Sha256 {
fn pkcs_mechanism() -> Mechanism<'static> {
Mechanism::Sha256RsaPkcs
}
}
127 changes: 127 additions & 0 deletions cryptoki-rustcrypto/src/rsa/pkcs1v15.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
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};
use std::convert::TryFrom;
use thiserror::Error;

use super::DigestSigning;

#[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 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 = rsa::pkcs1v15::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)
}
}
66 changes: 66 additions & 0 deletions cryptoki-rustcrypto/tests/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2021 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0
mod common;

use crate::common::USER_PIN;
use common::init_pins;
use cryptoki::{mechanism::Mechanism, object::Attribute, session::UserType, types::AuthPin};
use cryptoki_rustcrypto::rsa::pkcs1v15;
use serial_test::serial;
use signature::{Keypair, Signer, Verifier};
use testresult::TestResult;

#[test]
#[serial]
fn rsa_pkcs1v15_sign_verify() -> TestResult {
let (pkcs11, slot) = init_pins();

// open a session
let session = pkcs11.open_rw_session(slot)?;

// log in the session
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;

// get mechanism
let mechanism = Mechanism::RsaPkcsKeyPairGen;

let public_exponent: Vec<u8> = vec![0x01, 0x00, 0x01];
let modulus_bits = 1024;

let label = b"demo-signer";

// pub key template
let pub_key_template = vec![
Attribute::Token(true),
Attribute::Private(false),
Attribute::Label(label.to_vec()),
Attribute::PublicExponent(public_exponent),
Attribute::ModulusBits(modulus_bits.into()),
];

// priv key template
let priv_key_template = vec![Attribute::Token(true), Attribute::Label(label.to_vec())];

// generate a key pair
let (public, private) =
session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?;

// data to sign
let data = [0xFF, 0x55, 0xDD];

let signer =
pkcs1v15::Signer::<sha2::Sha256>::new(session, label).expect("Lookup keys from HSM");

let signature = signer.sign(&data);

let verifying_key = signer.verifying_key();
verifying_key.verify(&data, &signature)?;

let session = signer.into_session();

// delete keys
session.destroy_object(public)?;
session.destroy_object(private)?;

Ok(())
}
43 changes: 43 additions & 0 deletions cryptoki-rustcrypto/tests/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2021 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0
use cryptoki::context::{CInitializeArgs, Pkcs11};
use cryptoki::session::UserType;
use cryptoki::slot::Slot;
use cryptoki::types::AuthPin;
use std::env;

// The default user pin
pub static USER_PIN: &str = "fedcba";
// The default SO pin
pub static SO_PIN: &str = "abcdef";

pub fn get_pkcs11() -> Pkcs11 {
Pkcs11::new(
env::var("PKCS11_SOFTHSM2_MODULE")
.unwrap_or_else(|_| "/usr/local/lib/softhsm/libsofthsm2.so".to_string()),
)
.unwrap()
}

pub fn init_pins() -> (Pkcs11, Slot) {
let pkcs11 = get_pkcs11();

// initialize the library
pkcs11.initialize(CInitializeArgs::OsThreads).unwrap();

// find a slot, get the first one
let slot = pkcs11.get_slots_with_token().unwrap().remove(0);

let so_pin = AuthPin::new(SO_PIN.into());
pkcs11.init_token(slot, &so_pin, "Test Token").unwrap();

{
// open a session
let session = pkcs11.open_rw_session(slot).unwrap();
// log in the session
session.login(UserType::So, Some(&so_pin)).unwrap();
session.init_pin(&AuthPin::new(USER_PIN.into())).unwrap();
}

(pkcs11, slot)
}

0 comments on commit 83ef190

Please sign in to comment.