From 8075aabddeff109e298a6caecbe568764496fd70 Mon Sep 17 00:00:00 2001 From: jbis9051 Date: Sat, 23 Sep 2023 23:57:43 -0400 Subject: [PATCH] Adds support for Es256, Es384, Rs256, Rs384, Rs512 --- Cargo.toml | 8 +- src/algorithm/rust_crypto.rs | 222 +++++++++++++++++++++++++++++++---- 2 files changed, 208 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4865ea2f..830870ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,9 +19,15 @@ base64 = "0.13" crypto-common = "0.1" digest = "0.10" hmac = { version = "0.12", features = ["reset"] } -sha2 = "0.10" +sha2 = { version = "0.10" , features = ["oid"]} serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +p256 = "0.13" +p384 = "0.13" +p521 = "0.13" +rsa = "0.9" +signature = "2.1" + [dependencies.openssl] version = "0.10" diff --git a/src/algorithm/rust_crypto.rs b/src/algorithm/rust_crypto.rs index ed7cc53d..93c071e5 100644 --- a/src/algorithm/rust_crypto.rs +++ b/src/algorithm/rust_crypto.rs @@ -7,13 +7,15 @@ use digest::{ consts::U256, core_api::{BlockSizeUser, BufferKindUser, CoreProxy, FixedOutputCore}, generic_array::typenum::{IsLess, Le, NonZero}, - HashMarker, + HashMarker, Digest, }; use hmac::{Hmac, Mac}; - +use std::marker::PhantomData; use crate::algorithm::{AlgorithmType, SigningAlgorithm, VerifyingAlgorithm}; use crate::error::Error; use crate::SEPARATOR; +use signature::{DigestSigner, DigestVerifier, SignatureEncoding}; + /// A trait used to make the implementation of `SigningAlgorithm` and /// `VerifyingAlgorithm` easier. /// RustCrypto crates tend to have algorithm types defined at the type level, @@ -37,16 +39,16 @@ type_level_algorithm_type!(sha2::Sha384, AlgorithmType::Hs384); type_level_algorithm_type!(sha2::Sha512, AlgorithmType::Hs512); impl SigningAlgorithm for Hmac -where - D: CoreProxy + TypeLevelAlgorithmType, - D::Core: HashMarker - + BufferKindUser + where + D: CoreProxy + TypeLevelAlgorithmType, + D::Core: HashMarker + + BufferKindUser + FixedOutputCore + digest::Reset + Default + Clone, - ::BlockSize: IsLess, - Le<::BlockSize, U256>: NonZero, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, { fn algorithm_type(&self) -> AlgorithmType { D::algorithm_type() @@ -60,17 +62,113 @@ where } } + +pub struct AsymmetricAuthentication (SignatureScheme, PhantomData, PhantomData); + +impl AsymmetricAuthentication { + pub fn new(scheme: SignatureScheme) -> Self { + AsymmetricAuthentication(scheme, PhantomData, PhantomData) + } +} + +pub struct AsymmetricAuthenticationBuilder(PhantomData); + +impl AsymmetricAuthenticationBuilder { + pub fn build(scheme: G) -> AsymmetricAuthentication:: { + AsymmetricAuthentication::new(scheme) + } +} + +macro_rules! type_level_Asymmetric_algorithm_type { + ($hash_type: ty, $signature_scheme: ty, $output: ty, $algorithm_type: expr) => { + impl TypeLevelAlgorithmType for AsymmetricAuthentication<$hash_type, $signature_scheme, $output> { + fn algorithm_type() -> AlgorithmType { + $algorithm_type + } + } + }; +} + +type_level_Asymmetric_algorithm_type!(sha2::Sha256, p256::ecdsa::SigningKey, p256::ecdsa::Signature, AlgorithmType::Es256); +type_level_Asymmetric_algorithm_type!(sha2::Sha256, p256::ecdsa::VerifyingKey, p256::ecdsa::Signature, AlgorithmType::Es256); + +type_level_Asymmetric_algorithm_type!(sha2::Sha384, p384::ecdsa::SigningKey, p384::ecdsa::Signature, AlgorithmType::Es384); +type_level_Asymmetric_algorithm_type!(sha2::Sha384, p384::ecdsa::VerifyingKey, p384::ecdsa::Signature, AlgorithmType::Es384); + +// TODO: Es512 once p521 is implemented + +type_level_Asymmetric_algorithm_type!(sha2::Sha256, rsa::pkcs1v15::SigningKey, rsa::pkcs1v15::Signature, AlgorithmType::Rs256); +type_level_Asymmetric_algorithm_type!(sha2::Sha256, rsa::pkcs1v15::VerifyingKey, rsa::pkcs1v15::Signature, AlgorithmType::Rs256); + +type_level_Asymmetric_algorithm_type!(sha2::Sha384, rsa::pkcs1v15::SigningKey, rsa::pkcs1v15::Signature, AlgorithmType::Rs384); +type_level_Asymmetric_algorithm_type!(sha2::Sha384, rsa::pkcs1v15::VerifyingKey, rsa::pkcs1v15::Signature, AlgorithmType::Rs384); + +type_level_Asymmetric_algorithm_type!(sha2::Sha512, rsa::pkcs1v15::SigningKey, rsa::pkcs1v15::Signature, AlgorithmType::Rs512); +type_level_Asymmetric_algorithm_type!(sha2::Sha512, rsa::pkcs1v15::VerifyingKey, rsa::pkcs1v15::Signature, AlgorithmType::Rs512); + +// TODO: Ps256, Ps384, Ps512 + + +impl SigningAlgorithm for AsymmetricAuthentication + where + Self: TypeLevelAlgorithmType, + SignatureScheme: DigestSigner, + HashAlgo: Digest, + S: SignatureEncoding + std::fmt::Debug +{ + fn algorithm_type(&self) -> AlgorithmType { + ::algorithm_type() + } + + fn sign(&self, header: &str, claims: &str) -> Result { + let mut hash = HashAlgo::new(); + hash.update(header.as_bytes()); + hash.update(SEPARATOR.as_bytes()); + hash.update(claims.as_bytes()); + + let signature = self.0.sign_digest(hash); + let code = signature.to_bytes(); + Ok(base64::encode_config(code, base64::URL_SAFE_NO_PAD)) + } +} + + +impl VerifyingAlgorithm for AsymmetricAuthentication + where + Self: TypeLevelAlgorithmType, + SignatureScheme: DigestVerifier, + HashAlgo: Digest, + S: SignatureEncoding +{ + fn algorithm_type(&self) -> AlgorithmType { + ::algorithm_type() + } + + fn verify_bytes(&self, header: &str, claims: &str, signature: &[u8]) -> Result { + let mut hash = HashAlgo::new(); + hash.update(header.as_bytes()); + hash.update(SEPARATOR.as_bytes()); + hash.update(claims.as_bytes()); + + let sig = S::try_from(signature).map_err(|_| Error::InvalidSignature)?; + + self.0.verify_digest(hash, &sig).map_err(|_| Error::InvalidSignature)?; + Ok(true) + } +} + + impl VerifyingAlgorithm for Hmac -where - D: CoreProxy + TypeLevelAlgorithmType, - D::Core: HashMarker - + BufferKindUser + where + D: CoreProxy + TypeLevelAlgorithmType, + D::Core: HashMarker + + BufferKindUser + FixedOutputCore + digest::Reset + Default + Clone, - ::BlockSize: IsLess, - Le<::BlockSize, U256>: NonZero, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, { fn algorithm_type(&self) -> AlgorithmType { D::algorithm_type() @@ -84,16 +182,16 @@ where } fn get_hmac_with_data(hmac: &Hmac, header: &str, claims: &str) -> Hmac -where - D: CoreProxy, - D::Core: HashMarker - + BufferKindUser + where + D: CoreProxy, + D::Core: HashMarker + + BufferKindUser + FixedOutputCore + digest::Reset + Default + Clone, - ::BlockSize: IsLess, - Le<::BlockSize, U256>: NonZero, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, { let mut hmac = hmac.clone(); hmac.reset(); @@ -106,9 +204,16 @@ where #[cfg(test)] mod tests { use crate::algorithm::{SigningAlgorithm, VerifyingAlgorithm}; + use crate::algorithm::rust_crypto::{AsymmetricAuthenticationBuilder}; use crate::error::Error; use hmac::{Hmac, Mac}; + use p256::ecdsa::{SigningKey}; + use p256::pkcs8::{DecodePrivateKey}; + use rsa::pkcs1::DecodeRsaPrivateKey; use sha2::Sha256; + use rsa::{RsaPrivateKey}; + use signature::Keypair; + #[test] pub fn sign() -> Result<(), Error> { @@ -131,8 +236,83 @@ mod tests { let verifier: Hmac = Hmac::new_from_slice(b"secret")?; assert!(VerifyingAlgorithm::verify( - &verifier, header, claims, signature + &verifier, header, claims, signature, )?); Ok(()) } + + #[test] + pub fn sign_asymmetric_ec() -> Result<(), Error> { + let private_key = include_str!("../../test/es256-private-2.pem"); + let signing_key = SigningKey::from_pkcs8_pem(private_key).unwrap(); + + let signer = AsymmetricAuthenticationBuilder::::build(signing_key); + + let header = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9"; + let claims = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0"; + let expected_signature = "dnpKzqJ1nYJc0BNCCwF0knVd5RQEX9abZrDETQrXfO_mZ9k8FMSMGb4Y8EF2OgAPw3HGCQ-gOn7TM6QPjXBvtA"; + + let computed_signature = SigningAlgorithm::sign(&signer, header, claims)?; + + assert_eq!(computed_signature, expected_signature); + + Ok(()) + } + + #[test] + pub fn verify_asymmetric_ec() -> Result<(), Error> { + let private_key = include_str!("../../test/es256-private-2.pem"); + let signing_key = SigningKey::from_pkcs8_pem(private_key).unwrap(); + let verifying_key = *signing_key.verifying_key(); + + + let verifer = AsymmetricAuthenticationBuilder::::build(verifying_key); + + let header = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9"; + let claims = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0"; + let signature = "dnpKzqJ1nYJc0BNCCwF0knVd5RQEX9abZrDETQrXfO_mZ9k8FMSMGb4Y8EF2OgAPw3HGCQ-gOn7TM6QPjXBvtA"; + + assert!(VerifyingAlgorithm::verify(&verifer, header, claims, signature)?); + + + Ok(()) + } + + #[test] + pub fn sign_asymmetric_rsa_pkcs1v15() -> Result<(), Error> { + let private_key = include_str!("../../test/rs256-private-3.pem"); + let private_key = RsaPrivateKey::from_pkcs1_pem(private_key).unwrap(); + let signing_key = rsa::pkcs1v15::SigningKey::new(private_key); + + + let signer = AsymmetricAuthenticationBuilder::::build(signing_key); + + let header = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"; + let claims = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0"; + let expected_signature = "PI6Ji_Nh9-j8Q_BvtE3yJLvVOUugUuDMB3wau7gFN6ZMxg8M2_jlJhK8MnbPT-6_VgZBI63Lm_2KsCGSHvqiEPVK9io5yJdgSjpHvuzQnsbZyTvtKJrsZCeI_whuUPCKOlqoDLxzmYAegqYb_fmpkJ8odfhNdO3CGQIh0lL1hWKF27TBB095o5NfYm52OE2LjmTPltixGgoiLBsmrOCHiXscjAZrppG0cOB5q9BbZhYIQIbTykL9bTXqwe-QEaJefKp6lpp36M0pPsPTqMf00wNQZSl7_iFUf_4IPAiGQrUHgNqGzbBLJz_i651AfXlpL7u85vbjFUyIDqASN5csYw"; + + let computed_signature = SigningAlgorithm::sign(&signer, header, claims)?; + + assert_eq!(computed_signature, expected_signature); + + Ok(()) + } + + #[test] + pub fn verify_asymmetric_rsa_pkcs1v15() -> Result<(), Error> { + let private_key = include_str!("../../test/rs256-private-3.pem"); + let private_key = RsaPrivateKey::from_pkcs1_pem(private_key).unwrap(); + let signing_key = rsa::pkcs1v15::SigningKey::new(private_key); + let verifying_key = signing_key.verifying_key(); + + let verifer = AsymmetricAuthenticationBuilder::::build(verifying_key); + + let header = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"; + let claims = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0"; + let signature = "PI6Ji_Nh9-j8Q_BvtE3yJLvVOUugUuDMB3wau7gFN6ZMxg8M2_jlJhK8MnbPT-6_VgZBI63Lm_2KsCGSHvqiEPVK9io5yJdgSjpHvuzQnsbZyTvtKJrsZCeI_whuUPCKOlqoDLxzmYAegqYb_fmpkJ8odfhNdO3CGQIh0lL1hWKF27TBB095o5NfYm52OE2LjmTPltixGgoiLBsmrOCHiXscjAZrppG0cOB5q9BbZhYIQIbTykL9bTXqwe-QEaJefKp6lpp36M0pPsPTqMf00wNQZSl7_iFUf_4IPAiGQrUHgNqGzbBLJz_i651AfXlpL7u85vbjFUyIDqASN5csYw"; + + assert!(VerifyingAlgorithm::verify(&verifer, header, claims, signature)?); + + Ok(()) + } }