-
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
8 changed files
with
593 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,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" |
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,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 | ||
} | ||
} |
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,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) | ||
} | ||
} |
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,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(()) | ||
} |
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,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) | ||
} |