From a3f5248334106ee1cc95601857ea39270d2a1ad7 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Wed, 22 Nov 2023 20:42:55 -0800 Subject: [PATCH] rustcrypto: initial commit Signed-off-by: Arthur Gautier --- Cargo.lock | 334 +++++++++++++++++++++++- Cargo.toml | 2 +- cryptoki-rustcrypto/Cargo.toml | 24 ++ cryptoki-rustcrypto/src/lib.rs | 1 + cryptoki-rustcrypto/src/rsa/mod.rs | 75 ++++++ cryptoki-rustcrypto/src/rsa/pkcs1v15.rs | 124 +++++++++ cryptoki-rustcrypto/src/rsa/pss.rs | 142 ++++++++++ cryptoki-rustcrypto/tests/common.rs | 43 +++ cryptoki-rustcrypto/tests/rsa.rs | 120 +++++++++ 9 files changed, 862 insertions(+), 3 deletions(-) create mode 100644 cryptoki-rustcrypto/Cargo.toml create mode 100644 cryptoki-rustcrypto/src/lib.rs create mode 100644 cryptoki-rustcrypto/src/rsa/mod.rs create mode 100644 cryptoki-rustcrypto/src/rsa/pkcs1v15.rs create mode 100644 cryptoki-rustcrypto/src/rsa/pss.rs create mode 100644 cryptoki-rustcrypto/tests/common.rs create mode 100644 cryptoki-rustcrypto/tests/rsa.rs diff --git a/Cargo.lock b/Cargo.lock index b6f41892..b4ec0d79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bindgen" version = "0.66.1" @@ -52,6 +58,21 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cc" version = "1.0.83" @@ -96,6 +117,31 @@ dependencies = [ "cc", ] +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "cryptoki" version = "0.6.1" @@ -113,6 +159,22 @@ dependencies = [ "testresult", ] +[[package]] +name = "cryptoki-rustcrypto" +version = "0.1.0" +dependencies = [ + "cryptoki", + "der", + "rsa", + "serial_test", + "sha1", + "sha2", + "signature", + "spki", + "testresult", + "thiserror", +] + [[package]] name = "cryptoki-sys" version = "0.1.7" @@ -121,6 +183,28 @@ dependencies = [ "libloading", ] +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", +] + [[package]] name = "either" version = "1.9.0" @@ -148,6 +232,27 @@ dependencies = [ "libc", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "glob" version = "0.3.1" @@ -183,6 +288,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] [[package]] name = "lazycell" @@ -192,9 +300,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" @@ -206,6 +314,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "linux-raw-sys" version = "0.4.8" @@ -250,6 +364,44 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.16" @@ -257,6 +409,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -302,6 +455,42 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "prettyplease" version = "0.2.15" @@ -354,6 +543,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -392,6 +610,26 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "rsa" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a3211b01eea83d80687da9eef70e39d65144a3894866a5153a2723e425a157f" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -478,18 +716,72 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + [[package]] name = "smallvec" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.109" @@ -518,12 +810,44 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "649113ab56eab59f78f02314a05b24bda0200d322c9eb1c60d0af8554e94c5ef" +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "walkdir" version = "2.4.0" @@ -534,6 +858,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "which" version = "4.4.2" diff --git a/Cargo.toml b/Cargo.toml index 99938e6c..740a8e6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["cryptoki", "cryptoki-sys"] +members = ["cryptoki", "cryptoki-sys", "cryptoki-rustcrypto"] diff --git a/cryptoki-rustcrypto/Cargo.toml b/cryptoki-rustcrypto/Cargo.toml new file mode 100644 index 00000000..dc35c4d7 --- /dev/null +++ b/cryptoki-rustcrypto/Cargo.toml @@ -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" diff --git a/cryptoki-rustcrypto/src/lib.rs b/cryptoki-rustcrypto/src/lib.rs new file mode 100644 index 00000000..5ba72a60 --- /dev/null +++ b/cryptoki-rustcrypto/src/lib.rs @@ -0,0 +1 @@ +pub mod rsa; diff --git a/cryptoki-rustcrypto/src/rsa/mod.rs b/cryptoki-rustcrypto/src/rsa/mod.rs new file mode 100644 index 00000000..07ff3185 --- /dev/null +++ b/cryptoki-rustcrypto/src/rsa/mod.rs @@ -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 +); diff --git a/cryptoki-rustcrypto/src/rsa/pkcs1v15.rs b/cryptoki-rustcrypto/src/rsa/pkcs1v15.rs new file mode 100644 index 00000000..da869804 --- /dev/null +++ b/cryptoki-rustcrypto/src/rsa/pkcs1v15.rs @@ -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 { + session: Session, + _public_key: ObjectHandle, + private_key: ObjectHandle, + verifying_key: VerifyingKey, +} + +impl Signer { + pub fn new(session: Session, label: &[u8]) -> Result { + // 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 AssociatedAlgorithmIdentifier for Signer { + type Params = AnyRef<'static>; + const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; +} + +impl signature::Keypair for Signer { + type VerifyingKey = VerifyingKey; + + fn verifying_key(&self) -> Self::VerifyingKey { + self.verifying_key.clone() + } +} + +impl signature::Signer for Signer { + fn try_sign(&self, msg: &[u8]) -> Result { + 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 SignatureAlgorithmIdentifier for Signer { + type Params = AnyRef<'static>; + + const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = + AlgorithmIdentifierRef { + oid: D::OID, + parameters: Some(AnyRef::NULL), + }; +} diff --git a/cryptoki-rustcrypto/src/rsa/pss.rs b/cryptoki-rustcrypto/src/rsa/pss.rs new file mode 100644 index 00000000..1764a236 --- /dev/null +++ b/cryptoki-rustcrypto/src/rsa/pss.rs @@ -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 { + session: Session, + _public_key: ObjectHandle, + private_key: ObjectHandle, + verifying_key: VerifyingKey, + salt_len: usize, +} + +impl Signer { + pub fn new(session: Session, label: &[u8]) -> Result { + // 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 = ::output_size(); + + Ok(Self { + session, + private_key, + _public_key: public_key, + verifying_key, + salt_len, + }) + } + + pub fn into_session(self) -> Session { + self.session + } +} + +impl AssociatedAlgorithmIdentifier for Signer { + type Params = AnyRef<'static>; + const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; +} + +impl signature::Keypair for Signer { + type VerifyingKey = VerifyingKey; + + fn verifying_key(&self) -> Self::VerifyingKey { + self.verifying_key.clone() + } +} + +impl signature::Signer for Signer { + fn try_sign(&self, msg: &[u8]) -> Result { + 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 DynSignatureAlgorithmIdentifier for Signer { + fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result { + get_pss_signature_algo_id::(self.salt_len as u8) + } +} + +fn get_pss_signature_algo_id(salt_len: u8) -> pkcs8::spki::Result +where + D: Digest + AssociatedOid, +{ + const ID_RSASSA_PSS: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10"); + + let pss_params = RsaPssParams::new::(salt_len); + + Ok(AlgorithmIdentifierOwned { + oid: ID_RSASSA_PSS, + parameters: Some(Any::encode_from(&pss_params)?), + }) +} diff --git a/cryptoki-rustcrypto/tests/common.rs b/cryptoki-rustcrypto/tests/common.rs new file mode 100644 index 00000000..95e4202d --- /dev/null +++ b/cryptoki-rustcrypto/tests/common.rs @@ -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) +} diff --git a/cryptoki-rustcrypto/tests/rsa.rs b/cryptoki-rustcrypto/tests/rsa.rs new file mode 100644 index 00000000..4f56dbcc --- /dev/null +++ b/cryptoki-rustcrypto/tests/rsa.rs @@ -0,0 +1,120 @@ +// 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, pss}; +use serial_test::serial; +use signature::{Keypair, Signer, Verifier}; +use testresult::TestResult; + +#[test] +#[serial] +fn 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 = 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::::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(()) +} + +#[test] +#[serial] +fn pss_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 = 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 = pss::Signer::::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(()) +}