From db2a2cd7c9c6bace809e239e995cd1c49b487722 Mon Sep 17 00:00:00 2001 From: rster2002 Date: Tue, 1 Apr 2025 21:21:20 +0200 Subject: [PATCH 01/17] =?UTF-8?q?=F0=9F=9A=A7=20Start=20working=20out=20so?= =?UTF-8?q?me=20ideas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules.rs | 3 ++- src/modules/algorithm/mod.rs | 4 ++-- .../algorithm/models/rs256_algorithm.rs | 21 +++++++++++++++---- .../rs256_algorithm/rs256_public_params.rs | 7 +++++++ src/modules/algorithm/traits.rs | 4 +++- src/modules/algorithm/traits/jw_alg.rs | 2 +- .../algorithm/traits/private_jwa_params.rs | 7 +++++++ .../algorithm/traits/public_jwa_params.rs | 8 +++++++ src/modules/key/mod.rs | 20 ++++++++++++++++++ src/modules/key/models.rs | 1 + src/modules/key/models/jwk.rs | 1 + src/modules/key/traits.rs | 1 + 12 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 src/modules/algorithm/models/rs256_algorithm/rs256_public_params.rs create mode 100644 src/modules/algorithm/traits/private_jwa_params.rs create mode 100644 src/modules/algorithm/traits/public_jwa_params.rs create mode 100644 src/modules/key/mod.rs create mode 100644 src/modules/key/models.rs create mode 100644 src/modules/key/models/jwk.rs create mode 100644 src/modules/key/traits.rs diff --git a/src/modules.rs b/src/modules.rs index 123cf88..806cf61 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -1,2 +1,3 @@ pub mod token; -pub mod algorithm; \ No newline at end of file +pub mod algorithm; +pub mod key; diff --git a/src/modules/algorithm/mod.rs b/src/modules/algorithm/mod.rs index 72bd57e..2d7df68 100644 --- a/src/modules/algorithm/mod.rs +++ b/src/modules/algorithm/mod.rs @@ -7,9 +7,9 @@ pub use models::none_algorithm::NoneAlgorithm; pub use models::hs256_algorithm::HS256Algorithm; #[cfg(feature = "rs256")] -pub use models::rs256_algorithm::RS256Algorithm; +pub use models::rs256_algorithm::{RS256Algorithm, rs256_public_params::RS256PublicParams}; #[cfg(feature = "es256")] pub use models::es256_algorithm::ES256Algorithm; -pub use traits::jw_alg::JwAlg; \ No newline at end of file +pub use traits::jw_alg::JwAlg; diff --git a/src/modules/algorithm/models/rs256_algorithm.rs b/src/modules/algorithm/models/rs256_algorithm.rs index ae604c5..1a77a3f 100644 --- a/src/modules/algorithm/models/rs256_algorithm.rs +++ b/src/modules/algorithm/models/rs256_algorithm.rs @@ -1,3 +1,5 @@ +pub mod rs256_public_params; + use std::fmt::{Debug, Formatter}; pub use rsa::pkcs1::DecodeRsaPrivateKey; use rsa::pkcs1v15::{Signature, SigningKey}; @@ -5,6 +7,8 @@ use rsa::signature::{Keypair, SignatureEncoding, Signer, Verifier}; use serde::{Deserialize, Serialize}; use sha2::Sha256; use crate::algorithm::JwAlg; +use crate::algorithm::models::rs256_algorithm::rs256_public_params::RS256PublicParams; +use crate::algorithm::traits::public_jwa_params::PublicJwaParams; #[derive(Clone)] pub struct RS256Algorithm { @@ -19,6 +23,12 @@ impl RS256Algorithm { } } +impl Debug for RS256Algorithm { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "RS256Algorithm {{ .. }}") + } +} + impl JwAlg for RS256Algorithm { type Error = rsa::signature::Error; @@ -37,9 +47,12 @@ impl JwAlg for RS256Algorithm { } } -impl Debug for RS256Algorithm { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "RS256Algorithm {{ .. }}") +impl PublicJwaParams for RS256Algorithm { + type Params = RS256PublicParams; + + fn public_params(&self) -> Self::Params { + todo!() + // self.inner. } } @@ -69,4 +82,4 @@ mod tests { assert!(verify); } -} \ No newline at end of file +} diff --git a/src/modules/algorithm/models/rs256_algorithm/rs256_public_params.rs b/src/modules/algorithm/models/rs256_algorithm/rs256_public_params.rs new file mode 100644 index 0000000..b16c858 --- /dev/null +++ b/src/modules/algorithm/models/rs256_algorithm/rs256_public_params.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct RS256PublicParams { + pub n: String, + pub e: String, +} diff --git a/src/modules/algorithm/traits.rs b/src/modules/algorithm/traits.rs index de80376..11bef56 100644 --- a/src/modules/algorithm/traits.rs +++ b/src/modules/algorithm/traits.rs @@ -1 +1,3 @@ -pub mod jw_alg; \ No newline at end of file +pub mod jw_alg; +pub mod public_jwa_params; +pub mod private_jwa_params; diff --git a/src/modules/algorithm/traits/jw_alg.rs b/src/modules/algorithm/traits/jw_alg.rs index 0ef5d4d..3c436b1 100644 --- a/src/modules/algorithm/traits/jw_alg.rs +++ b/src/modules/algorithm/traits/jw_alg.rs @@ -4,4 +4,4 @@ pub trait JwAlg { fn alg() -> impl AsRef; fn sign(&self, payload: &str) -> Vec; fn verify(&self, payload: &str, signature: &[u8]) -> Result; -} \ No newline at end of file +} diff --git a/src/modules/algorithm/traits/private_jwa_params.rs b/src/modules/algorithm/traits/private_jwa_params.rs new file mode 100644 index 0000000..db4ab78 --- /dev/null +++ b/src/modules/algorithm/traits/private_jwa_params.rs @@ -0,0 +1,7 @@ +use serde::de::DeserializeOwned; +use serde::Serialize; +use crate::algorithm::traits::public_jwa_params::PublicJwaParams; + +pub trait PrivateJwaParams: PublicJwaParams { + type Params: Serialize + DeserializeOwned; +} diff --git a/src/modules/algorithm/traits/public_jwa_params.rs b/src/modules/algorithm/traits/public_jwa_params.rs new file mode 100644 index 0000000..f2eeffc --- /dev/null +++ b/src/modules/algorithm/traits/public_jwa_params.rs @@ -0,0 +1,8 @@ +use serde::de::DeserializeOwned; +use serde::Serialize; + +pub trait PublicJwaParams { + type Params: Serialize + DeserializeOwned; + + fn public_params(&self) -> Self::Params; +} diff --git a/src/modules/key/mod.rs b/src/modules/key/mod.rs new file mode 100644 index 0000000..efcfe9e --- /dev/null +++ b/src/modules/key/mod.rs @@ -0,0 +1,20 @@ +mod models; +mod traits; + +#[cfg(all(test, feature = "rs256"))] +mod tests { + use pkcs1::DecodeRsaPrivateKey; + use rsa::pkcs1v15::SigningKey; + use rsa::RsaPrivateKey; + use crate::algorithm::{HS256Algorithm, RS256Algorithm}; + + #[test] + fn jwk_is_created_correctly() { + let private_key = RsaPrivateKey::from_pkcs1_pem(include_str!("../../../test-files/rs256.key")).unwrap(); + + let signing_key = SigningKey::new(private_key); + let alg = RS256Algorithm::new(signing_key); + + + } +} diff --git a/src/modules/key/models.rs b/src/modules/key/models.rs new file mode 100644 index 0000000..3c693be --- /dev/null +++ b/src/modules/key/models.rs @@ -0,0 +1 @@ +mod jwk; diff --git a/src/modules/key/models/jwk.rs b/src/modules/key/models/jwk.rs new file mode 100644 index 0000000..d853e45 --- /dev/null +++ b/src/modules/key/models/jwk.rs @@ -0,0 +1 @@ +pub struct Jwk; diff --git a/src/modules/key/traits.rs b/src/modules/key/traits.rs new file mode 100644 index 0000000..531db74 --- /dev/null +++ b/src/modules/key/traits.rs @@ -0,0 +1 @@ +mod jw_key; From dfb69beec164ba2f040345178421dd386a24d03a Mon Sep 17 00:00:00 2001 From: rster2002 Date: Thu, 3 Apr 2025 23:46:53 +0200 Subject: [PATCH 02/17] =?UTF-8?q?=F0=9F=9A=A7=20Start=20adding=20key=20typ?= =?UTF-8?q?e=20representations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/algorithm/models/es256_algorithm.rs | 9 ++++++++- src/modules/algorithm/models/hs256_algorithm.rs | 9 ++++++++- src/modules/algorithm/models/none_algorithm.rs | 2 +- src/modules/algorithm/models/rs256_algorithm.rs | 10 ++++------ src/modules/algorithm/traits/private_jwa_params.rs | 7 ------- src/modules/algorithm/traits/public_jwa_params.rs | 8 -------- src/modules/key/mod.rs | 2 ++ src/modules/key/traits.rs | 2 +- src/modules/key/traits/jw_key_type.rs | 3 +++ 9 files changed, 27 insertions(+), 25 deletions(-) delete mode 100644 src/modules/algorithm/traits/private_jwa_params.rs delete mode 100644 src/modules/algorithm/traits/public_jwa_params.rs create mode 100644 src/modules/key/traits/jw_key_type.rs diff --git a/src/modules/algorithm/models/es256_algorithm.rs b/src/modules/algorithm/models/es256_algorithm.rs index a2700e5..4594333 100644 --- a/src/modules/algorithm/models/es256_algorithm.rs +++ b/src/modules/algorithm/models/es256_algorithm.rs @@ -3,6 +3,7 @@ use std::fmt::{Debug, Formatter}; use p256::ecdsa::{SigningKey, Signature, signature::Signer}; use p256::ecdsa::signature::Verifier; use crate::algorithm::JwAlg; +use crate::modules::key::JwKeyType; /// ```shell /// openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out es256.pem @@ -40,6 +41,12 @@ impl JwAlg for ES256Algorithm { } } +impl JwKeyType for ES256Algorithm { + fn kty() -> impl AsRef { + "EC" + } +} + impl Debug for ES256Algorithm { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "ES256Algorithm {{ .. }}") @@ -72,4 +79,4 @@ mod tests { assert!(verify); } -} \ No newline at end of file +} diff --git a/src/modules/algorithm/models/hs256_algorithm.rs b/src/modules/algorithm/models/hs256_algorithm.rs index ac393b6..f569292 100644 --- a/src/modules/algorithm/models/hs256_algorithm.rs +++ b/src/modules/algorithm/models/hs256_algorithm.rs @@ -4,6 +4,7 @@ use hmac::{Hmac, Mac}; use hmac::digest::InvalidLength; use sha2::Sha256; use crate::algorithm::JwAlg; +use crate::modules::key::JwKeyType; #[derive(Clone)] pub struct HS256Algorithm { @@ -44,6 +45,12 @@ impl JwAlg for HS256Algorithm { } } +impl JwKeyType for HS256Algorithm { + fn kty() -> impl AsRef { + "oct" + } +} + impl Debug for HS256Algorithm { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "HS256Algorithm {{ .. }}") @@ -70,4 +77,4 @@ mod tests { assert!(verify); } -} \ No newline at end of file +} diff --git a/src/modules/algorithm/models/none_algorithm.rs b/src/modules/algorithm/models/none_algorithm.rs index 68aa95f..1b8ddba 100644 --- a/src/modules/algorithm/models/none_algorithm.rs +++ b/src/modules/algorithm/models/none_algorithm.rs @@ -18,4 +18,4 @@ impl JwAlg for NoneAlgorithm { fn verify(&self, _: &str, _: &[u8]) -> Result { Ok(true) } -} \ No newline at end of file +} diff --git a/src/modules/algorithm/models/rs256_algorithm.rs b/src/modules/algorithm/models/rs256_algorithm.rs index 1a77a3f..3273bb5 100644 --- a/src/modules/algorithm/models/rs256_algorithm.rs +++ b/src/modules/algorithm/models/rs256_algorithm.rs @@ -9,6 +9,7 @@ use sha2::Sha256; use crate::algorithm::JwAlg; use crate::algorithm::models::rs256_algorithm::rs256_public_params::RS256PublicParams; use crate::algorithm::traits::public_jwa_params::PublicJwaParams; +use crate::modules::key::JwKeyType; #[derive(Clone)] pub struct RS256Algorithm { @@ -47,12 +48,9 @@ impl JwAlg for RS256Algorithm { } } -impl PublicJwaParams for RS256Algorithm { - type Params = RS256PublicParams; - - fn public_params(&self) -> Self::Params { - todo!() - // self.inner. +impl JwKeyType for RS256Algorithm { + fn kty() -> impl AsRef { + "RSA" } } diff --git a/src/modules/algorithm/traits/private_jwa_params.rs b/src/modules/algorithm/traits/private_jwa_params.rs deleted file mode 100644 index db4ab78..0000000 --- a/src/modules/algorithm/traits/private_jwa_params.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::de::DeserializeOwned; -use serde::Serialize; -use crate::algorithm::traits::public_jwa_params::PublicJwaParams; - -pub trait PrivateJwaParams: PublicJwaParams { - type Params: Serialize + DeserializeOwned; -} diff --git a/src/modules/algorithm/traits/public_jwa_params.rs b/src/modules/algorithm/traits/public_jwa_params.rs deleted file mode 100644 index f2eeffc..0000000 --- a/src/modules/algorithm/traits/public_jwa_params.rs +++ /dev/null @@ -1,8 +0,0 @@ -use serde::de::DeserializeOwned; -use serde::Serialize; - -pub trait PublicJwaParams { - type Params: Serialize + DeserializeOwned; - - fn public_params(&self) -> Self::Params; -} diff --git a/src/modules/key/mod.rs b/src/modules/key/mod.rs index efcfe9e..02f55fe 100644 --- a/src/modules/key/mod.rs +++ b/src/modules/key/mod.rs @@ -1,6 +1,8 @@ mod models; mod traits; +pub use traits::jw_key_type::JwKeyType; + #[cfg(all(test, feature = "rs256"))] mod tests { use pkcs1::DecodeRsaPrivateKey; diff --git a/src/modules/key/traits.rs b/src/modules/key/traits.rs index 531db74..5a80ba2 100644 --- a/src/modules/key/traits.rs +++ b/src/modules/key/traits.rs @@ -1 +1 @@ -mod jw_key; +pub mod jw_key_type; diff --git a/src/modules/key/traits/jw_key_type.rs b/src/modules/key/traits/jw_key_type.rs new file mode 100644 index 0000000..738a593 --- /dev/null +++ b/src/modules/key/traits/jw_key_type.rs @@ -0,0 +1,3 @@ +pub trait JwKeyType { + fn kty() -> impl AsRef; +} From 731438c9d87330672c085b402664f89974777088 Mon Sep 17 00:00:00 2001 From: rster2002 Date: Tue, 29 Apr 2025 21:50:10 +0200 Subject: [PATCH 03/17] =?UTF-8?q?=F0=9F=9A=A7=20Some=20progress=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../algorithm/models/es256_algorithm.rs | 4 +-- .../algorithm/models/hs256_algorithm.rs | 10 +++--- .../algorithm/models/rs256_algorithm.rs | 35 ++++++++++++++----- src/modules/algorithm/traits.rs | 2 -- src/modules/key/mod.rs | 12 +++---- src/modules/key/models.rs | 3 +- src/modules/key/models/jwk.rs | 16 ++++++++- src/modules/key/models/rsa_private_jwk.rs | 7 ++++ src/modules/key/traits/jw_key_type.rs | 11 +++++- 9 files changed, 72 insertions(+), 28 deletions(-) create mode 100644 src/modules/key/models/rsa_private_jwk.rs diff --git a/src/modules/algorithm/models/es256_algorithm.rs b/src/modules/algorithm/models/es256_algorithm.rs index 4594333..45f3dd0 100644 --- a/src/modules/algorithm/models/es256_algorithm.rs +++ b/src/modules/algorithm/models/es256_algorithm.rs @@ -41,11 +41,11 @@ impl JwAlg for ES256Algorithm { } } -impl JwKeyType for ES256Algorithm { +/*impl JwKeyType<'_> for ES256Algorithm { fn kty() -> impl AsRef { "EC" } -} +}*/ impl Debug for ES256Algorithm { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { diff --git a/src/modules/algorithm/models/hs256_algorithm.rs b/src/modules/algorithm/models/hs256_algorithm.rs index f569292..5ec5457 100644 --- a/src/modules/algorithm/models/hs256_algorithm.rs +++ b/src/modules/algorithm/models/hs256_algorithm.rs @@ -45,11 +45,11 @@ impl JwAlg for HS256Algorithm { } } -impl JwKeyType for HS256Algorithm { - fn kty() -> impl AsRef { - "oct" - } -} +// impl JwKeyType for HS256Algorithm { +// fn kty() -> impl AsRef { +// "oct" +// } +// } impl Debug for HS256Algorithm { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { diff --git a/src/modules/algorithm/models/rs256_algorithm.rs b/src/modules/algorithm/models/rs256_algorithm.rs index 3273bb5..9e596ce 100644 --- a/src/modules/algorithm/models/rs256_algorithm.rs +++ b/src/modules/algorithm/models/rs256_algorithm.rs @@ -1,24 +1,29 @@ pub mod rs256_public_params; use std::fmt::{Debug, Formatter}; +use base64::Engine; +use base64::prelude::BASE64_URL_SAFE_NO_PAD; pub use rsa::pkcs1::DecodeRsaPrivateKey; use rsa::pkcs1v15::{Signature, SigningKey}; +use rsa::RsaPrivateKey; use rsa::signature::{Keypair, SignatureEncoding, Signer, Verifier}; +use rsa::traits::PublicKeyParts; use serde::{Deserialize, Serialize}; use sha2::Sha256; use crate::algorithm::JwAlg; use crate::algorithm::models::rs256_algorithm::rs256_public_params::RS256PublicParams; -use crate::algorithm::traits::public_jwa_params::PublicJwaParams; -use crate::modules::key::JwKeyType; +use crate::modules::key::{JwKeyType, RsaPublicJwk}; #[derive(Clone)] pub struct RS256Algorithm { - inner: SigningKey + inner: RsaPrivateKey, + signing_key: SigningKey, } impl RS256Algorithm { - pub fn new(key: SigningKey) -> Self { + pub fn new(key: RsaPrivateKey) -> Self { RS256Algorithm { + signing_key: SigningKey::new(key.clone()), inner: key, } } @@ -38,20 +43,33 @@ impl JwAlg for RS256Algorithm { } fn sign(&self, payload: &str) -> Vec { - self.inner.sign(payload.as_bytes()).to_vec() + self.signing_key.sign(payload.as_bytes()).to_vec() } fn verify(&self, payload: &str, signature: &[u8]) -> Result { let signature = Signature::try_from(signature)?; - Ok(self.inner.verifying_key().verify(payload.as_bytes(), &signature).is_ok()) + Ok(self.signing_key.verifying_key().verify(payload.as_bytes(), &signature).is_ok()) } } -impl JwKeyType for RS256Algorithm { +impl JwKeyType<'_> for RS256Algorithm { + type Public = RsaPublicJwk; + type Private = (); + fn kty() -> impl AsRef { "RSA" } + + fn public_params(&self) -> Self::Public { + let n = BASE64_URL_SAFE_NO_PAD.encode(self.inner.n().to_bytes_le()); + let e = BASE64_URL_SAFE_NO_PAD.encode(self.inner.e().to_bytes_le()); + + RsaPublicJwk { + n, + e, + } + } } #[cfg(test)] @@ -68,8 +86,7 @@ mod tests { let payload = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJoaiI6dHJ1ZX0"; let private_key = RsaPrivateKey::from_pkcs1_pem(include_str!("../../../../test-files/rs256.key")).unwrap(); - let signing_key = SigningKey::new(private_key); - let alg = RS256Algorithm::new(signing_key); + let alg = RS256Algorithm::new(private_key); let signature_bytes = alg.sign(payload); let signature_string = BASE64_URL_SAFE_NO_PAD.encode(&signature_bytes); diff --git a/src/modules/algorithm/traits.rs b/src/modules/algorithm/traits.rs index 11bef56..d724133 100644 --- a/src/modules/algorithm/traits.rs +++ b/src/modules/algorithm/traits.rs @@ -1,3 +1 @@ pub mod jw_alg; -pub mod public_jwa_params; -pub mod private_jwa_params; diff --git a/src/modules/key/mod.rs b/src/modules/key/mod.rs index 02f55fe..3d2cd57 100644 --- a/src/modules/key/mod.rs +++ b/src/modules/key/mod.rs @@ -2,21 +2,19 @@ mod models; mod traits; pub use traits::jw_key_type::JwKeyType; +pub use models::rsa_private_jwk::RsaPublicJwk; #[cfg(all(test, feature = "rs256"))] mod tests { use pkcs1::DecodeRsaPrivateKey; - use rsa::pkcs1v15::SigningKey; use rsa::RsaPrivateKey; - use crate::algorithm::{HS256Algorithm, RS256Algorithm}; + use crate::algorithm::{RS256Algorithm}; + use crate::modules::key::models::jwk::Jwk; #[test] fn jwk_is_created_correctly() { let private_key = RsaPrivateKey::from_pkcs1_pem(include_str!("../../../test-files/rs256.key")).unwrap(); - - let signing_key = SigningKey::new(private_key); - let alg = RS256Algorithm::new(signing_key); - - + let alg = RS256Algorithm::new(private_key); + let jwk = Jwk::new(&alg); } } diff --git a/src/modules/key/models.rs b/src/modules/key/models.rs index 3c693be..3ab15a6 100644 --- a/src/modules/key/models.rs +++ b/src/modules/key/models.rs @@ -1 +1,2 @@ -mod jwk; +pub mod jwk; +pub mod rsa_private_jwk; diff --git a/src/modules/key/models/jwk.rs b/src/modules/key/models/jwk.rs index d853e45..8992ca1 100644 --- a/src/modules/key/models/jwk.rs +++ b/src/modules/key/models/jwk.rs @@ -1 +1,15 @@ -pub struct Jwk; +use crate::modules::key::JwKeyType; + +pub struct Jwk<'a, T> +where T : JwKeyType<'a> +{ + inner: &'a T, +} + +impl<'a, T> Jwk<'a, T> +where T : JwKeyType<'a> +{ + pub fn new(inner: &'a T) -> Self { + Self { inner } + } +} diff --git a/src/modules/key/models/rsa_private_jwk.rs b/src/modules/key/models/rsa_private_jwk.rs new file mode 100644 index 0000000..1a200f3 --- /dev/null +++ b/src/modules/key/models/rsa_private_jwk.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct RsaPublicJwk { + pub n: String, + pub e: String, +} diff --git a/src/modules/key/traits/jw_key_type.rs b/src/modules/key/traits/jw_key_type.rs index 738a593..be12ab1 100644 --- a/src/modules/key/traits/jw_key_type.rs +++ b/src/modules/key/traits/jw_key_type.rs @@ -1,3 +1,12 @@ -pub trait JwKeyType { +use serde::{Deserialize, Serialize}; + +pub trait JwKeyType<'a> { + type Public: Serialize + Deserialize<'a>; + type Private: Serialize + Deserialize<'a>; + fn kty() -> impl AsRef; + fn public_params(&self) -> Self::Public; + fn private_parameters(&self) -> Option { + None + } } From 98d0084b07a55a871055339777cffe4bb80f0324 Mon Sep 17 00:00:00 2001 From: rster2002 Date: Tue, 29 Apr 2025 22:38:20 +0200 Subject: [PATCH 04/17] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Split=20algorithms?= =?UTF-8?q?=20into=20public=20and=20private=20variants?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/algorithm/mod.rs | 9 ++- .../es256_private.rs} | 33 +++++----- .../models/es256_algorithm/es256_public.rs | 22 +++++++ .../algorithm/models/es256_algorithm/mod.rs | 2 + .../hs256_private.rs} | 37 +++++------ .../algorithm/models/hs256_algorithm/mod.rs | 1 + .../algorithm/models/none_algorithm.rs | 12 ++-- .../algorithm/models/rs256_algorithm/mod.rs | 2 + .../rs256_private.rs} | 63 +++++++++---------- .../models/rs256_algorithm/rs256_public.rs | 24 +++++++ .../rs256_algorithm/rs256_public_params.rs | 7 --- src/modules/algorithm/traits.rs | 1 + src/modules/algorithm/traits/jw_alg.rs | 1 - src/modules/algorithm/traits/jw_alg_sign.rs | 5 ++ src/modules/key/mod.rs | 6 +- src/modules/token/mod.rs | 8 +-- src/modules/token/models/jwt.rs | 4 +- 17 files changed, 141 insertions(+), 96 deletions(-) rename src/modules/algorithm/models/{es256_algorithm.rs => es256_algorithm/es256_private.rs} (78%) create mode 100644 src/modules/algorithm/models/es256_algorithm/es256_public.rs create mode 100644 src/modules/algorithm/models/es256_algorithm/mod.rs rename src/modules/algorithm/models/{hs256_algorithm.rs => hs256_algorithm/hs256_private.rs} (79%) create mode 100644 src/modules/algorithm/models/hs256_algorithm/mod.rs create mode 100644 src/modules/algorithm/models/rs256_algorithm/mod.rs rename src/modules/algorithm/models/{rs256_algorithm.rs => rs256_algorithm/rs256_private.rs} (72%) create mode 100644 src/modules/algorithm/models/rs256_algorithm/rs256_public.rs delete mode 100644 src/modules/algorithm/models/rs256_algorithm/rs256_public_params.rs create mode 100644 src/modules/algorithm/traits/jw_alg_sign.rs diff --git a/src/modules/algorithm/mod.rs b/src/modules/algorithm/mod.rs index 2d7df68..5b589fe 100644 --- a/src/modules/algorithm/mod.rs +++ b/src/modules/algorithm/mod.rs @@ -4,12 +4,15 @@ mod traits; pub use models::none_algorithm::NoneAlgorithm; #[cfg(feature = "hs256")] -pub use models::hs256_algorithm::HS256Algorithm; +pub use models::hs256_algorithm::hs256_private::HS256Private; #[cfg(feature = "rs256")] -pub use models::rs256_algorithm::{RS256Algorithm, rs256_public_params::RS256PublicParams}; +pub use models::rs256_algorithm::rs256_public::RS256Public; +pub use models::rs256_algorithm::rs256_private::RS256Private; #[cfg(feature = "es256")] -pub use models::es256_algorithm::ES256Algorithm; +pub use models::es256_algorithm::es256_public::ES256Public; +pub use models::es256_algorithm::es256_private::ES256Private; pub use traits::jw_alg::JwAlg; +pub use traits::jw_alg_sign::JwAlgSign; diff --git a/src/modules/algorithm/models/es256_algorithm.rs b/src/modules/algorithm/models/es256_algorithm/es256_private.rs similarity index 78% rename from src/modules/algorithm/models/es256_algorithm.rs rename to src/modules/algorithm/models/es256_algorithm/es256_private.rs index 45f3dd0..557d7a2 100644 --- a/src/modules/algorithm/models/es256_algorithm.rs +++ b/src/modules/algorithm/models/es256_algorithm/es256_private.rs @@ -2,37 +2,32 @@ use std::convert::Infallible; use std::fmt::{Debug, Formatter}; use p256::ecdsa::{SigningKey, Signature, signature::Signer}; use p256::ecdsa::signature::Verifier; -use crate::algorithm::JwAlg; +use crate::algorithm::{JwAlg, JwAlgSign}; use crate::modules::key::JwKeyType; /// ```shell /// openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out es256.pem /// ``` #[derive(Clone)] -pub struct ES256Algorithm { +pub struct ES256Private { inner: SigningKey, } -impl ES256Algorithm { +impl ES256Private { pub fn new(key: SigningKey) -> Self { - ES256Algorithm { + ES256Private { inner: key, } } } -impl JwAlg for ES256Algorithm { +impl JwAlg for ES256Private { type Error = Infallible; fn alg() -> impl AsRef { "ES256" } - fn sign(&self, payload: &str) -> Vec { - let signature: Signature = self.inner.sign(payload.as_bytes()); - signature.to_vec() - } - fn verify(&self, payload: &str, signature: &[u8]) -> Result { let verifying_key = self.inner.verifying_key(); let signature = Signature::try_from(signature).unwrap(); @@ -41,13 +36,14 @@ impl JwAlg for ES256Algorithm { } } -/*impl JwKeyType<'_> for ES256Algorithm { - fn kty() -> impl AsRef { - "EC" +impl JwAlgSign for ES256Private { + fn sign(&self, payload: &str) -> Vec { + let signature: Signature = self.inner.sign(payload.as_bytes()); + signature.to_vec() } -}*/ +} -impl Debug for ES256Algorithm { +impl Debug for ES256Private { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "ES256Algorithm {{ .. }}") } @@ -59,16 +55,15 @@ mod tests { use base64::prelude::BASE64_URL_SAFE_NO_PAD; use p256::ecdsa::SigningKey; use p256::SecretKey; - use crate::algorithm::JwAlg; - use crate::algorithm::models::es256_algorithm::ES256Algorithm; + use crate::algorithm::{ES256Private, JwAlg, JwAlgSign}; #[test] fn es256_algorithm_works_as_expected() { let payload = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0"; - let secret_key = include_str!("../../../../test-files/es256.key").parse::().unwrap(); + let secret_key = include_str!("../../../../../test-files/es256.key").parse::().unwrap(); let signing_key = SigningKey::from(secret_key); - let alg = ES256Algorithm::new(signing_key); + let alg = ES256Private::new(signing_key); let signature_bytes = alg.sign(payload); let signature_string = BASE64_URL_SAFE_NO_PAD.encode(&signature_bytes); diff --git a/src/modules/algorithm/models/es256_algorithm/es256_public.rs b/src/modules/algorithm/models/es256_algorithm/es256_public.rs new file mode 100644 index 0000000..c4f0ef2 --- /dev/null +++ b/src/modules/algorithm/models/es256_algorithm/es256_public.rs @@ -0,0 +1,22 @@ +use std::convert::Infallible; +use p256::ecdsa::{VerifyingKey, Signature, signature::Verifier}; +use crate::algorithm::JwAlg; + +#[derive(Clone)] +pub struct ES256Public { + inner: VerifyingKey, +} + +impl JwAlg for ES256Public { + type Error = Infallible; + + fn alg() -> impl AsRef { + "ES256" + } + + fn verify(&self, payload: &str, signature: &[u8]) -> Result { + let signature = Signature::try_from(signature).unwrap(); + + Ok(self.inner.verify(payload.as_bytes(), &signature).is_ok()) + } +} diff --git a/src/modules/algorithm/models/es256_algorithm/mod.rs b/src/modules/algorithm/models/es256_algorithm/mod.rs new file mode 100644 index 0000000..592b5ba --- /dev/null +++ b/src/modules/algorithm/models/es256_algorithm/mod.rs @@ -0,0 +1,2 @@ +pub mod es256_private; +pub mod es256_public; \ No newline at end of file diff --git a/src/modules/algorithm/models/hs256_algorithm.rs b/src/modules/algorithm/models/hs256_algorithm/hs256_private.rs similarity index 79% rename from src/modules/algorithm/models/hs256_algorithm.rs rename to src/modules/algorithm/models/hs256_algorithm/hs256_private.rs index 5ec5457..22a3dae 100644 --- a/src/modules/algorithm/models/hs256_algorithm.rs +++ b/src/modules/algorithm/models/hs256_algorithm/hs256_private.rs @@ -3,36 +3,29 @@ use std::fmt::{Debug, Formatter}; use hmac::{Hmac, Mac}; use hmac::digest::InvalidLength; use sha2::Sha256; -use crate::algorithm::JwAlg; +use crate::algorithm::{JwAlg, JwAlgSign}; use crate::modules::key::JwKeyType; #[derive(Clone)] -pub struct HS256Algorithm { +pub struct HS256Private { inner: Hmac, } -impl HS256Algorithm { +impl HS256Private { pub fn new(key: &[u8]) -> Result { - Ok(HS256Algorithm { + Ok(HS256Private { inner: Hmac::::new_from_slice(key)? }) } } -impl JwAlg for HS256Algorithm { +impl JwAlg for HS256Private { type Error = Infallible; fn alg() -> impl AsRef { "HS256" } - fn sign(&self, payload: &str) -> Vec { - let mut inner = self.inner.clone(); - inner.update(payload.as_bytes()); - - inner.finalize().into_bytes().to_vec() - } - fn verify(&self, payload: &str, signature: &[u8]) -> Result { let mut inner = self.inner.clone(); inner.update(payload.as_bytes()); @@ -45,13 +38,16 @@ impl JwAlg for HS256Algorithm { } } -// impl JwKeyType for HS256Algorithm { -// fn kty() -> impl AsRef { -// "oct" -// } -// } +impl JwAlgSign for HS256Private { + fn sign(&self, payload: &str) -> Vec { + let mut inner = self.inner.clone(); + inner.update(payload.as_bytes()); + + inner.finalize().into_bytes().to_vec() + } +} -impl Debug for HS256Algorithm { +impl Debug for HS256Private { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "HS256Algorithm {{ .. }}") } @@ -61,12 +57,13 @@ impl Debug for HS256Algorithm { mod tests { use base64::Engine; use base64::prelude::BASE64_URL_SAFE_NO_PAD; - use crate::modules::algorithm::{HS256Algorithm, JwAlg}; + use crate::algorithm::JwAlgSign; + use crate::modules::algorithm::{HS256Private, JwAlg}; #[test] fn hs256_algorithm_works_as_expected() { let payload = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJoaiI6dHJ1ZX0"; - let alg = HS256Algorithm::new("qwed".as_ref()).unwrap(); + let alg = HS256Private::new("qwed".as_ref()).unwrap(); let signature_bytes = alg.sign(payload); let signature_string = BASE64_URL_SAFE_NO_PAD.encode(&signature_bytes); diff --git a/src/modules/algorithm/models/hs256_algorithm/mod.rs b/src/modules/algorithm/models/hs256_algorithm/mod.rs new file mode 100644 index 0000000..cadb704 --- /dev/null +++ b/src/modules/algorithm/models/hs256_algorithm/mod.rs @@ -0,0 +1 @@ +pub mod hs256_private; \ No newline at end of file diff --git a/src/modules/algorithm/models/none_algorithm.rs b/src/modules/algorithm/models/none_algorithm.rs index 1b8ddba..7734eab 100644 --- a/src/modules/algorithm/models/none_algorithm.rs +++ b/src/modules/algorithm/models/none_algorithm.rs @@ -1,5 +1,5 @@ use std::convert::Infallible; -use crate::algorithm::JwAlg; +use crate::algorithm::{JwAlg, JwAlgSign}; #[derive(Clone, Debug)] pub struct NoneAlgorithm; @@ -11,11 +11,13 @@ impl JwAlg for NoneAlgorithm { "none" } - fn sign(&self, _: &str) -> Vec { - vec![] - } - fn verify(&self, _: &str, _: &[u8]) -> Result { Ok(true) } } + +impl JwAlgSign for NoneAlgorithm { + fn sign(&self, _: &str) -> Vec { + vec![] + } +} diff --git a/src/modules/algorithm/models/rs256_algorithm/mod.rs b/src/modules/algorithm/models/rs256_algorithm/mod.rs new file mode 100644 index 0000000..c3bf060 --- /dev/null +++ b/src/modules/algorithm/models/rs256_algorithm/mod.rs @@ -0,0 +1,2 @@ +pub mod rs256_public; +pub mod rs256_private; \ No newline at end of file diff --git a/src/modules/algorithm/models/rs256_algorithm.rs b/src/modules/algorithm/models/rs256_algorithm/rs256_private.rs similarity index 72% rename from src/modules/algorithm/models/rs256_algorithm.rs rename to src/modules/algorithm/models/rs256_algorithm/rs256_private.rs index 9e596ce..27d85a0 100644 --- a/src/modules/algorithm/models/rs256_algorithm.rs +++ b/src/modules/algorithm/models/rs256_algorithm/rs256_private.rs @@ -1,5 +1,3 @@ -pub mod rs256_public_params; - use std::fmt::{Debug, Formatter}; use base64::Engine; use base64::prelude::BASE64_URL_SAFE_NO_PAD; @@ -10,42 +8,37 @@ use rsa::signature::{Keypair, SignatureEncoding, Signer, Verifier}; use rsa::traits::PublicKeyParts; use serde::{Deserialize, Serialize}; use sha2::Sha256; -use crate::algorithm::JwAlg; -use crate::algorithm::models::rs256_algorithm::rs256_public_params::RS256PublicParams; +use crate::algorithm::{JwAlg, JwAlgSign}; use crate::modules::key::{JwKeyType, RsaPublicJwk}; #[derive(Clone)] -pub struct RS256Algorithm { +pub struct RS256Private { inner: RsaPrivateKey, signing_key: SigningKey, } -impl RS256Algorithm { +impl RS256Private { pub fn new(key: RsaPrivateKey) -> Self { - RS256Algorithm { + RS256Private { signing_key: SigningKey::new(key.clone()), inner: key, } } } -impl Debug for RS256Algorithm { +impl Debug for RS256Private { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "RS256Algorithm {{ .. }}") } } -impl JwAlg for RS256Algorithm { +impl JwAlg for RS256Private { type Error = rsa::signature::Error; fn alg() -> impl AsRef { "RS256" } - fn sign(&self, payload: &str) -> Vec { - self.signing_key.sign(payload.as_bytes()).to_vec() - } - fn verify(&self, payload: &str, signature: &[u8]) -> Result { let signature = Signature::try_from(signature)?; @@ -53,25 +46,31 @@ impl JwAlg for RS256Algorithm { } } -impl JwKeyType<'_> for RS256Algorithm { - type Public = RsaPublicJwk; - type Private = (); - - fn kty() -> impl AsRef { - "RSA" - } - - fn public_params(&self) -> Self::Public { - let n = BASE64_URL_SAFE_NO_PAD.encode(self.inner.n().to_bytes_le()); - let e = BASE64_URL_SAFE_NO_PAD.encode(self.inner.e().to_bytes_le()); - - RsaPublicJwk { - n, - e, - } +impl JwAlgSign for RS256Private { + fn sign(&self, payload: &str) -> Vec { + self.signing_key.sign(payload.as_bytes()).to_vec() } } +// impl JwKeyType<'_> for RS256Algorithm { +// type Public = RsaPublicJwk; +// type Private = (); +// +// fn kty() -> impl AsRef { +// "RSA" +// } +// +// fn public_params(&self) -> Self::Public { +// let n = BASE64_URL_SAFE_NO_PAD.encode(self.inner.n().to_bytes_le()); +// let e = BASE64_URL_SAFE_NO_PAD.encode(self.inner.e().to_bytes_le()); +// +// RsaPublicJwk { +// n, +// e, +// } +// } +// } + #[cfg(test)] mod tests { use base64::Engine; @@ -79,14 +78,14 @@ mod tests { use pkcs1::DecodeRsaPrivateKey; use rsa::pkcs1v15::SigningKey; pub use rsa::RsaPrivateKey; - use crate::algorithm::{JwAlg, RS256Algorithm}; + use crate::algorithm::{JwAlg, JwAlgSign, RS256Private}; #[test] fn rs256_algorithm_works_as_expected() { let payload = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJoaiI6dHJ1ZX0"; - let private_key = RsaPrivateKey::from_pkcs1_pem(include_str!("../../../../test-files/rs256.key")).unwrap(); - let alg = RS256Algorithm::new(private_key); + let private_key = RsaPrivateKey::from_pkcs1_pem(include_str!("../../../../../test-files/rs256.key")).unwrap(); + let alg = RS256Private::new(private_key); let signature_bytes = alg.sign(payload); let signature_string = BASE64_URL_SAFE_NO_PAD.encode(&signature_bytes); diff --git a/src/modules/algorithm/models/rs256_algorithm/rs256_public.rs b/src/modules/algorithm/models/rs256_algorithm/rs256_public.rs new file mode 100644 index 0000000..8dc4771 --- /dev/null +++ b/src/modules/algorithm/models/rs256_algorithm/rs256_public.rs @@ -0,0 +1,24 @@ +use rsa::pkcs1v15::{Signature, VerifyingKey}; +use rsa::signature::Verifier; +use rsa::RsaPublicKey; +use sha2::Sha256; +use crate::algorithm::JwAlg; + +#[derive(Clone)] +pub struct RS256Public { + inner: RsaPublicKey, + verifying_key: VerifyingKey, +} + +impl JwAlg for RS256Public { + type Error = rsa::signature::Error; + + fn alg() -> impl AsRef { + "RS256" + } + + fn verify(&self, payload: &str, signature: &[u8]) -> Result { + let signature = Signature::try_from(signature)?; + Ok(self.verifying_key.verify(payload.as_bytes(), &signature).is_ok()) + } +} diff --git a/src/modules/algorithm/models/rs256_algorithm/rs256_public_params.rs b/src/modules/algorithm/models/rs256_algorithm/rs256_public_params.rs deleted file mode 100644 index b16c858..0000000 --- a/src/modules/algorithm/models/rs256_algorithm/rs256_public_params.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize)] -pub struct RS256PublicParams { - pub n: String, - pub e: String, -} diff --git a/src/modules/algorithm/traits.rs b/src/modules/algorithm/traits.rs index d724133..6a8049b 100644 --- a/src/modules/algorithm/traits.rs +++ b/src/modules/algorithm/traits.rs @@ -1 +1,2 @@ pub mod jw_alg; +pub mod jw_alg_sign; diff --git a/src/modules/algorithm/traits/jw_alg.rs b/src/modules/algorithm/traits/jw_alg.rs index 3c436b1..887a02f 100644 --- a/src/modules/algorithm/traits/jw_alg.rs +++ b/src/modules/algorithm/traits/jw_alg.rs @@ -2,6 +2,5 @@ pub trait JwAlg { type Error: std::error::Error; fn alg() -> impl AsRef; - fn sign(&self, payload: &str) -> Vec; fn verify(&self, payload: &str, signature: &[u8]) -> Result; } diff --git a/src/modules/algorithm/traits/jw_alg_sign.rs b/src/modules/algorithm/traits/jw_alg_sign.rs new file mode 100644 index 0000000..1af2e8c --- /dev/null +++ b/src/modules/algorithm/traits/jw_alg_sign.rs @@ -0,0 +1,5 @@ +use crate::algorithm::JwAlg; + +pub trait JwAlgSign: JwAlg { + fn sign(&self, payload: &str) -> Vec; +} \ No newline at end of file diff --git a/src/modules/key/mod.rs b/src/modules/key/mod.rs index 3d2cd57..855b176 100644 --- a/src/modules/key/mod.rs +++ b/src/modules/key/mod.rs @@ -8,13 +8,13 @@ pub use models::rsa_private_jwk::RsaPublicJwk; mod tests { use pkcs1::DecodeRsaPrivateKey; use rsa::RsaPrivateKey; - use crate::algorithm::{RS256Algorithm}; + use crate::algorithm::{RS256Private}; use crate::modules::key::models::jwk::Jwk; #[test] fn jwk_is_created_correctly() { let private_key = RsaPrivateKey::from_pkcs1_pem(include_str!("../../../test-files/rs256.key")).unwrap(); - let alg = RS256Algorithm::new(private_key); - let jwk = Jwk::new(&alg); + let alg = RS256Private::new(private_key); + // let jwk = Jwk::new(&alg); } } diff --git a/src/modules/token/mod.rs b/src/modules/token/mod.rs index 3671d85..3cc6a3e 100644 --- a/src/modules/token/mod.rs +++ b/src/modules/token/mod.rs @@ -8,12 +8,12 @@ pub use error::JwtError; #[cfg(test)] mod tests { - use crate::algorithm::{HS256Algorithm, JwAlg}; + use crate::algorithm::{HS256Private, JwAlg}; use crate::token::Jwt; #[test] fn simple_jwt_token_can_be_generated() { - let algorithm = HS256Algorithm::new("something".as_bytes()) + let algorithm = HS256Private::new("something".as_bytes()) .unwrap(); let token = Jwt::new("hello world".to_string()) @@ -26,14 +26,14 @@ mod tests { #[test] fn incorrect_signature_key() { - let algorithm_1 = HS256Algorithm::new("something".as_bytes()) + let algorithm_1 = HS256Private::new("something".as_bytes()) .unwrap(); let token = Jwt::new("hello world".to_string()) .into_token(&algorithm_1) .unwrap(); - let algorithm_2 = HS256Algorithm::new("else".as_bytes()) + let algorithm_2 = HS256Private::new("else".as_bytes()) .unwrap(); let jwt = Jwt::::check(&token, &algorithm_2); diff --git a/src/modules/token/models/jwt.rs b/src/modules/token/models/jwt.rs index 742bdcc..310d6ef 100644 --- a/src/modules/token/models/jwt.rs +++ b/src/modules/token/models/jwt.rs @@ -5,7 +5,7 @@ use base64::prelude::BASE64_URL_SAFE_NO_PAD; use chrono::{DateTime, Duration, Utc}; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; -use crate::algorithm::JwAlg; +use crate::algorithm::{JwAlg, JwAlgSign}; use crate::token::{JwtError, JwtHeader}; use crate::token::models::jwt_claims::JwtClaims; @@ -21,7 +21,7 @@ impl Jwt where T : Serialize + for<'a> Deserialize<'a>, { /// Takes the JWT instance, signs it, and returns the string representation for the token. - pub fn into_token(self, algorithm: &A) -> Result { + pub fn into_token(self, algorithm: &A) -> Result { let alg_ref = A::alg(); let header = JwtHeader { From a06892dd63ac8bb00352cbb0fe7a6ba8d7838f37 Mon Sep 17 00:00:00 2001 From: rster2002 Date: Wed, 30 Apr 2025 21:31:48 +0200 Subject: [PATCH 05/17] =?UTF-8?q?=F0=9F=9A=A7=20Some=20progress=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 14 ++++ Cargo.toml | 2 +- .../models/es256_algorithm/es256_private.rs | 68 ++++++++++++++++--- .../es256_algorithm/es256_private_params.rs | 9 +++ .../algorithm/models/es256_algorithm/mod.rs | 3 +- src/modules/key/models.rs | 1 + src/modules/key/models/jwk_sign.rs | 3 + src/modules/key/traits/jw_key_type.rs | 10 +-- 8 files changed, 93 insertions(+), 17 deletions(-) create mode 100644 src/modules/algorithm/models/es256_algorithm/es256_private_params.rs create mode 100644 src/modules/key/models/jwk_sign.rs diff --git a/Cargo.lock b/Cargo.lock index ccc02a1..7e16265 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,6 +178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", + "base64ct", "crypto-bigint", "digest", "ff", @@ -187,6 +188,8 @@ dependencies = [ "pkcs8", "rand_core", "sec1", + "serde_json", + "serdect", "subtle", "zeroize", ] @@ -543,6 +546,7 @@ dependencies = [ "der", "generic-array", "pkcs8", + "serdect", "subtle", "zeroize", ] @@ -579,6 +583,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "sha2" version = "0.10.8" diff --git a/Cargo.toml b/Cargo.toml index d614632..988ed2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ hmac = { version = "0.12.1", optional = true } sha2 = { version = "0.10.8", features = ["oid"], optional = true } rsa = { version = "0.9.7", optional = true } pkcs1 = "0.7.5" -p256 = { version = "0.13.2", features = ["pem"], optional = true } +p256 = { version = "0.13.2", features = ["pem", "arithmetic", "jwk"], optional = true } chrono = "0.4.39" [features] diff --git a/src/modules/algorithm/models/es256_algorithm/es256_private.rs b/src/modules/algorithm/models/es256_algorithm/es256_private.rs index 557d7a2..e83c4bf 100644 --- a/src/modules/algorithm/models/es256_algorithm/es256_private.rs +++ b/src/modules/algorithm/models/es256_algorithm/es256_private.rs @@ -2,7 +2,10 @@ use std::convert::Infallible; use std::fmt::{Debug, Formatter}; use p256::ecdsa::{SigningKey, Signature, signature::Signer}; use p256::ecdsa::signature::Verifier; +use p256::elliptic_curve::SecretKey; +use p256::NistP256; use crate::algorithm::{JwAlg, JwAlgSign}; +use crate::algorithm::models::es256_algorithm::es256_private_params::ES256PrivateParams; use crate::modules::key::JwKeyType; /// ```shell @@ -10,13 +13,15 @@ use crate::modules::key::JwKeyType; /// ``` #[derive(Clone)] pub struct ES256Private { - inner: SigningKey, + inner: SecretKey, + signing_key: SigningKey, } -impl ES256Private { - pub fn new(key: SigningKey) -> Self { +impl From> for ES256Private { + fn from(value: SecretKey) -> Self { ES256Private { - inner: key, + signing_key: SigningKey::from(value.clone()), + inner: value, } } } @@ -29,7 +34,7 @@ impl JwAlg for ES256Private { } fn verify(&self, payload: &str, signature: &[u8]) -> Result { - let verifying_key = self.inner.verifying_key(); + let verifying_key = self.signing_key.verifying_key(); let signature = Signature::try_from(signature).unwrap(); Ok(verifying_key.verify(payload.as_bytes(), &signature).is_ok()) @@ -38,11 +43,54 @@ impl JwAlg for ES256Private { impl JwAlgSign for ES256Private { fn sign(&self, payload: &str) -> Vec { - let signature: Signature = self.inner.sign(payload.as_bytes()); + let signature: Signature = self.signing_key.sign(payload.as_bytes()); signature.to_vec() } } +impl JwKeyType<'_> for ES256Private { + type Params = ES256PrivateParams; + + fn kty() -> impl AsRef { + "EC" + } + + fn parms(&self) -> Self::Params { + let base_value = serde_json::to_value(self.inner.clone().to_jwk()) + .expect("should always work"); + + let key = base_value + .as_object() + .expect("should always be an object"); + + ES256PrivateParams { + crv: key.get("crv") + .expect("should always have a value") + .as_str() + .expect("should always be a string") + .to_string(), + + x: key.get("x") + .expect("should always have a value") + .as_str() + .expect("should always be a string") + .to_string(), + + y: key.get("y") + .expect("should always have a value") + .as_str() + .expect("should always be a string") + .to_string(), + + d: key.get("d") + .expect("should always have a value") + .as_str() + .expect("should always be a string") + .to_string(), + } + } +} + impl Debug for ES256Private { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "ES256Algorithm {{ .. }}") @@ -61,9 +109,8 @@ mod tests { fn es256_algorithm_works_as_expected() { let payload = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0"; let secret_key = include_str!("../../../../../test-files/es256.key").parse::().unwrap(); - let signing_key = SigningKey::from(secret_key); - let alg = ES256Private::new(signing_key); + let alg = ES256Private::from(secret_key); let signature_bytes = alg.sign(payload); let signature_string = BASE64_URL_SAFE_NO_PAD.encode(&signature_bytes); @@ -74,4 +121,9 @@ mod tests { assert!(verify); } + + #[test] + fn jwk_is_generated_correctly() { + + } } diff --git a/src/modules/algorithm/models/es256_algorithm/es256_private_params.rs b/src/modules/algorithm/models/es256_algorithm/es256_private_params.rs new file mode 100644 index 0000000..bf51aa4 --- /dev/null +++ b/src/modules/algorithm/models/es256_algorithm/es256_private_params.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +pub struct ES256PrivateParams { + pub crv: String, + pub x: String, + pub y: String, + pub d: String, +} diff --git a/src/modules/algorithm/models/es256_algorithm/mod.rs b/src/modules/algorithm/models/es256_algorithm/mod.rs index 592b5ba..267a881 100644 --- a/src/modules/algorithm/models/es256_algorithm/mod.rs +++ b/src/modules/algorithm/models/es256_algorithm/mod.rs @@ -1,2 +1,3 @@ pub mod es256_private; -pub mod es256_public; \ No newline at end of file +pub mod es256_public; +mod es256_private_params; \ No newline at end of file diff --git a/src/modules/key/models.rs b/src/modules/key/models.rs index 3ab15a6..5a5f6b4 100644 --- a/src/modules/key/models.rs +++ b/src/modules/key/models.rs @@ -1,2 +1,3 @@ pub mod jwk; pub mod rsa_private_jwk; +mod jwk_sign; diff --git a/src/modules/key/models/jwk_sign.rs b/src/modules/key/models/jwk_sign.rs new file mode 100644 index 0000000..4c53fed --- /dev/null +++ b/src/modules/key/models/jwk_sign.rs @@ -0,0 +1,3 @@ +pub struct JwkSign { + +} \ No newline at end of file diff --git a/src/modules/key/traits/jw_key_type.rs b/src/modules/key/traits/jw_key_type.rs index be12ab1..0c7e3dc 100644 --- a/src/modules/key/traits/jw_key_type.rs +++ b/src/modules/key/traits/jw_key_type.rs @@ -1,12 +1,8 @@ use serde::{Deserialize, Serialize}; pub trait JwKeyType<'a> { - type Public: Serialize + Deserialize<'a>; - type Private: Serialize + Deserialize<'a>; - + type Params: Serialize + Deserialize<'a>; + fn kty() -> impl AsRef; - fn public_params(&self) -> Self::Public; - fn private_parameters(&self) -> Option { - None - } + fn parms(&self) -> Self::Params; } From 37028470674daa6a7d72537c8667e7d9a4f687ff Mon Sep 17 00:00:00 2001 From: rster2002 Date: Mon, 16 Mar 2026 20:04:14 +0100 Subject: [PATCH 06/17] =?UTF-8?q?=F0=9F=9A=A7=20Breaking=20stuff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 1 + Cargo.toml | 1 + scripts/generate-jwks/.gitignore | 34 ++++ scripts/generate-jwks/README.md | 15 ++ scripts/generate-jwks/bun.lock | 26 +++ scripts/generate-jwks/index.ts | 31 ++++ scripts/generate-jwks/package.json | 12 ++ scripts/generate-jwks/tsconfig.json | 29 ++++ src/modules/algorithm/mod.rs | 2 +- src/modules/algorithm/models.rs | 3 +- .../models/es256_algorithm/es256_private.rs | 150 +++++++++--------- .../es256_algorithm/es256_private_params.rs | 2 +- .../models/es256_algorithm/es256_public.rs | 4 +- .../models/es_algorithm/es_private.rs | 43 +++++ .../models/es_algorithm/es_public.rs | 7 + .../algorithm/models/es_algorithm/mod.rs | 2 + .../models/hs256_algorithm/hs256_private.rs | 6 +- .../algorithm/models/none_algorithm.rs | 4 +- .../models/rs256_algorithm/rs256_private.rs | 6 +- .../models/rs256_algorithm/rs256_public.rs | 4 +- src/modules/algorithm/traits.rs | 3 +- src/modules/algorithm/traits/jw_alg.rs | 5 +- src/modules/algorithm/traits/jw_alg_sign.rs | 5 +- src/modules/algorithm/traits/jw_alg_verify.rs | 8 + src/modules/token/mod.rs | 2 +- src/modules/token/models/jwt.rs | 12 +- test-files/es256-private.jwks.json | 11 ++ test-files/es256-public.jwks.json | 10 ++ test-files/rs256-private.jwks.json | 16 ++ test-files/rs256-public.jwks.json | 10 ++ 30 files changed, 360 insertions(+), 104 deletions(-) create mode 100644 scripts/generate-jwks/.gitignore create mode 100644 scripts/generate-jwks/README.md create mode 100644 scripts/generate-jwks/bun.lock create mode 100644 scripts/generate-jwks/index.ts create mode 100644 scripts/generate-jwks/package.json create mode 100644 scripts/generate-jwks/tsconfig.json create mode 100644 src/modules/algorithm/models/es_algorithm/es_private.rs create mode 100644 src/modules/algorithm/models/es_algorithm/es_public.rs create mode 100644 src/modules/algorithm/models/es_algorithm/mod.rs create mode 100644 src/modules/algorithm/traits/jw_alg_verify.rs create mode 100644 test-files/es256-private.jwks.json create mode 100644 test-files/es256-public.jwks.json create mode 100644 test-files/rs256-private.jwks.json create mode 100644 test-files/rs256-public.jwks.json diff --git a/Cargo.lock b/Cargo.lock index 7e16265..14129ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,6 +291,7 @@ version = "1.0.1" dependencies = [ "base64", "chrono", + "ecdsa", "hmac", "p256", "pkcs1", diff --git a/Cargo.toml b/Cargo.toml index 988ed2b..93ac0ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ rsa = { version = "0.9.7", optional = true } pkcs1 = "0.7.5" p256 = { version = "0.13.2", features = ["pem", "arithmetic", "jwk"], optional = true } chrono = "0.4.39" +ecdsa = "0.16.9" [features] default = ["hs256"] diff --git a/scripts/generate-jwks/.gitignore b/scripts/generate-jwks/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/scripts/generate-jwks/.gitignore @@ -0,0 +1,34 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/scripts/generate-jwks/README.md b/scripts/generate-jwks/README.md new file mode 100644 index 0000000..7d9b94d --- /dev/null +++ b/scripts/generate-jwks/README.md @@ -0,0 +1,15 @@ +# generate-jwks + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.ts +``` + +This project was created using `bun init` in bun v1.3.10. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime. diff --git a/scripts/generate-jwks/bun.lock b/scripts/generate-jwks/bun.lock new file mode 100644 index 0000000..5ef14f0 --- /dev/null +++ b/scripts/generate-jwks/bun.lock @@ -0,0 +1,26 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "generate-jwks", + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="], + + "@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="], + + "bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], + } +} diff --git a/scripts/generate-jwks/index.ts b/scripts/generate-jwks/index.ts new file mode 100644 index 0000000..4afc261 --- /dev/null +++ b/scripts/generate-jwks/index.ts @@ -0,0 +1,31 @@ +const es256 = await crypto.subtle.generateKey( + { + name: "ECDSA", + namedCurve: "P-256", + }, + true, + ["sign", "verify"], +); + +const es256private = await crypto.subtle.exportKey("jwk", es256.privateKey); +const es256public = await crypto.subtle.exportKey("jwk", es256.publicKey); + +await Bun.write("es256-public.jwks.json", JSON.stringify(es256public, null, 2)); +await Bun.write("es256-private.jwks.json", JSON.stringify(es256private, null, 2)); + +const rs256 = await crypto.subtle.generateKey( + { + name: "RSASSA-PKCS1-v1_5", + modulusLength: 2048, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + hash: "SHA-256", + }, + true, + ["sign", "verify"], +); + +const rs256private = await crypto.subtle.exportKey("jwk", rs256.privateKey); +const rs256public = await crypto.subtle.exportKey("jwk", rs256.publicKey); + +await Bun.write("rs256-public.jwks.json", JSON.stringify(rs256public, null, 2)); +await Bun.write("rs256-private.jwks.json", JSON.stringify(rs256private, null, 2)); diff --git a/scripts/generate-jwks/package.json b/scripts/generate-jwks/package.json new file mode 100644 index 0000000..5bf4393 --- /dev/null +++ b/scripts/generate-jwks/package.json @@ -0,0 +1,12 @@ +{ + "name": "generate-jwks", + "module": "index.ts", + "type": "module", + "private": true, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5" + } +} diff --git a/scripts/generate-jwks/tsconfig.json b/scripts/generate-jwks/tsconfig.json new file mode 100644 index 0000000..bfa0fea --- /dev/null +++ b/scripts/generate-jwks/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/src/modules/algorithm/mod.rs b/src/modules/algorithm/mod.rs index 5b589fe..ca05d80 100644 --- a/src/modules/algorithm/mod.rs +++ b/src/modules/algorithm/mod.rs @@ -14,5 +14,5 @@ pub use models::rs256_algorithm::rs256_private::RS256Private; pub use models::es256_algorithm::es256_public::ES256Public; pub use models::es256_algorithm::es256_private::ES256Private; -pub use traits::jw_alg::JwAlg; +pub use traits::jw_alg_verify::JwAlgVerify; pub use traits::jw_alg_sign::JwAlgSign; diff --git a/src/modules/algorithm/models.rs b/src/modules/algorithm/models.rs index 7d6e2d5..63de14f 100644 --- a/src/modules/algorithm/models.rs +++ b/src/modules/algorithm/models.rs @@ -7,4 +7,5 @@ pub mod hs256_algorithm; pub mod rs256_algorithm; #[cfg(feature = "es256")] -pub mod es256_algorithm; \ No newline at end of file +pub mod es256_algorithm; +pub mod es_algorithm; \ No newline at end of file diff --git a/src/modules/algorithm/models/es256_algorithm/es256_private.rs b/src/modules/algorithm/models/es256_algorithm/es256_private.rs index e83c4bf..e54cba6 100644 --- a/src/modules/algorithm/models/es256_algorithm/es256_private.rs +++ b/src/modules/algorithm/models/es256_algorithm/es256_private.rs @@ -4,7 +4,7 @@ use p256::ecdsa::{SigningKey, Signature, signature::Signer}; use p256::ecdsa::signature::Verifier; use p256::elliptic_curve::SecretKey; use p256::NistP256; -use crate::algorithm::{JwAlg, JwAlgSign}; +use crate::algorithm::{JwAlgVerify, JwAlgSign}; use crate::algorithm::models::es256_algorithm::es256_private_params::ES256PrivateParams; use crate::modules::key::JwKeyType; @@ -17,79 +17,79 @@ pub struct ES256Private { signing_key: SigningKey, } -impl From> for ES256Private { - fn from(value: SecretKey) -> Self { - ES256Private { - signing_key: SigningKey::from(value.clone()), - inner: value, - } - } -} - -impl JwAlg for ES256Private { - type Error = Infallible; - - fn alg() -> impl AsRef { - "ES256" - } - - fn verify(&self, payload: &str, signature: &[u8]) -> Result { - let verifying_key = self.signing_key.verifying_key(); - let signature = Signature::try_from(signature).unwrap(); - - Ok(verifying_key.verify(payload.as_bytes(), &signature).is_ok()) - } -} - -impl JwAlgSign for ES256Private { - fn sign(&self, payload: &str) -> Vec { - let signature: Signature = self.signing_key.sign(payload.as_bytes()); - signature.to_vec() - } -} - -impl JwKeyType<'_> for ES256Private { - type Params = ES256PrivateParams; - - fn kty() -> impl AsRef { - "EC" - } - - fn parms(&self) -> Self::Params { - let base_value = serde_json::to_value(self.inner.clone().to_jwk()) - .expect("should always work"); - - let key = base_value - .as_object() - .expect("should always be an object"); - - ES256PrivateParams { - crv: key.get("crv") - .expect("should always have a value") - .as_str() - .expect("should always be a string") - .to_string(), - - x: key.get("x") - .expect("should always have a value") - .as_str() - .expect("should always be a string") - .to_string(), - - y: key.get("y") - .expect("should always have a value") - .as_str() - .expect("should always be a string") - .to_string(), - - d: key.get("d") - .expect("should always have a value") - .as_str() - .expect("should always be a string") - .to_string(), - } - } -} +// impl From> for ES256Private { +// fn from(value: SecretKey) -> Self { +// ES256Private { +// signing_key: SigningKey::from(value.clone()), +// inner: value, +// } +// } +// } +// +// impl JwAlgVerify for ES256Private { +// type Error = Infallible; +// +// fn alg() -> impl AsRef { +// "ES256" +// } +// +// fn verify(&self, payload: &str, signature: &[u8]) -> Result { +// let verifying_key = self.signing_key.verifying_key(); +// let signature = Signature::try_from(signature).unwrap(); +// +// Ok(verifying_key.verify(payload.as_bytes(), &signature).is_ok()) +// } +// } +// +// impl JwAlgSign for ES256Private { +// fn sign(&self, payload: &str) -> Vec { +// let signature: Signature = self.signing_key.sign(payload.as_bytes()); +// signature.to_vec() +// } +// } + +// impl JwKeyType<'_> for ES256Private { +// type Params = ES256PrivateParams; +// +// fn kty() -> impl AsRef { +// "EC" +// } +// +// fn parms(&self) -> Self::Params { +// let base_value = serde_json::to_value(self.inner.clone().to_jwk()) +// .expect("should always work"); +// +// let key = base_value +// .as_object() +// .expect("should always be an object"); +// +// ES256PrivateParams { +// crv: key.get("crv") +// .expect("should always have a value") +// .as_str() +// .expect("should always be a string") +// .to_string(), +// +// x: key.get("x") +// .expect("should always have a value") +// .as_str() +// .expect("should always be a string") +// .to_string(), +// +// y: key.get("y") +// .expect("should always have a value") +// .as_str() +// .expect("should always be a string") +// .to_string(), +// +// d: key.get("d") +// .expect("should always have a value") +// .as_str() +// .expect("should always be a string") +// .to_string(), +// } +// } +// } impl Debug for ES256Private { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -103,7 +103,7 @@ mod tests { use base64::prelude::BASE64_URL_SAFE_NO_PAD; use p256::ecdsa::SigningKey; use p256::SecretKey; - use crate::algorithm::{ES256Private, JwAlg, JwAlgSign}; + use crate::algorithm::{ES256Private, JwAlgVerify, JwAlgSign}; #[test] fn es256_algorithm_works_as_expected() { diff --git a/src/modules/algorithm/models/es256_algorithm/es256_private_params.rs b/src/modules/algorithm/models/es256_algorithm/es256_private_params.rs index bf51aa4..e8bb3e5 100644 --- a/src/modules/algorithm/models/es256_algorithm/es256_private_params.rs +++ b/src/modules/algorithm/models/es256_algorithm/es256_private_params.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] pub struct ES256PrivateParams { pub crv: String, + pub d: String, pub x: String, pub y: String, - pub d: String, } diff --git a/src/modules/algorithm/models/es256_algorithm/es256_public.rs b/src/modules/algorithm/models/es256_algorithm/es256_public.rs index c4f0ef2..57f5484 100644 --- a/src/modules/algorithm/models/es256_algorithm/es256_public.rs +++ b/src/modules/algorithm/models/es256_algorithm/es256_public.rs @@ -1,13 +1,13 @@ use std::convert::Infallible; use p256::ecdsa::{VerifyingKey, Signature, signature::Verifier}; -use crate::algorithm::JwAlg; +use crate::algorithm::JwAlgVerify; #[derive(Clone)] pub struct ES256Public { inner: VerifyingKey, } -impl JwAlg for ES256Public { +impl JwAlgVerify for ES256Public { type Error = Infallible; fn alg() -> impl AsRef { diff --git a/src/modules/algorithm/models/es_algorithm/es_private.rs b/src/modules/algorithm/models/es_algorithm/es_private.rs new file mode 100644 index 0000000..eb0066b --- /dev/null +++ b/src/modules/algorithm/models/es_algorithm/es_private.rs @@ -0,0 +1,43 @@ +use std::convert::Infallible; +use ecdsa::elliptic_curve::{Curve, SecretKey}; +use ecdsa::SigningKey; +use hmac::Mac; +use crate::algorithm::{JwAlgSign, JwAlgVerify}; +use p256::NistP256; +use crate::algorithm::traits::jw_alg::JwAlg; + +#[derive(Clone)] +pub struct ESPrivate +where C : Curve, +{ + inner: SecretKey, + signing_key: SigningKey, +} + +impl JwAlg for ESPrivate { + fn alg() -> impl AsRef { + "ES256" + } +} + +impl JwAlgVerify for ESPrivate +where C : Curve, +{ + type Error = Infallible; + + fn verify(&self, payload: &str, signature: &[u8]) -> Result { + let verifying_key = self.signing_key.verifying_key(); + let signature = Signature::try_from(signature).unwrap(); + + Ok(verifying_key.verify(payload.as_bytes(), &signature).is_ok()) + } +} + +impl JwAlgSign for ESPrivate +where C : Curve, +{ + fn sign(&self, payload: &str) -> Vec { + let signature: Signature = self.signing_key.sign(payload.as_bytes()); + signature.to_vec() + } +} diff --git a/src/modules/algorithm/models/es_algorithm/es_public.rs b/src/modules/algorithm/models/es_algorithm/es_public.rs new file mode 100644 index 0000000..074e79d --- /dev/null +++ b/src/modules/algorithm/models/es_algorithm/es_public.rs @@ -0,0 +1,7 @@ +use p256::elliptic_curve::{Curve}; + +pub struct ESPublic +where C : Curve +{ + inner: VerifyingKey, +} \ No newline at end of file diff --git a/src/modules/algorithm/models/es_algorithm/mod.rs b/src/modules/algorithm/models/es_algorithm/mod.rs new file mode 100644 index 0000000..b1a6fec --- /dev/null +++ b/src/modules/algorithm/models/es_algorithm/mod.rs @@ -0,0 +1,2 @@ +pub mod es_private; +pub mod es_public; \ No newline at end of file diff --git a/src/modules/algorithm/models/hs256_algorithm/hs256_private.rs b/src/modules/algorithm/models/hs256_algorithm/hs256_private.rs index 22a3dae..ca5fca8 100644 --- a/src/modules/algorithm/models/hs256_algorithm/hs256_private.rs +++ b/src/modules/algorithm/models/hs256_algorithm/hs256_private.rs @@ -3,7 +3,7 @@ use std::fmt::{Debug, Formatter}; use hmac::{Hmac, Mac}; use hmac::digest::InvalidLength; use sha2::Sha256; -use crate::algorithm::{JwAlg, JwAlgSign}; +use crate::algorithm::{JwAlgVerify, JwAlgSign}; use crate::modules::key::JwKeyType; #[derive(Clone)] @@ -19,7 +19,7 @@ impl HS256Private { } } -impl JwAlg for HS256Private { +impl JwAlgVerify for HS256Private { type Error = Infallible; fn alg() -> impl AsRef { @@ -58,7 +58,7 @@ mod tests { use base64::Engine; use base64::prelude::BASE64_URL_SAFE_NO_PAD; use crate::algorithm::JwAlgSign; - use crate::modules::algorithm::{HS256Private, JwAlg}; + use crate::modules::algorithm::{HS256Private, JwAlgVerify}; #[test] fn hs256_algorithm_works_as_expected() { diff --git a/src/modules/algorithm/models/none_algorithm.rs b/src/modules/algorithm/models/none_algorithm.rs index 7734eab..6160c05 100644 --- a/src/modules/algorithm/models/none_algorithm.rs +++ b/src/modules/algorithm/models/none_algorithm.rs @@ -1,10 +1,10 @@ use std::convert::Infallible; -use crate::algorithm::{JwAlg, JwAlgSign}; +use crate::algorithm::{JwAlgVerify, JwAlgSign}; #[derive(Clone, Debug)] pub struct NoneAlgorithm; -impl JwAlg for NoneAlgorithm { +impl JwAlgVerify for NoneAlgorithm { type Error = Infallible; fn alg() -> impl AsRef { diff --git a/src/modules/algorithm/models/rs256_algorithm/rs256_private.rs b/src/modules/algorithm/models/rs256_algorithm/rs256_private.rs index 27d85a0..16127ac 100644 --- a/src/modules/algorithm/models/rs256_algorithm/rs256_private.rs +++ b/src/modules/algorithm/models/rs256_algorithm/rs256_private.rs @@ -8,7 +8,7 @@ use rsa::signature::{Keypair, SignatureEncoding, Signer, Verifier}; use rsa::traits::PublicKeyParts; use serde::{Deserialize, Serialize}; use sha2::Sha256; -use crate::algorithm::{JwAlg, JwAlgSign}; +use crate::algorithm::{JwAlgVerify, JwAlgSign}; use crate::modules::key::{JwKeyType, RsaPublicJwk}; #[derive(Clone)] @@ -32,7 +32,7 @@ impl Debug for RS256Private { } } -impl JwAlg for RS256Private { +impl JwAlgVerify for RS256Private { type Error = rsa::signature::Error; fn alg() -> impl AsRef { @@ -78,7 +78,7 @@ mod tests { use pkcs1::DecodeRsaPrivateKey; use rsa::pkcs1v15::SigningKey; pub use rsa::RsaPrivateKey; - use crate::algorithm::{JwAlg, JwAlgSign, RS256Private}; + use crate::algorithm::{JwAlgVerify, JwAlgSign, RS256Private}; #[test] fn rs256_algorithm_works_as_expected() { diff --git a/src/modules/algorithm/models/rs256_algorithm/rs256_public.rs b/src/modules/algorithm/models/rs256_algorithm/rs256_public.rs index 8dc4771..b63bc85 100644 --- a/src/modules/algorithm/models/rs256_algorithm/rs256_public.rs +++ b/src/modules/algorithm/models/rs256_algorithm/rs256_public.rs @@ -2,7 +2,7 @@ use rsa::pkcs1v15::{Signature, VerifyingKey}; use rsa::signature::Verifier; use rsa::RsaPublicKey; use sha2::Sha256; -use crate::algorithm::JwAlg; +use crate::algorithm::JwAlgVerify; #[derive(Clone)] pub struct RS256Public { @@ -10,7 +10,7 @@ pub struct RS256Public { verifying_key: VerifyingKey, } -impl JwAlg for RS256Public { +impl JwAlgVerify for RS256Public { type Error = rsa::signature::Error; fn alg() -> impl AsRef { diff --git a/src/modules/algorithm/traits.rs b/src/modules/algorithm/traits.rs index 6a8049b..35e9888 100644 --- a/src/modules/algorithm/traits.rs +++ b/src/modules/algorithm/traits.rs @@ -1,2 +1,3 @@ -pub mod jw_alg; +pub mod jw_alg_verify; pub mod jw_alg_sign; +pub mod jw_alg; diff --git a/src/modules/algorithm/traits/jw_alg.rs b/src/modules/algorithm/traits/jw_alg.rs index 887a02f..0399ed6 100644 --- a/src/modules/algorithm/traits/jw_alg.rs +++ b/src/modules/algorithm/traits/jw_alg.rs @@ -1,6 +1,3 @@ pub trait JwAlg { - type Error: std::error::Error; - fn alg() -> impl AsRef; - fn verify(&self, payload: &str, signature: &[u8]) -> Result; -} +} \ No newline at end of file diff --git a/src/modules/algorithm/traits/jw_alg_sign.rs b/src/modules/algorithm/traits/jw_alg_sign.rs index 1af2e8c..8bc573c 100644 --- a/src/modules/algorithm/traits/jw_alg_sign.rs +++ b/src/modules/algorithm/traits/jw_alg_sign.rs @@ -1,5 +1,6 @@ -use crate::algorithm::JwAlg; +use crate::algorithm::JwAlgVerify; +use crate::algorithm::traits::jw_alg::JwAlg; -pub trait JwAlgSign: JwAlg { +pub trait JwAlgSign: JwAlgVerify { fn sign(&self, payload: &str) -> Vec; } \ No newline at end of file diff --git a/src/modules/algorithm/traits/jw_alg_verify.rs b/src/modules/algorithm/traits/jw_alg_verify.rs new file mode 100644 index 0000000..80a425d --- /dev/null +++ b/src/modules/algorithm/traits/jw_alg_verify.rs @@ -0,0 +1,8 @@ +use crate::algorithm::traits::jw_alg::JwAlg; + +pub trait JwAlgVerify { + type Error: std::error::Error; + + // fn alg() -> impl AsRef; + fn verify(&self, payload: &str, signature: &[u8]) -> Result; +} diff --git a/src/modules/token/mod.rs b/src/modules/token/mod.rs index 3cc6a3e..2835525 100644 --- a/src/modules/token/mod.rs +++ b/src/modules/token/mod.rs @@ -8,7 +8,7 @@ pub use error::JwtError; #[cfg(test)] mod tests { - use crate::algorithm::{HS256Private, JwAlg}; + use crate::algorithm::{HS256Private, JwAlgVerify}; use crate::token::Jwt; #[test] diff --git a/src/modules/token/models/jwt.rs b/src/modules/token/models/jwt.rs index 310d6ef..48d768c 100644 --- a/src/modules/token/models/jwt.rs +++ b/src/modules/token/models/jwt.rs @@ -5,7 +5,7 @@ use base64::prelude::BASE64_URL_SAFE_NO_PAD; use chrono::{DateTime, Duration, Utc}; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; -use crate::algorithm::{JwAlg, JwAlgSign}; +use crate::algorithm::{JwAlgVerify, JwAlgSign}; use crate::token::{JwtError, JwtHeader}; use crate::token::models::jwt_claims::JwtClaims; @@ -61,10 +61,10 @@ where T : Serialize + for<'a> Deserialize<'a>, /// Decodes and verifies the given string token with the given algorithm. Returns a JWT token /// instance with the expected payload. Note that this does not check any claims. To verify - /// basic expiry claims you can use [Jwt::verify_now] or you can further verify the token using + /// basic expiry claims, you can use [Jwt::verify_now] or you can further verify the token using /// [Jwt::against] or [Jwt::guard]. - pub fn check(token: &str, algorithm: &A) -> Result, JwtError> - where ::Error: 'static + pub fn check(token: &str, algorithm: &A) -> Result, JwtError> + where ::Error: 'static { let mut parts = token.split('.'); @@ -103,8 +103,8 @@ where T : Serialize + for<'a> Deserialize<'a>, /// Largely the same as [Jwt::check], but also verifies basic expiry claims. You can further /// verify the token using [Jwt::against] or [Jwt::guard]. - pub fn verify_now(token: &str, algorithm: &A) -> Result, JwtError> - where ::Error: 'static + pub fn verify_now(token: &str, algorithm: &A) -> Result, JwtError> + where ::Error: 'static { let jwt = Jwt::::check(token, algorithm)? .against(&JwtClaims::now())?; diff --git a/test-files/es256-private.jwks.json b/test-files/es256-private.jwks.json new file mode 100644 index 0000000..fb2bbcf --- /dev/null +++ b/test-files/es256-private.jwks.json @@ -0,0 +1,11 @@ +{ + "crv": "P-256", + "d": "3XAy6ko-fegKfkV5uE5nKY4wTncjvFMYTJM0uh4rj8E", + "ext": true, + "key_ops": [ + "sign" + ], + "kty": "EC", + "x": "Q5PISQVZ7LMwDNrjCNUcPLR45-zArUIqVazbscbLZ5Y", + "y": "cum17-MtTGX2X7yrI0tvp0Xl9NVEmHUuotAwyJHOv7I" +} \ No newline at end of file diff --git a/test-files/es256-public.jwks.json b/test-files/es256-public.jwks.json new file mode 100644 index 0000000..b78824b --- /dev/null +++ b/test-files/es256-public.jwks.json @@ -0,0 +1,10 @@ +{ + "crv": "P-256", + "ext": true, + "key_ops": [ + "verify" + ], + "kty": "EC", + "x": "Q5PISQVZ7LMwDNrjCNUcPLR45-zArUIqVazbscbLZ5Y", + "y": "cum17-MtTGX2X7yrI0tvp0Xl9NVEmHUuotAwyJHOv7I" +} \ No newline at end of file diff --git a/test-files/rs256-private.jwks.json b/test-files/rs256-private.jwks.json new file mode 100644 index 0000000..3819cd1 --- /dev/null +++ b/test-files/rs256-private.jwks.json @@ -0,0 +1,16 @@ +{ + "alg": "RS256", + "d": "AWwlG9V-uOv5_VzUvnELzpaqnfmHcknukEUUDJIOv8GLESbRLLdyIP2N3zHlm0Ml2qumifJ6Z_NSxk-R0NskCIsKYmgTcMDmla_boF3eYcOq0ssByCND31mOG31FdqXZFYg2QDgwPK-ZL9ztYP3AJmJS9MYNVY1LckuPyEP6fERL63CYqIH7TxYfgHp5h3kwr0EsdF7aOCc41XLrUbXAptTQT82Ny3KQbG7B0FQo-ot2HSMkrDxsCFj8Cqrlqvre3xC9bJeZkuJR83Z093gM8-DrHJ5SzzkiczHHpZF17SnlyEsGsM3J1OX9tG1qPpmbBbgcaUlNPoWX4wu1LOMwQQ", + "dp": "B0bhmelCm1rdRTims3yDdbwctdnxyQYrfxuHDwk5ISZEriFmQRwFu-mk7MD58_BgMca86pcfU_4o-9ntk06pbeQhwQtTjiLpk2KfRlhw4s-EWh_V2YddPxcZIUK7v03_uiZqWwKlVdhNxV44DS6JnSx0Gq_jHXxVe_r0Vyz9458", + "dq": "prODOLzzxjRu2EgEfe4maUxYhLvPlCIhjUZeu1bX19AG9TLMPmmi08JS1iOXPtP6cyOc_jiV5hrP5t1qWl71j0zVoCVoI91XlBFpYZvcpWPcvL_Uvy2ACxn8Nrs0qfbLuN0WhE7cFFBl1ZiEXRPloJxxVjp9IJ_YaAro9Gfyd_U", + "e": "AQAB", + "ext": true, + "key_ops": [ + "sign" + ], + "kty": "RSA", + "n": "3t46Cq-6fA5fPu20XRLczcEWbNq_43dtw0cwkm0MTR5nxQAGXAIXWVYJM543nda3Ug66rslPIC8h39xnx5R6j4VJ-oLhCECk5_cC722_bPhQ5MrX9RmJS12Rxp1Cv5Qevwtoe4wrjTBb_4uaATdBezgTu18ETdlxD3r58unm-ERub9aNZIUqmZzYZfDFqf5arMUdEX5nVn-fYi_TYVeoPlPL7xFMpeWtII1xKRVQHkHhmg2ZTbTSRRd5sBxi_k79dbochxXGPb49bwurDwiRd4k15g6LoHvCiiU-dJ01jxy2C58RddZz_46UAaZo4P1hE2_7vw7j2VQ2vvZmZyShwQ", + "p": "_P_7wwC_2IsnQqvsdlGdKi2xOc7JrBBeJ0CIKDzda1fYZd4ZmejBYhsBATV4Cy5UOOURbZpnXcZHKlxFs7UiKcUCvXbqD9E-GurEguGcNuvsH4h8CweTJJRVVjhcoH3mN23wKw04bWm-OU9ek9cX_7zP8VTakT2nOTw0-7MBTjc", + "q": "4YLGGMCU0hosTonxmEuGKe3aXF7CL0rYHz1vPf4NE5_9UnQyhg91KKm44Gx1_8jW35jv3yPxqYhVUUn_DDao5WQDqXBrFiCsvQ3SFdy2lmRZKKjvLioUW6kU0CdKVY8442U7aakGB70XgRXKvkwWDGsXCvVle_vc09oV6lbjU8c", + "qi": "QsA_u5DtRHg_fZDMkU9-LoE1m5_p3ea13_VrAaXbLx97KGdtNT1X3GO62KfgV7uesS4F__goK9IsW6FQelDdz2PqvsB8pQVv1zVDCB-0__FKoueiYcJy3ECUPEEUxEE_kvknAiZM1Lyp240BWWCSKzw1dKmMtlO4sQeW7H9tzUE" +} \ No newline at end of file diff --git a/test-files/rs256-public.jwks.json b/test-files/rs256-public.jwks.json new file mode 100644 index 0000000..7ff20b9 --- /dev/null +++ b/test-files/rs256-public.jwks.json @@ -0,0 +1,10 @@ +{ + "alg": "RS256", + "e": "AQAB", + "ext": true, + "key_ops": [ + "verify" + ], + "kty": "RSA", + "n": "3t46Cq-6fA5fPu20XRLczcEWbNq_43dtw0cwkm0MTR5nxQAGXAIXWVYJM543nda3Ug66rslPIC8h39xnx5R6j4VJ-oLhCECk5_cC722_bPhQ5MrX9RmJS12Rxp1Cv5Qevwtoe4wrjTBb_4uaATdBezgTu18ETdlxD3r58unm-ERub9aNZIUqmZzYZfDFqf5arMUdEX5nVn-fYi_TYVeoPlPL7xFMpeWtII1xKRVQHkHhmg2ZTbTSRRd5sBxi_k79dbochxXGPb49bwurDwiRd4k15g6LoHvCiiU-dJ01jxy2C58RddZz_46UAaZo4P1hE2_7vw7j2VQ2vvZmZyShwQ" +} \ No newline at end of file From c83c3f34de2a48740200c6cbda946fd1d7895c23 Mon Sep 17 00:00:00 2001 From: rster2002 Date: Tue, 17 Mar 2026 19:53:57 +0100 Subject: [PATCH 07/17] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20ES=20and?= =?UTF-8?q?=20HS=20algorithms?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 23 ++++ Cargo.toml | 4 + src/modules/algorithm/mod.rs | 21 ++- src/modules/algorithm/models.rs | 7 +- .../models/es256_algorithm/es256_private.rs | 129 ------------------ .../es256_algorithm/es256_private_params.rs | 9 -- .../models/es256_algorithm/es256_public.rs | 22 --- .../algorithm/models/es256_algorithm/mod.rs | 3 - .../models/es_algorithm/es_private.rs | 55 +++++--- .../models/es_algorithm/es_public.rs | 7 - .../algorithm/models/es_algorithm/mod.rs | 3 +- .../models/hs256_algorithm/hs256_private.rs | 77 ----------- .../algorithm/models/hs256_algorithm/mod.rs | 1 - .../algorithm/models/hs_algorithm/mod.rs | 113 +++++++++++++++ .../algorithm/models/none_algorithm.rs | 4 - .../models/rs256_algorithm/rs256_private.rs | 4 - .../models/rs256_algorithm/rs256_public.rs | 6 +- src/modules/algorithm/traits/jw_alg_verify.rs | 3 +- src/modules/token/models/jwt.rs | 8 +- 19 files changed, 205 insertions(+), 294 deletions(-) delete mode 100644 src/modules/algorithm/models/es256_algorithm/es256_private.rs delete mode 100644 src/modules/algorithm/models/es256_algorithm/es256_private_params.rs delete mode 100644 src/modules/algorithm/models/es256_algorithm/es256_public.rs delete mode 100644 src/modules/algorithm/models/es256_algorithm/mod.rs delete mode 100644 src/modules/algorithm/models/es_algorithm/es_public.rs delete mode 100644 src/modules/algorithm/models/hs256_algorithm/hs256_private.rs delete mode 100644 src/modules/algorithm/models/hs256_algorithm/mod.rs create mode 100644 src/modules/algorithm/models/hs_algorithm/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 14129ee..9806b08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -184,6 +184,7 @@ dependencies = [ "ff", "generic-array", "group", + "hkdf", "pem-rfc7468", "pkcs8", "rand_core", @@ -237,6 +238,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -294,6 +304,7 @@ dependencies = [ "ecdsa", "hmac", "p256", + "p384", "pkcs1", "rsa", "serde", @@ -400,6 +411,18 @@ dependencies = [ "sha2", ] +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index 93ac0ff..e9558ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,11 +13,15 @@ sha2 = { version = "0.10.8", features = ["oid"], optional = true } rsa = { version = "0.9.7", optional = true } pkcs1 = "0.7.5" p256 = { version = "0.13.2", features = ["pem", "arithmetic", "jwk"], optional = true } +p384 = { version ="0.13.1", optional = true } chrono = "0.4.39" ecdsa = "0.16.9" [features] default = ["hs256"] hs256 = ["dep:hmac", "dep:sha2"] +hs384 = ["dep:hmac", "dep:sha2"] +hs512 = ["dep:hmac", "dep:sha2"] rs256 = ["dep:rsa", "dep:sha2"] es256 = ["dep:p256"] +es384 = ["dep:p384"] diff --git a/src/modules/algorithm/mod.rs b/src/modules/algorithm/mod.rs index ca05d80..382362c 100644 --- a/src/modules/algorithm/mod.rs +++ b/src/modules/algorithm/mod.rs @@ -3,16 +3,31 @@ mod traits; pub use models::none_algorithm::NoneAlgorithm; +#[cfg(any(feature = "hs256", feature = "hs384", feature = "hs512"))] +use crate::algorithm::models::hs_algorithm::HSAlg; + #[cfg(feature = "hs256")] -pub use models::hs256_algorithm::hs256_private::HS256Private; +pub type HS256Private = HSAlg; + +#[cfg(feature = "hs384")] +pub type HS384Private = HSAlg; + +#[cfg(feature = "hs512")] +pub type HS512Private = HSAlg; #[cfg(feature = "rs256")] pub use models::rs256_algorithm::rs256_public::RS256Public; pub use models::rs256_algorithm::rs256_private::RS256Private; +#[cfg(any(feature = "es256", feature = "es384"))] +use crate::algorithm::models::es_algorithm::es_private::ESPrivate; + #[cfg(feature = "es256")] -pub use models::es256_algorithm::es256_public::ES256Public; -pub use models::es256_algorithm::es256_private::ES256Private; +pub type ES256Private = ESPrivate; + +#[cfg(feature = "es384")] +pub type ES384Private = ESPrivate; pub use traits::jw_alg_verify::JwAlgVerify; pub use traits::jw_alg_sign::JwAlgSign; +pub use traits::jw_alg::JwAlg; diff --git a/src/modules/algorithm/models.rs b/src/modules/algorithm/models.rs index 63de14f..b1984fe 100644 --- a/src/modules/algorithm/models.rs +++ b/src/modules/algorithm/models.rs @@ -1,11 +1,10 @@ pub mod none_algorithm; -#[cfg(feature = "hs256")] -pub mod hs256_algorithm; +#[cfg(any(feature = "hs256"))] +pub mod hs_algorithm; #[cfg(feature = "rs256")] pub mod rs256_algorithm; -#[cfg(feature = "es256")] -pub mod es256_algorithm; +#[cfg(any(feature = "es256", feature = "es384"))] pub mod es_algorithm; \ No newline at end of file diff --git a/src/modules/algorithm/models/es256_algorithm/es256_private.rs b/src/modules/algorithm/models/es256_algorithm/es256_private.rs deleted file mode 100644 index e54cba6..0000000 --- a/src/modules/algorithm/models/es256_algorithm/es256_private.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::convert::Infallible; -use std::fmt::{Debug, Formatter}; -use p256::ecdsa::{SigningKey, Signature, signature::Signer}; -use p256::ecdsa::signature::Verifier; -use p256::elliptic_curve::SecretKey; -use p256::NistP256; -use crate::algorithm::{JwAlgVerify, JwAlgSign}; -use crate::algorithm::models::es256_algorithm::es256_private_params::ES256PrivateParams; -use crate::modules::key::JwKeyType; - -/// ```shell -/// openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out es256.pem -/// ``` -#[derive(Clone)] -pub struct ES256Private { - inner: SecretKey, - signing_key: SigningKey, -} - -// impl From> for ES256Private { -// fn from(value: SecretKey) -> Self { -// ES256Private { -// signing_key: SigningKey::from(value.clone()), -// inner: value, -// } -// } -// } -// -// impl JwAlgVerify for ES256Private { -// type Error = Infallible; -// -// fn alg() -> impl AsRef { -// "ES256" -// } -// -// fn verify(&self, payload: &str, signature: &[u8]) -> Result { -// let verifying_key = self.signing_key.verifying_key(); -// let signature = Signature::try_from(signature).unwrap(); -// -// Ok(verifying_key.verify(payload.as_bytes(), &signature).is_ok()) -// } -// } -// -// impl JwAlgSign for ES256Private { -// fn sign(&self, payload: &str) -> Vec { -// let signature: Signature = self.signing_key.sign(payload.as_bytes()); -// signature.to_vec() -// } -// } - -// impl JwKeyType<'_> for ES256Private { -// type Params = ES256PrivateParams; -// -// fn kty() -> impl AsRef { -// "EC" -// } -// -// fn parms(&self) -> Self::Params { -// let base_value = serde_json::to_value(self.inner.clone().to_jwk()) -// .expect("should always work"); -// -// let key = base_value -// .as_object() -// .expect("should always be an object"); -// -// ES256PrivateParams { -// crv: key.get("crv") -// .expect("should always have a value") -// .as_str() -// .expect("should always be a string") -// .to_string(), -// -// x: key.get("x") -// .expect("should always have a value") -// .as_str() -// .expect("should always be a string") -// .to_string(), -// -// y: key.get("y") -// .expect("should always have a value") -// .as_str() -// .expect("should always be a string") -// .to_string(), -// -// d: key.get("d") -// .expect("should always have a value") -// .as_str() -// .expect("should always be a string") -// .to_string(), -// } -// } -// } - -impl Debug for ES256Private { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "ES256Algorithm {{ .. }}") - } -} - -#[cfg(test)] -mod tests { - use base64::Engine; - use base64::prelude::BASE64_URL_SAFE_NO_PAD; - use p256::ecdsa::SigningKey; - use p256::SecretKey; - use crate::algorithm::{ES256Private, JwAlgVerify, JwAlgSign}; - - #[test] - fn es256_algorithm_works_as_expected() { - let payload = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0"; - let secret_key = include_str!("../../../../../test-files/es256.key").parse::().unwrap(); - - let alg = ES256Private::from(secret_key); - - let signature_bytes = alg.sign(payload); - let signature_string = BASE64_URL_SAFE_NO_PAD.encode(&signature_bytes); - - assert_eq!(signature_string, "XX7zPdDrYpegeS7mBfBIUVXnqVT-XSemrGjgoZBlrN0--n94Lv03J9vzbDDJXPzxnSs_62ymIJr1zBMaoMAveA"); - - let verify = alg.verify(payload, &signature_bytes).unwrap(); - - assert!(verify); - } - - #[test] - fn jwk_is_generated_correctly() { - - } -} diff --git a/src/modules/algorithm/models/es256_algorithm/es256_private_params.rs b/src/modules/algorithm/models/es256_algorithm/es256_private_params.rs deleted file mode 100644 index e8bb3e5..0000000 --- a/src/modules/algorithm/models/es256_algorithm/es256_private_params.rs +++ /dev/null @@ -1,9 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize)] -pub struct ES256PrivateParams { - pub crv: String, - pub d: String, - pub x: String, - pub y: String, -} diff --git a/src/modules/algorithm/models/es256_algorithm/es256_public.rs b/src/modules/algorithm/models/es256_algorithm/es256_public.rs deleted file mode 100644 index 57f5484..0000000 --- a/src/modules/algorithm/models/es256_algorithm/es256_public.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::convert::Infallible; -use p256::ecdsa::{VerifyingKey, Signature, signature::Verifier}; -use crate::algorithm::JwAlgVerify; - -#[derive(Clone)] -pub struct ES256Public { - inner: VerifyingKey, -} - -impl JwAlgVerify for ES256Public { - type Error = Infallible; - - fn alg() -> impl AsRef { - "ES256" - } - - fn verify(&self, payload: &str, signature: &[u8]) -> Result { - let signature = Signature::try_from(signature).unwrap(); - - Ok(self.inner.verify(payload.as_bytes(), &signature).is_ok()) - } -} diff --git a/src/modules/algorithm/models/es256_algorithm/mod.rs b/src/modules/algorithm/models/es256_algorithm/mod.rs deleted file mode 100644 index 267a881..0000000 --- a/src/modules/algorithm/models/es256_algorithm/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod es256_private; -pub mod es256_public; -mod es256_private_params; \ No newline at end of file diff --git a/src/modules/algorithm/models/es_algorithm/es_private.rs b/src/modules/algorithm/models/es_algorithm/es_private.rs index eb0066b..01bcec8 100644 --- a/src/modules/algorithm/models/es_algorithm/es_private.rs +++ b/src/modules/algorithm/models/es_algorithm/es_private.rs @@ -1,43 +1,58 @@ use std::convert::Infallible; -use ecdsa::elliptic_curve::{Curve, SecretKey}; -use ecdsa::SigningKey; -use hmac::Mac; +use ecdsa::elliptic_curve::{AffinePoint, CurveArithmetic, PrimeCurve, Scalar}; +use ecdsa::elliptic_curve::ops::Invert; +use ecdsa::elliptic_curve::subtle::CtOption; +use ecdsa::hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive}; +use ecdsa::{Signature, SignatureSize, SigningKey}; +use ecdsa::elliptic_curve::generic_array::ArrayLength; +use ecdsa::signature::{Signer, Verifier}; use crate::algorithm::{JwAlgSign, JwAlgVerify}; -use p256::NistP256; use crate::algorithm::traits::jw_alg::JwAlg; -#[derive(Clone)] -pub struct ESPrivate -where C : Curve, -{ - inner: SecretKey, - signing_key: SigningKey, -} +pub struct ESPrivate(SigningKey) +where C: PrimeCurve + CurveArithmetic + DigestPrimitive, + Scalar: Invert>> + SignPrimitive, + AffinePoint: VerifyPrimitive, + SignatureSize: ArrayLength; -impl JwAlg for ESPrivate { +#[cfg(feature = "es256")] +impl JwAlg for ESPrivate { fn alg() -> impl AsRef { "ES256" } } -impl JwAlgVerify for ESPrivate -where C : Curve, +#[cfg(feature = "es384")] +impl JwAlg for ESPrivate { + fn alg() -> impl AsRef { + "ES384" + } +} + +impl JwAlgVerify for SigningKey +where C: PrimeCurve + CurveArithmetic + DigestPrimitive, + Scalar: Invert>> + SignPrimitive, + AffinePoint: VerifyPrimitive, + SignatureSize: ArrayLength, { type Error = Infallible; fn verify(&self, payload: &str, signature: &[u8]) -> Result { - let verifying_key = self.signing_key.verifying_key(); - let signature = Signature::try_from(signature).unwrap(); + let verifying_key = self.verifying_key(); + let signature = Signature::::try_from(signature).unwrap(); // TODO return false Ok(verifying_key.verify(payload.as_bytes(), &signature).is_ok()) } } -impl JwAlgSign for ESPrivate -where C : Curve, +impl JwAlgSign for SigningKey +where C: PrimeCurve + CurveArithmetic + DigestPrimitive, + Scalar: Invert>> + SignPrimitive, + AffinePoint: VerifyPrimitive, + SignatureSize: ArrayLength, { fn sign(&self, payload: &str) -> Vec { - let signature: Signature = self.signing_key.sign(payload.as_bytes()); + let signature: Signature = Signer::sign(self, payload.as_bytes()); signature.to_vec() } -} +} \ No newline at end of file diff --git a/src/modules/algorithm/models/es_algorithm/es_public.rs b/src/modules/algorithm/models/es_algorithm/es_public.rs deleted file mode 100644 index 074e79d..0000000 --- a/src/modules/algorithm/models/es_algorithm/es_public.rs +++ /dev/null @@ -1,7 +0,0 @@ -use p256::elliptic_curve::{Curve}; - -pub struct ESPublic -where C : Curve -{ - inner: VerifyingKey, -} \ No newline at end of file diff --git a/src/modules/algorithm/models/es_algorithm/mod.rs b/src/modules/algorithm/models/es_algorithm/mod.rs index b1a6fec..3da5752 100644 --- a/src/modules/algorithm/models/es_algorithm/mod.rs +++ b/src/modules/algorithm/models/es_algorithm/mod.rs @@ -1,2 +1 @@ -pub mod es_private; -pub mod es_public; \ No newline at end of file +pub mod es_private; \ No newline at end of file diff --git a/src/modules/algorithm/models/hs256_algorithm/hs256_private.rs b/src/modules/algorithm/models/hs256_algorithm/hs256_private.rs deleted file mode 100644 index ca5fca8..0000000 --- a/src/modules/algorithm/models/hs256_algorithm/hs256_private.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::convert::Infallible; -use std::fmt::{Debug, Formatter}; -use hmac::{Hmac, Mac}; -use hmac::digest::InvalidLength; -use sha2::Sha256; -use crate::algorithm::{JwAlgVerify, JwAlgSign}; -use crate::modules::key::JwKeyType; - -#[derive(Clone)] -pub struct HS256Private { - inner: Hmac, -} - -impl HS256Private { - pub fn new(key: &[u8]) -> Result { - Ok(HS256Private { - inner: Hmac::::new_from_slice(key)? - }) - } -} - -impl JwAlgVerify for HS256Private { - type Error = Infallible; - - fn alg() -> impl AsRef { - "HS256" - } - - fn verify(&self, payload: &str, signature: &[u8]) -> Result { - let mut inner = self.inner.clone(); - inner.update(payload.as_bytes()); - - let finalized = inner.finalize() - .into_bytes() - .to_vec(); - - Ok(signature == finalized) - } -} - -impl JwAlgSign for HS256Private { - fn sign(&self, payload: &str) -> Vec { - let mut inner = self.inner.clone(); - inner.update(payload.as_bytes()); - - inner.finalize().into_bytes().to_vec() - } -} - -impl Debug for HS256Private { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "HS256Algorithm {{ .. }}") - } -} - -#[cfg(test)] -mod tests { - use base64::Engine; - use base64::prelude::BASE64_URL_SAFE_NO_PAD; - use crate::algorithm::JwAlgSign; - use crate::modules::algorithm::{HS256Private, JwAlgVerify}; - - #[test] - fn hs256_algorithm_works_as_expected() { - let payload = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJoaiI6dHJ1ZX0"; - let alg = HS256Private::new("qwed".as_ref()).unwrap(); - - let signature_bytes = alg.sign(payload); - let signature_string = BASE64_URL_SAFE_NO_PAD.encode(&signature_bytes); - - assert_eq!(signature_string, "AeQU9YyCnBlrJwtd1PVmGW3apn6kQ6yi_U4qT9o0vkQ"); - - let verify = alg.verify(payload, &signature_bytes).unwrap(); - - assert!(verify); - } -} diff --git a/src/modules/algorithm/models/hs256_algorithm/mod.rs b/src/modules/algorithm/models/hs256_algorithm/mod.rs deleted file mode 100644 index cadb704..0000000 --- a/src/modules/algorithm/models/hs256_algorithm/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod hs256_private; \ No newline at end of file diff --git a/src/modules/algorithm/models/hs_algorithm/mod.rs b/src/modules/algorithm/models/hs_algorithm/mod.rs new file mode 100644 index 0000000..c108df2 --- /dev/null +++ b/src/modules/algorithm/models/hs_algorithm/mod.rs @@ -0,0 +1,113 @@ +use std::convert::Infallible; +use std::fmt::{Debug}; +use ecdsa::elliptic_curve::consts::U256; +use ecdsa::elliptic_curve::generic_array::typenum::{IsLess, Le, NonZero}; +use ecdsa::signature::digest::block_buffer::Eager; +use ecdsa::signature::digest::core_api::{BlockSizeUser, BufferKindUser, CoreProxy, FixedOutputCore, UpdateCore}; +use ecdsa::signature::digest::HashMarker; +use hmac::{Hmac, Mac}; +use sha2::Sha256; +use crate::algorithm::{JwAlgVerify, JwAlgSign, JwAlg}; +use crate::modules::key::JwKeyType; +use hmac::digest::InvalidLength; + +pub struct HSAlg(Hmac) +where D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero; + +impl HSAlg +where D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + pub fn new(key: &[u8]) -> Result { + Ok(HSAlg(Hmac::::new_from_slice(key)?)) + } +} + +#[cfg(feature = "hs256")] +impl JwAlg for HSAlg { + fn alg() -> impl AsRef { + "HS256" + } +} + +impl JwAlgVerify for HSAlg +where D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + type Error = Infallible; + + fn verify(&self, payload: &str, signature: &[u8]) -> Result { + let mut inner = self.0.clone(); + inner.update(payload.as_bytes()); + + let finalized = inner.finalize() + .into_bytes() + .to_vec(); + + Ok(signature == finalized) + } +} + +impl JwAlgSign for HSAlg +where D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + fn sign(&self, payload: &str) -> Vec { + let mut inner = self.0.clone(); + inner.update(payload.as_bytes()); + + inner.finalize().into_bytes().to_vec() + } +} + +#[cfg(test)] +mod tests { + use base64::Engine; + use base64::prelude::BASE64_URL_SAFE_NO_PAD; + use crate::algorithm::JwAlgSign; + use crate::modules::algorithm::{HS256Private, JwAlgVerify}; + + #[test] + fn hs256_algorithm_works_as_expected() { + let payload = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJoaiI6dHJ1ZX0"; + let alg = HS256Private::new("qwed".as_ref()).unwrap(); + + let signature_bytes = alg.sign(payload); + let signature_string = BASE64_URL_SAFE_NO_PAD.encode(&signature_bytes); + + assert_eq!(signature_string, "AeQU9YyCnBlrJwtd1PVmGW3apn6kQ6yi_U4qT9o0vkQ"); + + let verify = alg.verify(payload, &signature_bytes).unwrap(); + + assert!(verify); + } +} diff --git a/src/modules/algorithm/models/none_algorithm.rs b/src/modules/algorithm/models/none_algorithm.rs index 6160c05..80e13e7 100644 --- a/src/modules/algorithm/models/none_algorithm.rs +++ b/src/modules/algorithm/models/none_algorithm.rs @@ -7,10 +7,6 @@ pub struct NoneAlgorithm; impl JwAlgVerify for NoneAlgorithm { type Error = Infallible; - fn alg() -> impl AsRef { - "none" - } - fn verify(&self, _: &str, _: &[u8]) -> Result { Ok(true) } diff --git a/src/modules/algorithm/models/rs256_algorithm/rs256_private.rs b/src/modules/algorithm/models/rs256_algorithm/rs256_private.rs index 16127ac..f3922b9 100644 --- a/src/modules/algorithm/models/rs256_algorithm/rs256_private.rs +++ b/src/modules/algorithm/models/rs256_algorithm/rs256_private.rs @@ -35,10 +35,6 @@ impl Debug for RS256Private { impl JwAlgVerify for RS256Private { type Error = rsa::signature::Error; - fn alg() -> impl AsRef { - "RS256" - } - fn verify(&self, payload: &str, signature: &[u8]) -> Result { let signature = Signature::try_from(signature)?; diff --git a/src/modules/algorithm/models/rs256_algorithm/rs256_public.rs b/src/modules/algorithm/models/rs256_algorithm/rs256_public.rs index b63bc85..227bc03 100644 --- a/src/modules/algorithm/models/rs256_algorithm/rs256_public.rs +++ b/src/modules/algorithm/models/rs256_algorithm/rs256_public.rs @@ -13,9 +13,9 @@ pub struct RS256Public { impl JwAlgVerify for RS256Public { type Error = rsa::signature::Error; - fn alg() -> impl AsRef { - "RS256" - } + // fn alg() -> impl AsRef { + // "RS256" + // } fn verify(&self, payload: &str, signature: &[u8]) -> Result { let signature = Signature::try_from(signature)?; diff --git a/src/modules/algorithm/traits/jw_alg_verify.rs b/src/modules/algorithm/traits/jw_alg_verify.rs index 80a425d..596c05d 100644 --- a/src/modules/algorithm/traits/jw_alg_verify.rs +++ b/src/modules/algorithm/traits/jw_alg_verify.rs @@ -1,8 +1,7 @@ -use crate::algorithm::traits::jw_alg::JwAlg; +use crate::algorithm::JwAlg; pub trait JwAlgVerify { type Error: std::error::Error; - // fn alg() -> impl AsRef; fn verify(&self, payload: &str, signature: &[u8]) -> Result; } diff --git a/src/modules/token/models/jwt.rs b/src/modules/token/models/jwt.rs index 48d768c..fa2d82c 100644 --- a/src/modules/token/models/jwt.rs +++ b/src/modules/token/models/jwt.rs @@ -5,7 +5,7 @@ use base64::prelude::BASE64_URL_SAFE_NO_PAD; use chrono::{DateTime, Duration, Utc}; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; -use crate::algorithm::{JwAlgVerify, JwAlgSign}; +use crate::algorithm::{JwAlgVerify, JwAlgSign, JwAlg}; use crate::token::{JwtError, JwtHeader}; use crate::token::models::jwt_claims::JwtClaims; @@ -21,7 +21,7 @@ impl Jwt where T : Serialize + for<'a> Deserialize<'a>, { /// Takes the JWT instance, signs it, and returns the string representation for the token. - pub fn into_token(self, algorithm: &A) -> Result { + pub fn into_token(self, algorithm: &A) -> Result { let alg_ref = A::alg(); let header = JwtHeader { @@ -63,7 +63,7 @@ where T : Serialize + for<'a> Deserialize<'a>, /// instance with the expected payload. Note that this does not check any claims. To verify /// basic expiry claims, you can use [Jwt::verify_now] or you can further verify the token using /// [Jwt::against] or [Jwt::guard]. - pub fn check(token: &str, algorithm: &A) -> Result, JwtError> + pub fn check(token: &str, algorithm: &A) -> Result, JwtError> where ::Error: 'static { let mut parts = token.split('.'); @@ -103,7 +103,7 @@ where T : Serialize + for<'a> Deserialize<'a>, /// Largely the same as [Jwt::check], but also verifies basic expiry claims. You can further /// verify the token using [Jwt::against] or [Jwt::guard]. - pub fn verify_now(token: &str, algorithm: &A) -> Result, JwtError> + pub fn verify_now(token: &str, algorithm: &A) -> Result, JwtError> where ::Error: 'static { let jwt = Jwt::::check(token, algorithm)? From 4c46ccbad397c3a4ae90535f8f6123db2f9460e2 Mon Sep 17 00:00:00 2001 From: rster2002 Date: Tue, 17 Mar 2026 20:41:50 +0100 Subject: [PATCH 08/17] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Continue=20working?= =?UTF-8?q?=20on=20refactor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 7 ++ src/modules/algorithm/mod.rs | 54 ++++++++--- src/modules/algorithm/models.rs | 6 +- .../models/es_algorithm/es_public.rs | 41 ++++++++ .../algorithm/models/es_algorithm/mod.rs | 3 +- .../algorithm/models/hs_algorithm/mod.rs | 1 - .../algorithm/models/rs256_algorithm/mod.rs | 2 - .../models/rs256_algorithm/rs256_private.rs | 95 ------------------- .../models/rs256_algorithm/rs256_public.rs | 24 ----- .../algorithm/models/rs_algorithm/mod.rs | 2 + .../models/rs_algorithm/rs_private.rs | 93 ++++++++++++++++++ .../models/rs_algorithm/rs_public.rs | 39 ++++++++ src/modules/key/mod.rs | 6 +- src/modules/key/models.rs | 6 +- src/modules/key/models/jwk.rs | 15 --- src/modules/key/models/jwk_sign.rs | 3 - src/modules/key/models/rsa_private_jwk.rs | 7 -- 17 files changed, 234 insertions(+), 170 deletions(-) create mode 100644 src/modules/algorithm/models/es_algorithm/es_public.rs delete mode 100644 src/modules/algorithm/models/rs256_algorithm/mod.rs delete mode 100644 src/modules/algorithm/models/rs256_algorithm/rs256_private.rs delete mode 100644 src/modules/algorithm/models/rs256_algorithm/rs256_public.rs create mode 100644 src/modules/algorithm/models/rs_algorithm/mod.rs create mode 100644 src/modules/algorithm/models/rs_algorithm/rs_private.rs create mode 100644 src/modules/algorithm/models/rs_algorithm/rs_public.rs delete mode 100644 src/modules/key/models/jwk.rs delete mode 100644 src/modules/key/models/jwk_sign.rs delete mode 100644 src/modules/key/models/rsa_private_jwk.rs diff --git a/Cargo.toml b/Cargo.toml index e9558ed..3dd01f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,9 +19,16 @@ ecdsa = "0.16.9" [features] default = ["hs256"] +hmac = ["hs256", "hs384", "hs512"] hs256 = ["dep:hmac", "dep:sha2"] hs384 = ["dep:hmac", "dep:sha2"] hs512 = ["dep:hmac", "dep:sha2"] + +rsassa-pkcs1-v1_5 = ["rs256", "rs384", "rs512"] rs256 = ["dep:rsa", "dep:sha2"] +rs384 = ["dep:rsa", "dep:sha2"] +rs512 = ["dep:rsa", "dep:sha2"] + +ecdsa = ["es256", "es384"] es256 = ["dep:p256"] es384 = ["dep:p384"] diff --git a/src/modules/algorithm/mod.rs b/src/modules/algorithm/mod.rs index 382362c..7431df9 100644 --- a/src/modules/algorithm/mod.rs +++ b/src/modules/algorithm/mod.rs @@ -1,8 +1,32 @@ mod models; mod traits; +pub use traits::jw_alg_verify::JwAlgVerify; +pub use traits::jw_alg_sign::JwAlgSign; +pub use traits::jw_alg::JwAlg; + pub use models::none_algorithm::NoneAlgorithm; +// ES +#[cfg(any(feature = "es256", feature = "es384"))] +use crate::algorithm::models::es_algorithm::es_private::ESPrivate; + +#[cfg(any(feature = "es256", feature = "es384"))] +use crate::algorithm::models::es_algorithm::es_public::ESPublic; + +#[cfg(feature = "es256")] +pub type ES256Private = ESPrivate; + +#[cfg(feature = "es256")] +pub type ES256Public = ESPublic; + +#[cfg(feature = "es384")] +pub type ES384Private = ESPrivate; + +#[cfg(feature = "es384")] +pub type ES384Public = ESPublic; + +// HS #[cfg(any(feature = "hs256", feature = "hs384", feature = "hs512"))] use crate::algorithm::models::hs_algorithm::HSAlg; @@ -15,19 +39,27 @@ pub type HS384Private = HSAlg; #[cfg(feature = "hs512")] pub type HS512Private = HSAlg; +// RS +#[cfg(any(feature = "rs256", feature = "rs384", feature = "rs512"))] +use crate::algorithm::models::rs_algorithm::rs_private::RSPrivate; + +#[cfg(any(feature = "rs256", feature = "rs384", feature = "rs512"))] +use crate::algorithm::models::rs_algorithm::rs_public::RSPublic; + #[cfg(feature = "rs256")] -pub use models::rs256_algorithm::rs256_public::RS256Public; -pub use models::rs256_algorithm::rs256_private::RS256Private; +pub type RS256Private = RSPrivate; -#[cfg(any(feature = "es256", feature = "es384"))] -use crate::algorithm::models::es_algorithm::es_private::ESPrivate; +#[cfg(feature = "rs256")] +pub type RS256Public = RSPublic; -#[cfg(feature = "es256")] -pub type ES256Private = ESPrivate; +#[cfg(feature = "rs384")] +pub type RS384Private = RSPrivate; -#[cfg(feature = "es384")] -pub type ES384Private = ESPrivate; +#[cfg(feature = "rs384")] +pub type RS384Public = RSPublic; -pub use traits::jw_alg_verify::JwAlgVerify; -pub use traits::jw_alg_sign::JwAlgSign; -pub use traits::jw_alg::JwAlg; +#[cfg(feature = "rs512")] +pub type RS512Private = RSPrivate; + +#[cfg(feature = "rs512")] +pub type RS512Public = RSPublic; diff --git a/src/modules/algorithm/models.rs b/src/modules/algorithm/models.rs index b1984fe..0fc1af2 100644 --- a/src/modules/algorithm/models.rs +++ b/src/modules/algorithm/models.rs @@ -1,10 +1,10 @@ pub mod none_algorithm; -#[cfg(any(feature = "hs256"))] +#[cfg(any(feature = "hs256", feature = "hs384", feature = "hs512"))] pub mod hs_algorithm; -#[cfg(feature = "rs256")] -pub mod rs256_algorithm; +#[cfg(any(feature = "rs256", feature = "rs384", feature = "rs512"))] +pub mod rs_algorithm; #[cfg(any(feature = "es256", feature = "es384"))] pub mod es_algorithm; \ No newline at end of file diff --git a/src/modules/algorithm/models/es_algorithm/es_public.rs b/src/modules/algorithm/models/es_algorithm/es_public.rs new file mode 100644 index 0000000..0cac619 --- /dev/null +++ b/src/modules/algorithm/models/es_algorithm/es_public.rs @@ -0,0 +1,41 @@ +use ecdsa::{PrimeCurve, Signature, SignatureSize, VerifyingKey}; +use ecdsa::elliptic_curve::{AffinePoint, CurveArithmetic}; +use crate::algorithm::{JwAlg, JwAlgVerify}; +use ecdsa::elliptic_curve::generic_array::ArrayLength; +use ecdsa::hazmat::{DigestPrimitive, VerifyPrimitive}; +use ecdsa::signature::Verifier; + +pub struct ESPublic(VerifyingKey) +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive, + AffinePoint: VerifyPrimitive, + SignatureSize: ArrayLength; + +#[cfg(feature = "es256")] +impl JwAlg for ESPublic { + fn alg() -> impl AsRef { + "ES256" + } +} + +#[cfg(feature = "es384")] +impl JwAlg for ESPublic { + fn alg() -> impl AsRef { + "ES384" + } +} + +impl JwAlgVerify for ESPublic +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive, + AffinePoint: VerifyPrimitive, + SignatureSize: ArrayLength, +{ + type Error = ecdsa::Error; + + fn verify(&self, payload: &str, signature: &[u8]) -> Result { + let signature = Signature::::try_from(signature).unwrap(); // TODO return false + + Ok(self.0.verify(payload.as_bytes(), &signature).is_ok()) + } +} diff --git a/src/modules/algorithm/models/es_algorithm/mod.rs b/src/modules/algorithm/models/es_algorithm/mod.rs index 3da5752..b1a6fec 100644 --- a/src/modules/algorithm/models/es_algorithm/mod.rs +++ b/src/modules/algorithm/models/es_algorithm/mod.rs @@ -1 +1,2 @@ -pub mod es_private; \ No newline at end of file +pub mod es_private; +pub mod es_public; \ No newline at end of file diff --git a/src/modules/algorithm/models/hs_algorithm/mod.rs b/src/modules/algorithm/models/hs_algorithm/mod.rs index c108df2..f44f617 100644 --- a/src/modules/algorithm/models/hs_algorithm/mod.rs +++ b/src/modules/algorithm/models/hs_algorithm/mod.rs @@ -8,7 +8,6 @@ use ecdsa::signature::digest::HashMarker; use hmac::{Hmac, Mac}; use sha2::Sha256; use crate::algorithm::{JwAlgVerify, JwAlgSign, JwAlg}; -use crate::modules::key::JwKeyType; use hmac::digest::InvalidLength; pub struct HSAlg(Hmac) diff --git a/src/modules/algorithm/models/rs256_algorithm/mod.rs b/src/modules/algorithm/models/rs256_algorithm/mod.rs deleted file mode 100644 index c3bf060..0000000 --- a/src/modules/algorithm/models/rs256_algorithm/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod rs256_public; -pub mod rs256_private; \ No newline at end of file diff --git a/src/modules/algorithm/models/rs256_algorithm/rs256_private.rs b/src/modules/algorithm/models/rs256_algorithm/rs256_private.rs deleted file mode 100644 index f3922b9..0000000 --- a/src/modules/algorithm/models/rs256_algorithm/rs256_private.rs +++ /dev/null @@ -1,95 +0,0 @@ -use std::fmt::{Debug, Formatter}; -use base64::Engine; -use base64::prelude::BASE64_URL_SAFE_NO_PAD; -pub use rsa::pkcs1::DecodeRsaPrivateKey; -use rsa::pkcs1v15::{Signature, SigningKey}; -use rsa::RsaPrivateKey; -use rsa::signature::{Keypair, SignatureEncoding, Signer, Verifier}; -use rsa::traits::PublicKeyParts; -use serde::{Deserialize, Serialize}; -use sha2::Sha256; -use crate::algorithm::{JwAlgVerify, JwAlgSign}; -use crate::modules::key::{JwKeyType, RsaPublicJwk}; - -#[derive(Clone)] -pub struct RS256Private { - inner: RsaPrivateKey, - signing_key: SigningKey, -} - -impl RS256Private { - pub fn new(key: RsaPrivateKey) -> Self { - RS256Private { - signing_key: SigningKey::new(key.clone()), - inner: key, - } - } -} - -impl Debug for RS256Private { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "RS256Algorithm {{ .. }}") - } -} - -impl JwAlgVerify for RS256Private { - type Error = rsa::signature::Error; - - fn verify(&self, payload: &str, signature: &[u8]) -> Result { - let signature = Signature::try_from(signature)?; - - Ok(self.signing_key.verifying_key().verify(payload.as_bytes(), &signature).is_ok()) - } -} - -impl JwAlgSign for RS256Private { - fn sign(&self, payload: &str) -> Vec { - self.signing_key.sign(payload.as_bytes()).to_vec() - } -} - -// impl JwKeyType<'_> for RS256Algorithm { -// type Public = RsaPublicJwk; -// type Private = (); -// -// fn kty() -> impl AsRef { -// "RSA" -// } -// -// fn public_params(&self) -> Self::Public { -// let n = BASE64_URL_SAFE_NO_PAD.encode(self.inner.n().to_bytes_le()); -// let e = BASE64_URL_SAFE_NO_PAD.encode(self.inner.e().to_bytes_le()); -// -// RsaPublicJwk { -// n, -// e, -// } -// } -// } - -#[cfg(test)] -mod tests { - use base64::Engine; - use base64::prelude::BASE64_URL_SAFE_NO_PAD; - use pkcs1::DecodeRsaPrivateKey; - use rsa::pkcs1v15::SigningKey; - pub use rsa::RsaPrivateKey; - use crate::algorithm::{JwAlgVerify, JwAlgSign, RS256Private}; - - #[test] - fn rs256_algorithm_works_as_expected() { - let payload = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJoaiI6dHJ1ZX0"; - - let private_key = RsaPrivateKey::from_pkcs1_pem(include_str!("../../../../../test-files/rs256.key")).unwrap(); - let alg = RS256Private::new(private_key); - - let signature_bytes = alg.sign(payload); - let signature_string = BASE64_URL_SAFE_NO_PAD.encode(&signature_bytes); - - assert_eq!(signature_string, "ptH8Vc-nhm4gTl7HqaictKQyK3fxiJmSfyu-ouYlmIfyyRBIYw2tUdKxIsxgYMPXC7oV0-ShYtlUm73-q2buLoYGc52d-03RQghcVvZrag2nQCKsBBmTXFUADEaVopO65aND5h7Uif_1aQJXmX-40-V5te0fT3WSyU_1oKayxpi53_c7RXD7gDlWSXAZFDNhPopcRnq2_4FQylzFf4qbwtGWUNdJA4SGOikr1lsTrQRPGXLNXREG0PWv9GFoobQDTj9DWBG4B_cCAUVAjYUCx8BbgHSY9jeiYE_FbDykW0tRSA3XAYpf1QCPZmrCPButUixWY03FTTxsQxlJuY8r-w"); - - let verify = alg.verify(payload, &signature_bytes).unwrap(); - - assert!(verify); - } -} diff --git a/src/modules/algorithm/models/rs256_algorithm/rs256_public.rs b/src/modules/algorithm/models/rs256_algorithm/rs256_public.rs deleted file mode 100644 index 227bc03..0000000 --- a/src/modules/algorithm/models/rs256_algorithm/rs256_public.rs +++ /dev/null @@ -1,24 +0,0 @@ -use rsa::pkcs1v15::{Signature, VerifyingKey}; -use rsa::signature::Verifier; -use rsa::RsaPublicKey; -use sha2::Sha256; -use crate::algorithm::JwAlgVerify; - -#[derive(Clone)] -pub struct RS256Public { - inner: RsaPublicKey, - verifying_key: VerifyingKey, -} - -impl JwAlgVerify for RS256Public { - type Error = rsa::signature::Error; - - // fn alg() -> impl AsRef { - // "RS256" - // } - - fn verify(&self, payload: &str, signature: &[u8]) -> Result { - let signature = Signature::try_from(signature)?; - Ok(self.verifying_key.verify(payload.as_bytes(), &signature).is_ok()) - } -} diff --git a/src/modules/algorithm/models/rs_algorithm/mod.rs b/src/modules/algorithm/models/rs_algorithm/mod.rs new file mode 100644 index 0000000..01a4417 --- /dev/null +++ b/src/modules/algorithm/models/rs_algorithm/mod.rs @@ -0,0 +1,2 @@ +pub mod rs_public; +pub mod rs_private; \ No newline at end of file diff --git a/src/modules/algorithm/models/rs_algorithm/rs_private.rs b/src/modules/algorithm/models/rs_algorithm/rs_private.rs new file mode 100644 index 0000000..f3abd2b --- /dev/null +++ b/src/modules/algorithm/models/rs_algorithm/rs_private.rs @@ -0,0 +1,93 @@ +use crate::algorithm::{JwAlg, JwAlgSign, JwAlgVerify}; +use ecdsa::elliptic_curve::pkcs8::AssociatedOid; +use rsa::pkcs1v15::{Signature, SigningKey}; +use rsa::signature::{Keypair, SignatureEncoding, Signer, Verifier}; +use rsa::RsaPrivateKey; +use serde::{Deserialize, Serialize}; +use sha2::Digest; + +pub struct RSPrivate(SigningKey) +where D: Digest; + +#[cfg(feature = "rs256")] +impl JwAlg for RSPrivate { + fn alg() -> impl AsRef { + "RS256" + } +} + +#[cfg(feature = "rs384")] +impl JwAlg for RSPrivate { + fn alg() -> impl AsRef { + "RS384" + } +} + +#[cfg(feature = "rs512")] +impl JwAlg for RSPrivate { + fn alg() -> impl AsRef { + "RS512" + } +} + +impl JwAlgVerify for RSPrivate +where D: Digest, +{ + type Error = rsa::signature::Error; + + fn verify(&self, payload: &str, signature: &[u8]) -> Result { + let signature = Signature::try_from(signature)?; + + Ok(self.0.verifying_key().verify(payload.as_bytes(), &signature).is_ok()) + } +} + +impl JwAlgSign for RSPrivate +where D: Digest, +{ + fn sign(&self, payload: &str) -> Vec { + self.0.sign(payload.as_bytes()).to_vec() + } +} + +impl From> for RSPrivate +where D: Digest, +{ + fn from(key: SigningKey) -> Self { + Self(key) + } +} + +impl From for RSPrivate +where D: Digest + AssociatedOid, +{ + fn from(key: RsaPrivateKey) -> Self { + Self::from(SigningKey::new(key)) + } +} + +#[cfg(test)] +mod tests { + use crate::algorithm::{JwAlgSign, JwAlgVerify, RS256Private}; + use base64::prelude::BASE64_URL_SAFE_NO_PAD; + use base64::Engine; + use pkcs1::DecodeRsaPrivateKey; + pub use rsa::RsaPrivateKey; + + #[test] + fn rs256_algorithm_works_as_expected() { + let payload = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJoaiI6dHJ1ZX0"; + + let private_key = RsaPrivateKey::from_pkcs1_pem(include_str!("../../../../../test-files/rs256.key")).unwrap(); + let alg = RS256Private::from(private_key); + + let signature_bytes = alg.sign(payload); + let signature_string = BASE64_URL_SAFE_NO_PAD.encode(&signature_bytes); + + assert_eq!(signature_string, "ptH8Vc-nhm4gTl7HqaictKQyK3fxiJmSfyu-ouYlmIfyyRBIYw2tUdKxIsxgYMPXC7oV0-ShYtlUm73-q2buLoYGc52d-03RQghcVvZrag2nQCKsBBmTXFUADEaVopO65aND5h7Uif_1aQJXmX-40-V5te0fT3WSyU_1oKayxpi53_c7RXD7gDlWSXAZFDNhPopcRnq2_4FQylzFf4qbwtGWUNdJA4SGOikr1lsTrQRPGXLNXREG0PWv9GFoobQDTj9DWBG4B_cCAUVAjYUCx8BbgHSY9jeiYE_FbDykW0tRSA3XAYpf1QCPZmrCPButUixWY03FTTxsQxlJuY8r-w"); + + let verify = alg.verify(payload, &signature_bytes).unwrap(); + + assert!(verify); + } +} diff --git a/src/modules/algorithm/models/rs_algorithm/rs_public.rs b/src/modules/algorithm/models/rs_algorithm/rs_public.rs new file mode 100644 index 0000000..0dce4ec --- /dev/null +++ b/src/modules/algorithm/models/rs_algorithm/rs_public.rs @@ -0,0 +1,39 @@ +use p384::ecdsa::signature::digest::Digest; +use rsa::pkcs1v15::{Signature, VerifyingKey}; +use rsa::signature::Verifier; +use crate::algorithm::{JwAlg, JwAlgVerify}; + +pub struct RSPublic(VerifyingKey) +where D : Digest; + +#[cfg(feature = "rs256")] +impl JwAlg for RSPublic { + fn alg() -> impl AsRef { + "RS256" + } +} + +#[cfg(feature = "rs384")] +impl JwAlg for RSPublic { + fn alg() -> impl AsRef { + "RS384" + } +} + +#[cfg(feature = "rs512")] +impl JwAlg for RSPublic { + fn alg() -> impl AsRef { + "RS512" + } +} + +impl JwAlgVerify for RSPublic +where D : Digest +{ + type Error = rsa::signature::Error; + + fn verify(&self, payload: &str, signature: &[u8]) -> Result { + let signature = Signature::try_from(signature)?; + Ok(self.0.verify(payload.as_bytes(), &signature).is_ok()) + } +} diff --git a/src/modules/key/mod.rs b/src/modules/key/mod.rs index 855b176..6d1473b 100644 --- a/src/modules/key/mod.rs +++ b/src/modules/key/mod.rs @@ -1,20 +1,16 @@ mod models; mod traits; -pub use traits::jw_key_type::JwKeyType; -pub use models::rsa_private_jwk::RsaPublicJwk; - #[cfg(all(test, feature = "rs256"))] mod tests { use pkcs1::DecodeRsaPrivateKey; use rsa::RsaPrivateKey; use crate::algorithm::{RS256Private}; - use crate::modules::key::models::jwk::Jwk; #[test] fn jwk_is_created_correctly() { let private_key = RsaPrivateKey::from_pkcs1_pem(include_str!("../../../test-files/rs256.key")).unwrap(); - let alg = RS256Private::new(private_key); + let alg = RS256Private::from(private_key); // let jwk = Jwk::new(&alg); } } diff --git a/src/modules/key/models.rs b/src/modules/key/models.rs index 5a5f6b4..0877d37 100644 --- a/src/modules/key/models.rs +++ b/src/modules/key/models.rs @@ -1,3 +1,3 @@ -pub mod jwk; -pub mod rsa_private_jwk; -mod jwk_sign; +// pub mod jwk; +// pub mod rsa_private_jwk; +// mod jwk_sign; diff --git a/src/modules/key/models/jwk.rs b/src/modules/key/models/jwk.rs deleted file mode 100644 index 8992ca1..0000000 --- a/src/modules/key/models/jwk.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::modules::key::JwKeyType; - -pub struct Jwk<'a, T> -where T : JwKeyType<'a> -{ - inner: &'a T, -} - -impl<'a, T> Jwk<'a, T> -where T : JwKeyType<'a> -{ - pub fn new(inner: &'a T) -> Self { - Self { inner } - } -} diff --git a/src/modules/key/models/jwk_sign.rs b/src/modules/key/models/jwk_sign.rs deleted file mode 100644 index 4c53fed..0000000 --- a/src/modules/key/models/jwk_sign.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub struct JwkSign { - -} \ No newline at end of file diff --git a/src/modules/key/models/rsa_private_jwk.rs b/src/modules/key/models/rsa_private_jwk.rs deleted file mode 100644 index 1a200f3..0000000 --- a/src/modules/key/models/rsa_private_jwk.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize)] -pub struct RsaPublicJwk { - pub n: String, - pub e: String, -} From be97ebc775d8b3f427ad68bd11d47c184b00ec34 Mon Sep 17 00:00:00 2001 From: rster2002 Date: Tue, 17 Mar 2026 20:54:59 +0100 Subject: [PATCH 09/17] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Continue=20working?= =?UTF-8?q?=20on=20refactor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 2 + Cargo.toml | 6 +- src/modules/algorithm/mod.rs | 8 +-- .../models/es_algorithm/es_private.rs | 56 ++++++++++++++++++- .../models/es_algorithm/es_public.rs | 1 + .../algorithm/models/hs_algorithm/mod.rs | 31 ++++++++-- .../models/rs_algorithm/rs_private.rs | 41 ++++++++++++-- .../models/rs_algorithm/rs_public.rs | 1 + 8 files changed, 129 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9806b08..2419ffd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -306,6 +306,7 @@ dependencies = [ "p256", "p384", "pkcs1", + "rand", "rsa", "serde", "serde_json", @@ -495,6 +496,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ + "libc", "rand_chacha", "rand_core", ] diff --git a/Cargo.toml b/Cargo.toml index 3dd01f1..29e7c23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,14 +11,18 @@ thiserror = "2.0.11" hmac = { version = "0.12.1", optional = true } sha2 = { version = "0.10.8", features = ["oid"], optional = true } rsa = { version = "0.9.7", optional = true } -pkcs1 = "0.7.5" +pkcs1 = { version = "0.7.5", optional = true } p256 = { version = "0.13.2", features = ["pem", "arithmetic", "jwk"], optional = true } p384 = { version ="0.13.1", optional = true } chrono = "0.4.39" ecdsa = "0.16.9" +rand = { version = "0.8.0", optional = true } [features] default = ["hs256"] +pkcs1 = ["dep:pkcs1"] +rand = ["dep:rand"] + hmac = ["hs256", "hs384", "hs512"] hs256 = ["dep:hmac", "dep:sha2"] hs384 = ["dep:hmac", "dep:sha2"] diff --git a/src/modules/algorithm/mod.rs b/src/modules/algorithm/mod.rs index 7431df9..d284d62 100644 --- a/src/modules/algorithm/mod.rs +++ b/src/modules/algorithm/mod.rs @@ -28,16 +28,16 @@ pub type ES384Public = ESPublic; // HS #[cfg(any(feature = "hs256", feature = "hs384", feature = "hs512"))] -use crate::algorithm::models::hs_algorithm::HSAlg; +use crate::algorithm::models::hs_algorithm::HSPrivate; #[cfg(feature = "hs256")] -pub type HS256Private = HSAlg; +pub type HS256Private = HSPrivate; #[cfg(feature = "hs384")] -pub type HS384Private = HSAlg; +pub type HS384Private = HSPrivate; #[cfg(feature = "hs512")] -pub type HS512Private = HSAlg; +pub type HS512Private = HSPrivate; // RS #[cfg(any(feature = "rs256", feature = "rs384", feature = "rs512"))] diff --git a/src/modules/algorithm/models/es_algorithm/es_private.rs b/src/modules/algorithm/models/es_algorithm/es_private.rs index 01bcec8..36f97e2 100644 --- a/src/modules/algorithm/models/es_algorithm/es_private.rs +++ b/src/modules/algorithm/models/es_algorithm/es_private.rs @@ -8,7 +8,9 @@ use ecdsa::elliptic_curve::generic_array::ArrayLength; use ecdsa::signature::{Signer, Verifier}; use crate::algorithm::{JwAlgSign, JwAlgVerify}; use crate::algorithm::traits::jw_alg::JwAlg; +use ecdsa::elliptic_curve::FieldBytes; +#[derive(Clone)] pub struct ESPrivate(SigningKey) where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, @@ -29,6 +31,28 @@ impl JwAlg for ESPrivate { } } +impl ESPrivate +where C: PrimeCurve + CurveArithmetic + DigestPrimitive, + Scalar: Invert>> + SignPrimitive, + AffinePoint: VerifyPrimitive, + SignatureSize: ArrayLength, +{ + #[cfg(feature = "rand")] + pub fn rand() -> Self { + let mut rng = rand::thread_rng(); + ESPrivate::from(SigningKey::random(&mut rng)) + } + + pub fn to_bytes(&self) -> Vec { + self.0.to_bytes().to_vec() + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + let field_bytes = FieldBytes::::from_slice(bytes); + Ok(ESPrivate::from(SigningKey::from_bytes(field_bytes)?)) + } +} + impl JwAlgVerify for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, @@ -55,4 +79,34 @@ where C: PrimeCurve + CurveArithmetic + DigestPrimitive, let signature: Signature = Signer::sign(self, payload.as_bytes()); signature.to_vec() } -} \ No newline at end of file +} + +impl From> for ESPrivate +where C: PrimeCurve + CurveArithmetic + DigestPrimitive, + Scalar: Invert>> + SignPrimitive, + AffinePoint: VerifyPrimitive, + SignatureSize: ArrayLength +{ + fn from(key: SigningKey) -> Self { + ESPrivate(key) + } +} + +#[cfg(test)] +mod tests { + use crate::algorithm::ES256Private; + + #[test] + fn es256_can_be_generated_randomly() { + ES256Private::rand(); + } + + #[test] + fn es256_to_and_from_bytes() { + let alg = ES256Private::rand(); + let bytes = alg.to_bytes(); + + let alg2 = ES256Private::from_bytes(&bytes).unwrap(); + assert_eq!(alg.0, alg2.0); + } +} diff --git a/src/modules/algorithm/models/es_algorithm/es_public.rs b/src/modules/algorithm/models/es_algorithm/es_public.rs index 0cac619..808a494 100644 --- a/src/modules/algorithm/models/es_algorithm/es_public.rs +++ b/src/modules/algorithm/models/es_algorithm/es_public.rs @@ -5,6 +5,7 @@ use ecdsa::elliptic_curve::generic_array::ArrayLength; use ecdsa::hazmat::{DigestPrimitive, VerifyPrimitive}; use ecdsa::signature::Verifier; +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ESPublic(VerifyingKey) where C: PrimeCurve + CurveArithmetic + DigestPrimitive, diff --git a/src/modules/algorithm/models/hs_algorithm/mod.rs b/src/modules/algorithm/models/hs_algorithm/mod.rs index f44f617..6e59be8 100644 --- a/src/modules/algorithm/models/hs_algorithm/mod.rs +++ b/src/modules/algorithm/models/hs_algorithm/mod.rs @@ -10,7 +10,11 @@ use sha2::Sha256; use crate::algorithm::{JwAlgVerify, JwAlgSign, JwAlg}; use hmac::digest::InvalidLength; -pub struct HSAlg(Hmac) +#[cfg(feature = "rand")] +use rand::RngCore; + +#[derive(Clone)] +pub struct HSPrivate(Hmac) where D: CoreProxy, D::Core: HashMarker + UpdateCore @@ -21,7 +25,7 @@ where D: CoreProxy, ::BlockSize: IsLess, Le<::BlockSize, U256>: NonZero; -impl HSAlg +impl HSPrivate where D: CoreProxy, D::Core: HashMarker + UpdateCore @@ -33,18 +37,27 @@ where D: CoreProxy, Le<::BlockSize, U256>: NonZero, { pub fn new(key: &[u8]) -> Result { - Ok(HSAlg(Hmac::::new_from_slice(key)?)) + Ok(HSPrivate(Hmac::::new_from_slice(key)?)) + } + + #[cfg(feature = "rand")] + pub fn rand() -> Result { + let mut rng = rand::thread_rng(); + let mut slice = [0u8; 32]; + rng.fill_bytes(&mut slice); + + HSPrivate::new(&slice) } } #[cfg(feature = "hs256")] -impl JwAlg for HSAlg { +impl JwAlg for HSPrivate { fn alg() -> impl AsRef { "HS256" } } -impl JwAlgVerify for HSAlg +impl JwAlgVerify for HSPrivate where D: CoreProxy, D::Core: HashMarker + UpdateCore @@ -69,7 +82,7 @@ where D: CoreProxy, } } -impl JwAlgSign for HSAlg +impl JwAlgSign for HSPrivate where D: CoreProxy, D::Core: HashMarker + UpdateCore @@ -93,6 +106,7 @@ mod tests { use base64::Engine; use base64::prelude::BASE64_URL_SAFE_NO_PAD; use crate::algorithm::JwAlgSign; + use crate::algorithm::models::hs_algorithm::HSPrivate; use crate::modules::algorithm::{HS256Private, JwAlgVerify}; #[test] @@ -109,4 +123,9 @@ mod tests { assert!(verify); } + + #[test] + fn hs256_can_be_generated_randomly() { + HS256Private::rand().unwrap(); + } } diff --git a/src/modules/algorithm/models/rs_algorithm/rs_private.rs b/src/modules/algorithm/models/rs_algorithm/rs_private.rs index f3abd2b..5f90ed6 100644 --- a/src/modules/algorithm/models/rs_algorithm/rs_private.rs +++ b/src/modules/algorithm/models/rs_algorithm/rs_private.rs @@ -6,8 +6,39 @@ use rsa::RsaPrivateKey; use serde::{Deserialize, Serialize}; use sha2::Digest; +#[cfg(feature = "pkcs1")] +use pkcs1::{DecodeRsaPrivateKey, EncodeRsaPrivateKey}; + +#[derive(Clone)] pub struct RSPrivate(SigningKey) -where D: Digest; +where D: Digest + AssociatedOid; + +impl RSPrivate +where D: Digest + AssociatedOid, +{ + #[cfg(feature = "rand")] + pub fn rand() -> Result { + Self::rand_size(4096) + } + + #[cfg(feature = "rand")] + pub fn rand_size(size: usize) -> Result { + let mut rng = rand::thread_rng(); + Ok(RSPrivate::from(SigningKey::random(&mut rng, size)?)) + } + + #[cfg(feature = "pkcs1")] + pub fn to_pkcs1_der_bytes(&self) -> Result, rsa::Error> { + Ok(self.0.to_pkcs1_der()? + .to_bytes() + .to_vec()) + } + + #[cfg(feature = "pkcs1")] + pub fn from_pkcs1_der_bytes(bytes: &[u8]) -> Result { + Ok(RSPrivate::from(SigningKey::from_pkcs1_der(bytes)?)) + } +} #[cfg(feature = "rs256")] impl JwAlg for RSPrivate { @@ -31,7 +62,7 @@ impl JwAlg for RSPrivate { } impl JwAlgVerify for RSPrivate -where D: Digest, +where D: Digest + AssociatedOid, { type Error = rsa::signature::Error; @@ -43,7 +74,7 @@ where D: Digest, } impl JwAlgSign for RSPrivate -where D: Digest, +where D: Digest + AssociatedOid, { fn sign(&self, payload: &str) -> Vec { self.0.sign(payload.as_bytes()).to_vec() @@ -51,7 +82,7 @@ where D: Digest, } impl From> for RSPrivate -where D: Digest, +where D: Digest + AssociatedOid, { fn from(key: SigningKey) -> Self { Self(key) @@ -66,7 +97,7 @@ where D: Digest + AssociatedOid, } } -#[cfg(test)] +#[cfg(all(test, feature = "pkcs1"))] mod tests { use crate::algorithm::{JwAlgSign, JwAlgVerify, RS256Private}; use base64::prelude::BASE64_URL_SAFE_NO_PAD; diff --git a/src/modules/algorithm/models/rs_algorithm/rs_public.rs b/src/modules/algorithm/models/rs_algorithm/rs_public.rs index 0dce4ec..676b792 100644 --- a/src/modules/algorithm/models/rs_algorithm/rs_public.rs +++ b/src/modules/algorithm/models/rs_algorithm/rs_public.rs @@ -3,6 +3,7 @@ use rsa::pkcs1v15::{Signature, VerifyingKey}; use rsa::signature::Verifier; use crate::algorithm::{JwAlg, JwAlgVerify}; +#[derive(Clone)] pub struct RSPublic(VerifyingKey) where D : Digest; From e6e457b14cbf003dc8b7130478931be6b8beb449 Mon Sep 17 00:00:00 2001 From: rster2002 Date: Wed, 18 Mar 2026 20:46:53 +0100 Subject: [PATCH 10/17] =?UTF-8?q?=F0=9F=9A=A7=20Some=20very=20minor=20work?= =?UTF-8?q?=20on=20JWKs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/algorithm/mod.rs | 1 + .../models/es_algorithm/es_private.rs | 15 +++++++++ .../models/es_algorithm/es_public.rs | 15 +++++++++ .../algorithm/models/hs_algorithm/mod.rs | 8 +++++ .../models/rs_algorithm/rs_private.rs | 22 +++++++++++++ .../models/rs_algorithm/rs_public.rs | 21 +++++++++++++ src/modules/algorithm/traits.rs | 1 + src/modules/algorithm/traits/jw_alg.rs | 4 ++- .../algorithm/traits/partial_jw_alg.rs | 3 ++ src/modules/key/error.rs | 4 +++ src/modules/key/mod.rs | 12 +++++-- src/modules/key/models.rs | 5 +++ src/modules/key/models/jwk.rs | 30 ++++++++++++++++++ src/modules/key/models/jwt_key_op.rs | 31 +++++++++++++++++++ src/modules/key/models/jwt_set.rs | 23 ++++++++++++++ src/modules/key/models/jwt_use.rs | 13 ++++++++ src/modules/token/models/jwt.rs | 4 ++- 17 files changed, 207 insertions(+), 5 deletions(-) create mode 100644 src/modules/algorithm/traits/partial_jw_alg.rs create mode 100644 src/modules/key/error.rs create mode 100644 src/modules/key/models/jwk.rs create mode 100644 src/modules/key/models/jwt_key_op.rs create mode 100644 src/modules/key/models/jwt_set.rs create mode 100644 src/modules/key/models/jwt_use.rs diff --git a/src/modules/algorithm/mod.rs b/src/modules/algorithm/mod.rs index d284d62..165d664 100644 --- a/src/modules/algorithm/mod.rs +++ b/src/modules/algorithm/mod.rs @@ -4,6 +4,7 @@ mod traits; pub use traits::jw_alg_verify::JwAlgVerify; pub use traits::jw_alg_sign::JwAlgSign; pub use traits::jw_alg::JwAlg; +pub use traits::partial_jw_alg::PartialJwAlg; pub use models::none_algorithm::NoneAlgorithm; diff --git a/src/modules/algorithm/models/es_algorithm/es_private.rs b/src/modules/algorithm/models/es_algorithm/es_private.rs index 36f97e2..4a1fbf4 100644 --- a/src/modules/algorithm/models/es_algorithm/es_private.rs +++ b/src/modules/algorithm/models/es_algorithm/es_private.rs @@ -9,6 +9,7 @@ use ecdsa::signature::{Signer, Verifier}; use crate::algorithm::{JwAlgSign, JwAlgVerify}; use crate::algorithm::traits::jw_alg::JwAlg; use ecdsa::elliptic_curve::FieldBytes; +use crate::algorithm::traits::partial_jw_alg::PartialJwAlg; #[derive(Clone)] pub struct ESPrivate(SigningKey) @@ -24,6 +25,13 @@ impl JwAlg for ESPrivate { } } +#[cfg(feature = "es256")] +impl PartialJwAlg for ESPrivate { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + #[cfg(feature = "es384")] impl JwAlg for ESPrivate { fn alg() -> impl AsRef { @@ -31,6 +39,13 @@ impl JwAlg for ESPrivate { } } +#[cfg(feature = "es384")] +impl PartialJwAlg for ESPrivate { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + impl ESPrivate where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, diff --git a/src/modules/algorithm/models/es_algorithm/es_public.rs b/src/modules/algorithm/models/es_algorithm/es_public.rs index 808a494..3e5cf27 100644 --- a/src/modules/algorithm/models/es_algorithm/es_public.rs +++ b/src/modules/algorithm/models/es_algorithm/es_public.rs @@ -4,6 +4,7 @@ use crate::algorithm::{JwAlg, JwAlgVerify}; use ecdsa::elliptic_curve::generic_array::ArrayLength; use ecdsa::hazmat::{DigestPrimitive, VerifyPrimitive}; use ecdsa::signature::Verifier; +use crate::algorithm::traits::partial_jw_alg::PartialJwAlg; #[derive(Debug, Clone, PartialEq, Eq)] pub struct ESPublic(VerifyingKey) @@ -19,6 +20,13 @@ impl JwAlg for ESPublic { } } +#[cfg(feature = "es256")] +impl PartialJwAlg for ESPublic { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + #[cfg(feature = "es384")] impl JwAlg for ESPublic { fn alg() -> impl AsRef { @@ -26,6 +34,13 @@ impl JwAlg for ESPublic { } } +#[cfg(feature = "es384")] +impl PartialJwAlg for ESPublic { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + impl JwAlgVerify for ESPublic where C: PrimeCurve + CurveArithmetic + DigestPrimitive, diff --git a/src/modules/algorithm/models/hs_algorithm/mod.rs b/src/modules/algorithm/models/hs_algorithm/mod.rs index 6e59be8..960344a 100644 --- a/src/modules/algorithm/models/hs_algorithm/mod.rs +++ b/src/modules/algorithm/models/hs_algorithm/mod.rs @@ -12,6 +12,7 @@ use hmac::digest::InvalidLength; #[cfg(feature = "rand")] use rand::RngCore; +use crate::algorithm::traits::partial_jw_alg::PartialJwAlg; #[derive(Clone)] pub struct HSPrivate(Hmac) @@ -57,6 +58,13 @@ impl JwAlg for HSPrivate { } } +#[cfg(feature = "hs256")] +impl PartialJwAlg for HSPrivate { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + impl JwAlgVerify for HSPrivate where D: CoreProxy, D::Core: HashMarker diff --git a/src/modules/algorithm/models/rs_algorithm/rs_private.rs b/src/modules/algorithm/models/rs_algorithm/rs_private.rs index 5f90ed6..fd405b2 100644 --- a/src/modules/algorithm/models/rs_algorithm/rs_private.rs +++ b/src/modules/algorithm/models/rs_algorithm/rs_private.rs @@ -8,6 +8,7 @@ use sha2::Digest; #[cfg(feature = "pkcs1")] use pkcs1::{DecodeRsaPrivateKey, EncodeRsaPrivateKey}; +use crate::algorithm::traits::partial_jw_alg::PartialJwAlg; #[derive(Clone)] pub struct RSPrivate(SigningKey) @@ -47,6 +48,13 @@ impl JwAlg for RSPrivate { } } +#[cfg(feature = "rs256")] +impl PartialJwAlg for RSPrivate { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + #[cfg(feature = "rs384")] impl JwAlg for RSPrivate { fn alg() -> impl AsRef { @@ -54,6 +62,13 @@ impl JwAlg for RSPrivate { } } +#[cfg(feature = "rs384")] +impl PartialJwAlg for RSPrivate { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + #[cfg(feature = "rs512")] impl JwAlg for RSPrivate { fn alg() -> impl AsRef { @@ -61,6 +76,13 @@ impl JwAlg for RSPrivate { } } +#[cfg(feature = "rs512")] +impl PartialJwAlg for RSPrivate { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + impl JwAlgVerify for RSPrivate where D: Digest + AssociatedOid, { diff --git a/src/modules/algorithm/models/rs_algorithm/rs_public.rs b/src/modules/algorithm/models/rs_algorithm/rs_public.rs index 676b792..94d518d 100644 --- a/src/modules/algorithm/models/rs_algorithm/rs_public.rs +++ b/src/modules/algorithm/models/rs_algorithm/rs_public.rs @@ -2,6 +2,7 @@ use p384::ecdsa::signature::digest::Digest; use rsa::pkcs1v15::{Signature, VerifyingKey}; use rsa::signature::Verifier; use crate::algorithm::{JwAlg, JwAlgVerify}; +use crate::algorithm::traits::partial_jw_alg::PartialJwAlg; #[derive(Clone)] pub struct RSPublic(VerifyingKey) @@ -14,6 +15,12 @@ impl JwAlg for RSPublic { } } +impl PartialJwAlg for RSPublic { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + #[cfg(feature = "rs384")] impl JwAlg for RSPublic { fn alg() -> impl AsRef { @@ -21,6 +28,13 @@ impl JwAlg for RSPublic { } } +#[cfg(feature = "rs384")] +impl PartialJwAlg for RSPublic { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + #[cfg(feature = "rs512")] impl JwAlg for RSPublic { fn alg() -> impl AsRef { @@ -28,6 +42,13 @@ impl JwAlg for RSPublic { } } +#[cfg(feature = "rs512")] +impl PartialJwAlg for RSPublic { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + impl JwAlgVerify for RSPublic where D : Digest { diff --git a/src/modules/algorithm/traits.rs b/src/modules/algorithm/traits.rs index 35e9888..37c62ae 100644 --- a/src/modules/algorithm/traits.rs +++ b/src/modules/algorithm/traits.rs @@ -1,3 +1,4 @@ pub mod jw_alg_verify; pub mod jw_alg_sign; pub mod jw_alg; +pub mod partial_jw_alg; diff --git a/src/modules/algorithm/traits/jw_alg.rs b/src/modules/algorithm/traits/jw_alg.rs index 0399ed6..038ea9a 100644 --- a/src/modules/algorithm/traits/jw_alg.rs +++ b/src/modules/algorithm/traits/jw_alg.rs @@ -1,3 +1,5 @@ -pub trait JwAlg { +use crate::algorithm::traits::partial_jw_alg::PartialJwAlg; + +pub trait JwAlg: PartialJwAlg { fn alg() -> impl AsRef; } \ No newline at end of file diff --git a/src/modules/algorithm/traits/partial_jw_alg.rs b/src/modules/algorithm/traits/partial_jw_alg.rs new file mode 100644 index 0000000..36b1a05 --- /dev/null +++ b/src/modules/algorithm/traits/partial_jw_alg.rs @@ -0,0 +1,3 @@ +pub trait PartialJwAlg { + fn partial_alg() -> Option>; +} \ No newline at end of file diff --git a/src/modules/key/error.rs b/src/modules/key/error.rs new file mode 100644 index 0000000..b253c96 --- /dev/null +++ b/src/modules/key/error.rs @@ -0,0 +1,4 @@ +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum JwkError {} \ No newline at end of file diff --git a/src/modules/key/mod.rs b/src/modules/key/mod.rs index 6d1473b..8ef6db5 100644 --- a/src/modules/key/mod.rs +++ b/src/modules/key/mod.rs @@ -1,16 +1,22 @@ mod models; mod traits; +pub mod error; #[cfg(all(test, feature = "rs256"))] mod tests { use pkcs1::DecodeRsaPrivateKey; use rsa::RsaPrivateKey; + use serde_json::json; use crate::algorithm::{RS256Private}; + use crate::modules::key::models::jwk::Jwk; #[test] fn jwk_is_created_correctly() { - let private_key = RsaPrivateKey::from_pkcs1_pem(include_str!("../../../test-files/rs256.key")).unwrap(); - let alg = RS256Private::from(private_key); - // let jwk = Jwk::new(&alg); + let ec_key = Jwk::try_from(json!({ + "kty": "EC", + "crv": "P-256", + "x": "", + "y": "", + })); } } diff --git a/src/modules/key/models.rs b/src/modules/key/models.rs index 0877d37..ddda118 100644 --- a/src/modules/key/models.rs +++ b/src/modules/key/models.rs @@ -1,3 +1,8 @@ // pub mod jwk; // pub mod rsa_private_jwk; // mod jwk_sign; + +pub mod jwk; +pub mod jwt_use; +pub mod jwt_key_op; +pub mod jwt_set; \ No newline at end of file diff --git a/src/modules/key/models/jwk.rs b/src/modules/key/models/jwk.rs new file mode 100644 index 0000000..26720b0 --- /dev/null +++ b/src/modules/key/models/jwk.rs @@ -0,0 +1,30 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use crate::modules::key::models::jwt_key_op::JwtKeyOp; +use crate::modules::key::models::jwt_use::JwtUse; +use crate::token::JwtError; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Jwk { + pub kty: String, + + #[serde(rename = "use")] + pub usage: Option, + pub key_ops: Option>, + pub alg: Option, + pub kid: Option, + pub x5u: Option, + pub x5c: Option>, + pub x5t: Option, + + #[serde(rename = "x5t#S256")] + pub x5t_s256: Option, +} + +impl TryFrom for Jwk { + type Error = JwtError; + + fn try_from(value: Value) -> Result { + todo!() + } +} \ No newline at end of file diff --git a/src/modules/key/models/jwt_key_op.rs b/src/modules/key/models/jwt_key_op.rs new file mode 100644 index 0000000..6b29595 --- /dev/null +++ b/src/modules/key/models/jwt_key_op.rs @@ -0,0 +1,31 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub enum JwtKeyOp { + #[serde(rename = "sign")] + Sign, + + #[serde(rename = "verify")] + Verify, + + #[serde(rename = "encrypt")] + Encrypt, + + #[serde(rename = "decrypt")] + Decrypt, + + #[serde(rename = "wrapKey")] + WrapKey, + + #[serde(rename = "unwrapKey")] + UnwrapKey, + + #[serde(rename = "deriveKey")] + DeriveKey, + + #[serde(rename = "deriveBits")] + DeriveBits, + + #[serde(untagged)] + Other(String), +} \ No newline at end of file diff --git a/src/modules/key/models/jwt_set.rs b/src/modules/key/models/jwt_set.rs new file mode 100644 index 0000000..da51869 --- /dev/null +++ b/src/modules/key/models/jwt_set.rs @@ -0,0 +1,23 @@ +use serde::{Deserialize, Serialize}; +use crate::algorithm::{JwAlgVerify, PartialJwAlg}; +use crate::modules::key::error::JwkError; +use crate::modules::key::models::jwk::Jwk; + +#[derive(Debug, Serialize, Deserialize)] +pub struct JwtSet { + pub keys: Vec, +} + +impl PartialJwAlg for JwtSet { + fn partial_alg() -> Option> { + None::<&'static str> + } +} + +impl JwAlgVerify for JwtSet { + type Error = JwkError; + + fn verify(&self, payload: &str, signature: &[u8]) -> Result { + todo!() + } +} \ No newline at end of file diff --git a/src/modules/key/models/jwt_use.rs b/src/modules/key/models/jwt_use.rs new file mode 100644 index 0000000..cae4ac0 --- /dev/null +++ b/src/modules/key/models/jwt_use.rs @@ -0,0 +1,13 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub enum JwtUse { + #[serde(rename = "sig")] + Signature, + + #[serde(rename = "enc")] + Encryption, + + #[serde(untagged)] + Unknown(String), +} \ No newline at end of file diff --git a/src/modules/token/models/jwt.rs b/src/modules/token/models/jwt.rs index fa2d82c..83dbdee 100644 --- a/src/modules/token/models/jwt.rs +++ b/src/modules/token/models/jwt.rs @@ -72,7 +72,9 @@ where T : Serialize + for<'a> Deserialize<'a>, let header_bytes = BASE64_URL_SAFE_NO_PAD.decode(header_string.as_bytes())?; let header: JwtHeader = serde_json::from_slice(&header_bytes)?; - if header.alg != Cow::Borrowed(A::alg().as_ref()) { + if let Some(alg_ref) = A::partial_alg() + && header.alg != Cow::Borrowed(alg_ref.as_ref()) + { return Err(JwtError::AlgMismatch); } From 397a737c470a5f79c55914d66191af94d5d61956 Mon Sep 17 00:00:00 2001 From: rster2002 Date: Thu, 19 Mar 2026 19:03:12 +0100 Subject: [PATCH 11/17] =?UTF-8?q?=F0=9F=9A=A7=20Continue=20working=20on=20?= =?UTF-8?q?JWKs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/algorithm/algorithms.rs | 17 +++++++ src/modules/algorithm/algorithms/es256.rs | 46 +++++++++++++++++++ src/modules/algorithm/algorithms/es384.rs | 30 ++++++++++++ src/modules/algorithm/algorithms/hs256.rs | 15 ++++++ src/modules/algorithm/algorithms/rs256.rs | 29 ++++++++++++ src/modules/algorithm/algorithms/rs384.rs | 29 ++++++++++++ src/modules/algorithm/algorithms/rs512.rs | 29 ++++++++++++ src/modules/algorithm/mod.rs | 1 + .../algorithm/models/es_algorithm/es_curve.rs | 13 ++++++ .../models/es_algorithm/es_private.rs | 42 ++++++----------- .../models/es_algorithm/es_private_params.rs | 10 ++++ .../models/es_algorithm/es_public.rs | 28 ----------- .../models/es_algorithm/es_public_params.rs | 9 ++++ .../algorithm/models/es_algorithm/mod.rs | 5 +- .../algorithm/models/hs_algorithm/mod.rs | 18 +------- .../models/rs_algorithm/rs_private.rs | 42 ----------------- .../models/rs_algorithm/rs_public.rs | 44 +----------------- src/modules/key/mod.rs | 8 +++- src/modules/key/traits.rs | 4 +- src/modules/key/traits/jw_key_type.rs | 8 ---- src/modules/key/traits/jwk_private_params.rs | 7 +++ src/modules/key/traits/jwk_public_params.rs | 7 +++ src/modules/key/traits/jwk_type.rs | 3 ++ 23 files changed, 274 insertions(+), 170 deletions(-) create mode 100644 src/modules/algorithm/algorithms.rs create mode 100644 src/modules/algorithm/algorithms/es256.rs create mode 100644 src/modules/algorithm/algorithms/es384.rs create mode 100644 src/modules/algorithm/algorithms/hs256.rs create mode 100644 src/modules/algorithm/algorithms/rs256.rs create mode 100644 src/modules/algorithm/algorithms/rs384.rs create mode 100644 src/modules/algorithm/algorithms/rs512.rs create mode 100644 src/modules/algorithm/models/es_algorithm/es_curve.rs create mode 100644 src/modules/algorithm/models/es_algorithm/es_private_params.rs create mode 100644 src/modules/algorithm/models/es_algorithm/es_public_params.rs delete mode 100644 src/modules/key/traits/jw_key_type.rs create mode 100644 src/modules/key/traits/jwk_private_params.rs create mode 100644 src/modules/key/traits/jwk_public_params.rs create mode 100644 src/modules/key/traits/jwk_type.rs diff --git a/src/modules/algorithm/algorithms.rs b/src/modules/algorithm/algorithms.rs new file mode 100644 index 0000000..b1da59e --- /dev/null +++ b/src/modules/algorithm/algorithms.rs @@ -0,0 +1,17 @@ +#[cfg(feature = "es256")] +pub mod es256; + +#[cfg(feature = "es384")] +pub mod es384; + +#[cfg(feature = "hs256")] +pub mod hs256; + +#[cfg(feature = "rs256")] +pub mod rs256; + +#[cfg(feature = "rs384")] +pub mod rs384; + +#[cfg(feature = "rs512")] +pub mod rs512; \ No newline at end of file diff --git a/src/modules/algorithm/algorithms/es256.rs b/src/modules/algorithm/algorithms/es256.rs new file mode 100644 index 0000000..cfed7ff --- /dev/null +++ b/src/modules/algorithm/algorithms/es256.rs @@ -0,0 +1,46 @@ +use crate::algorithm::{JwAlg, PartialJwAlg}; +use crate::algorithm::models::es_algorithm::es_curve::EsCurve; +use crate::algorithm::models::es_algorithm::es_private::ESPrivate; +use crate::algorithm::models::es_algorithm::es_private_params::EsPrivateParams; +use crate::algorithm::models::es_algorithm::es_public::ESPublic; +use crate::modules::key::JwkPrivateParams; + +// Private +impl JwAlg for ESPrivate { + fn alg() -> impl AsRef { + "ES256" + } +} + +impl PartialJwAlg for ESPrivate { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + +impl JwkPrivateParams<'_> for ESPrivate { + type PrivateParams = EsPrivateParams; + + fn get_private_params(&self) -> Self::PrivateParams { + + EsPrivateParams { + crv: EsCurve::P256, + x: "".to_string(), + y: "".to_string(), + d: "".to_string(), + } + } +} + +// Public +impl JwAlg for ESPublic { + fn alg() -> impl AsRef { + "ES256" + } +} + +impl PartialJwAlg for ESPublic { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} \ No newline at end of file diff --git a/src/modules/algorithm/algorithms/es384.rs b/src/modules/algorithm/algorithms/es384.rs new file mode 100644 index 0000000..11ecffc --- /dev/null +++ b/src/modules/algorithm/algorithms/es384.rs @@ -0,0 +1,30 @@ +use crate::algorithm::{JwAlg, PartialJwAlg}; +use crate::algorithm::models::es_algorithm::es_private::ESPrivate; +use crate::algorithm::models::es_algorithm::es_public::ESPublic; + +// Private +impl JwAlg for ESPrivate { + fn alg() -> impl AsRef { + "ES384" + } +} + +impl PartialJwAlg for ESPrivate { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + +// Public + +impl JwAlg for ESPublic { + fn alg() -> impl AsRef { + "ES384" + } +} + +impl PartialJwAlg for ESPublic { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} \ No newline at end of file diff --git a/src/modules/algorithm/algorithms/hs256.rs b/src/modules/algorithm/algorithms/hs256.rs new file mode 100644 index 0000000..938c491 --- /dev/null +++ b/src/modules/algorithm/algorithms/hs256.rs @@ -0,0 +1,15 @@ +use sha2::Sha256; +use crate::algorithm::{JwAlg, PartialJwAlg}; +use crate::algorithm::models::hs_algorithm::HSPrivate; + +impl JwAlg for HSPrivate { + fn alg() -> impl AsRef { + "HS256" + } +} + +impl PartialJwAlg for HSPrivate { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} \ No newline at end of file diff --git a/src/modules/algorithm/algorithms/rs256.rs b/src/modules/algorithm/algorithms/rs256.rs new file mode 100644 index 0000000..cddd270 --- /dev/null +++ b/src/modules/algorithm/algorithms/rs256.rs @@ -0,0 +1,29 @@ +use crate::algorithm::{JwAlg, PartialJwAlg}; +use crate::algorithm::models::rs_algorithm::rs_private::RSPrivate; +use crate::algorithm::models::rs_algorithm::rs_public::RSPublic; + +// Private +impl JwAlg for RSPrivate { + fn alg() -> impl AsRef { + "RS256" + } +} + +impl PartialJwAlg for RSPrivate { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + +// Public +impl JwAlg for RSPublic { + fn alg() -> impl AsRef { + "RS256" + } +} + +impl PartialJwAlg for RSPublic { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} \ No newline at end of file diff --git a/src/modules/algorithm/algorithms/rs384.rs b/src/modules/algorithm/algorithms/rs384.rs new file mode 100644 index 0000000..0bfa07d --- /dev/null +++ b/src/modules/algorithm/algorithms/rs384.rs @@ -0,0 +1,29 @@ +use crate::algorithm::{JwAlg, PartialJwAlg}; +use crate::algorithm::models::rs_algorithm::rs_private::RSPrivate; +use crate::algorithm::models::rs_algorithm::rs_public::RSPublic; + +// Private +impl JwAlg for RSPrivate { + fn alg() -> impl AsRef { + "RS384" + } +} + +impl PartialJwAlg for RSPrivate { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + +// Public +impl JwAlg for RSPublic { + fn alg() -> impl AsRef { + "RS384" + } +} + +impl PartialJwAlg for RSPublic { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} \ No newline at end of file diff --git a/src/modules/algorithm/algorithms/rs512.rs b/src/modules/algorithm/algorithms/rs512.rs new file mode 100644 index 0000000..b2e71ee --- /dev/null +++ b/src/modules/algorithm/algorithms/rs512.rs @@ -0,0 +1,29 @@ +use crate::algorithm::{JwAlg, PartialJwAlg}; +use crate::algorithm::models::rs_algorithm::rs_private::RSPrivate; +use crate::algorithm::models::rs_algorithm::rs_public::RSPublic; + +// Private +impl JwAlg for RSPrivate { + fn alg() -> impl AsRef { + "RS512" + } +} + +impl PartialJwAlg for RSPrivate { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} + +// Public +impl JwAlg for RSPublic { + fn alg() -> impl AsRef { + "RS512" + } +} + +impl PartialJwAlg for RSPublic { + fn partial_alg() -> Option> { + Some(Self::alg()) + } +} \ No newline at end of file diff --git a/src/modules/algorithm/mod.rs b/src/modules/algorithm/mod.rs index 165d664..38a53d8 100644 --- a/src/modules/algorithm/mod.rs +++ b/src/modules/algorithm/mod.rs @@ -1,5 +1,6 @@ mod models; mod traits; +mod algorithms; pub use traits::jw_alg_verify::JwAlgVerify; pub use traits::jw_alg_sign::JwAlgSign; diff --git a/src/modules/algorithm/models/es_algorithm/es_curve.rs b/src/modules/algorithm/models/es_algorithm/es_curve.rs new file mode 100644 index 0000000..4b3e397 --- /dev/null +++ b/src/modules/algorithm/models/es_algorithm/es_curve.rs @@ -0,0 +1,13 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub enum EsCurve { + #[serde(rename = "P-256")] + P256, + + #[serde(rename = "P-384")] + P384, + + #[serde(rename = "P-521")] + P521, +} \ No newline at end of file diff --git a/src/modules/algorithm/models/es_algorithm/es_private.rs b/src/modules/algorithm/models/es_algorithm/es_private.rs index 4a1fbf4..54a5d94 100644 --- a/src/modules/algorithm/models/es_algorithm/es_private.rs +++ b/src/modules/algorithm/models/es_algorithm/es_private.rs @@ -10,42 +10,15 @@ use crate::algorithm::{JwAlgSign, JwAlgVerify}; use crate::algorithm::traits::jw_alg::JwAlg; use ecdsa::elliptic_curve::FieldBytes; use crate::algorithm::traits::partial_jw_alg::PartialJwAlg; +use crate::modules::key::{JwkPrivateParams, JwkType}; #[derive(Clone)] -pub struct ESPrivate(SigningKey) +pub struct ESPrivate(pub SigningKey) where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength; -#[cfg(feature = "es256")] -impl JwAlg for ESPrivate { - fn alg() -> impl AsRef { - "ES256" - } -} - -#[cfg(feature = "es256")] -impl PartialJwAlg for ESPrivate { - fn partial_alg() -> Option> { - Some(Self::alg()) - } -} - -#[cfg(feature = "es384")] -impl JwAlg for ESPrivate { - fn alg() -> impl AsRef { - "ES384" - } -} - -#[cfg(feature = "es384")] -impl PartialJwAlg for ESPrivate { - fn partial_alg() -> Option> { - Some(Self::alg()) - } -} - impl ESPrivate where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, @@ -96,6 +69,17 @@ where C: PrimeCurve + CurveArithmetic + DigestPrimitive, } } +impl JwkType for ESPrivate +where C: PrimeCurve + CurveArithmetic + DigestPrimitive, + Scalar: Invert>> + SignPrimitive, + AffinePoint: VerifyPrimitive, + SignatureSize: ArrayLength, +{ + fn kty() -> impl AsRef { + "EC" + } +} + impl From> for ESPrivate where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, diff --git a/src/modules/algorithm/models/es_algorithm/es_private_params.rs b/src/modules/algorithm/models/es_algorithm/es_private_params.rs new file mode 100644 index 0000000..4aed235 --- /dev/null +++ b/src/modules/algorithm/models/es_algorithm/es_private_params.rs @@ -0,0 +1,10 @@ +use serde::{Deserialize, Serialize}; +use crate::algorithm::models::es_algorithm::es_curve::EsCurve; + +#[derive(Debug, Serialize, Deserialize)] +pub struct EsPrivateParams { + pub crv: EsCurve, + pub x: String, + pub y: String, + pub d: String, +} \ No newline at end of file diff --git a/src/modules/algorithm/models/es_algorithm/es_public.rs b/src/modules/algorithm/models/es_algorithm/es_public.rs index 3e5cf27..cff2154 100644 --- a/src/modules/algorithm/models/es_algorithm/es_public.rs +++ b/src/modules/algorithm/models/es_algorithm/es_public.rs @@ -13,34 +13,6 @@ where AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength; -#[cfg(feature = "es256")] -impl JwAlg for ESPublic { - fn alg() -> impl AsRef { - "ES256" - } -} - -#[cfg(feature = "es256")] -impl PartialJwAlg for ESPublic { - fn partial_alg() -> Option> { - Some(Self::alg()) - } -} - -#[cfg(feature = "es384")] -impl JwAlg for ESPublic { - fn alg() -> impl AsRef { - "ES384" - } -} - -#[cfg(feature = "es384")] -impl PartialJwAlg for ESPublic { - fn partial_alg() -> Option> { - Some(Self::alg()) - } -} - impl JwAlgVerify for ESPublic where C: PrimeCurve + CurveArithmetic + DigestPrimitive, diff --git a/src/modules/algorithm/models/es_algorithm/es_public_params.rs b/src/modules/algorithm/models/es_algorithm/es_public_params.rs new file mode 100644 index 0000000..1a443d3 --- /dev/null +++ b/src/modules/algorithm/models/es_algorithm/es_public_params.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; +use crate::algorithm::models::es_algorithm::es_curve::EsCurve; + +#[derive(Debug, Serialize, Deserialize)] +pub struct EsPublicParams { + pub crv: EsCurve, + pub x: String, + pub y: String, +} \ No newline at end of file diff --git a/src/modules/algorithm/models/es_algorithm/mod.rs b/src/modules/algorithm/models/es_algorithm/mod.rs index b1a6fec..ddeeeb8 100644 --- a/src/modules/algorithm/models/es_algorithm/mod.rs +++ b/src/modules/algorithm/models/es_algorithm/mod.rs @@ -1,2 +1,5 @@ pub mod es_private; -pub mod es_public; \ No newline at end of file +pub mod es_public; +pub mod es_private_params; +pub mod es_curve; +pub mod es_public_params; \ No newline at end of file diff --git a/src/modules/algorithm/models/hs_algorithm/mod.rs b/src/modules/algorithm/models/hs_algorithm/mod.rs index 960344a..f7dfd9b 100644 --- a/src/modules/algorithm/models/hs_algorithm/mod.rs +++ b/src/modules/algorithm/models/hs_algorithm/mod.rs @@ -6,13 +6,11 @@ use ecdsa::signature::digest::block_buffer::Eager; use ecdsa::signature::digest::core_api::{BlockSizeUser, BufferKindUser, CoreProxy, FixedOutputCore, UpdateCore}; use ecdsa::signature::digest::HashMarker; use hmac::{Hmac, Mac}; -use sha2::Sha256; -use crate::algorithm::{JwAlgVerify, JwAlgSign, JwAlg}; +use crate::algorithm::{JwAlgVerify, JwAlgSign}; use hmac::digest::InvalidLength; #[cfg(feature = "rand")] use rand::RngCore; -use crate::algorithm::traits::partial_jw_alg::PartialJwAlg; #[derive(Clone)] pub struct HSPrivate(Hmac) @@ -51,20 +49,6 @@ where D: CoreProxy, } } -#[cfg(feature = "hs256")] -impl JwAlg for HSPrivate { - fn alg() -> impl AsRef { - "HS256" - } -} - -#[cfg(feature = "hs256")] -impl PartialJwAlg for HSPrivate { - fn partial_alg() -> Option> { - Some(Self::alg()) - } -} - impl JwAlgVerify for HSPrivate where D: CoreProxy, D::Core: HashMarker diff --git a/src/modules/algorithm/models/rs_algorithm/rs_private.rs b/src/modules/algorithm/models/rs_algorithm/rs_private.rs index fd405b2..62c347e 100644 --- a/src/modules/algorithm/models/rs_algorithm/rs_private.rs +++ b/src/modules/algorithm/models/rs_algorithm/rs_private.rs @@ -41,48 +41,6 @@ where D: Digest + AssociatedOid, } } -#[cfg(feature = "rs256")] -impl JwAlg for RSPrivate { - fn alg() -> impl AsRef { - "RS256" - } -} - -#[cfg(feature = "rs256")] -impl PartialJwAlg for RSPrivate { - fn partial_alg() -> Option> { - Some(Self::alg()) - } -} - -#[cfg(feature = "rs384")] -impl JwAlg for RSPrivate { - fn alg() -> impl AsRef { - "RS384" - } -} - -#[cfg(feature = "rs384")] -impl PartialJwAlg for RSPrivate { - fn partial_alg() -> Option> { - Some(Self::alg()) - } -} - -#[cfg(feature = "rs512")] -impl JwAlg for RSPrivate { - fn alg() -> impl AsRef { - "RS512" - } -} - -#[cfg(feature = "rs512")] -impl PartialJwAlg for RSPrivate { - fn partial_alg() -> Option> { - Some(Self::alg()) - } -} - impl JwAlgVerify for RSPrivate where D: Digest + AssociatedOid, { diff --git a/src/modules/algorithm/models/rs_algorithm/rs_public.rs b/src/modules/algorithm/models/rs_algorithm/rs_public.rs index 94d518d..37feb3b 100644 --- a/src/modules/algorithm/models/rs_algorithm/rs_public.rs +++ b/src/modules/algorithm/models/rs_algorithm/rs_public.rs @@ -1,54 +1,12 @@ use p384::ecdsa::signature::digest::Digest; use rsa::pkcs1v15::{Signature, VerifyingKey}; use rsa::signature::Verifier; -use crate::algorithm::{JwAlg, JwAlgVerify}; -use crate::algorithm::traits::partial_jw_alg::PartialJwAlg; +use crate::algorithm::{JwAlgVerify}; #[derive(Clone)] pub struct RSPublic(VerifyingKey) where D : Digest; -#[cfg(feature = "rs256")] -impl JwAlg for RSPublic { - fn alg() -> impl AsRef { - "RS256" - } -} - -impl PartialJwAlg for RSPublic { - fn partial_alg() -> Option> { - Some(Self::alg()) - } -} - -#[cfg(feature = "rs384")] -impl JwAlg for RSPublic { - fn alg() -> impl AsRef { - "RS384" - } -} - -#[cfg(feature = "rs384")] -impl PartialJwAlg for RSPublic { - fn partial_alg() -> Option> { - Some(Self::alg()) - } -} - -#[cfg(feature = "rs512")] -impl JwAlg for RSPublic { - fn alg() -> impl AsRef { - "RS512" - } -} - -#[cfg(feature = "rs512")] -impl PartialJwAlg for RSPublic { - fn partial_alg() -> Option> { - Some(Self::alg()) - } -} - impl JwAlgVerify for RSPublic where D : Digest { diff --git a/src/modules/key/mod.rs b/src/modules/key/mod.rs index 8ef6db5..9f4ead1 100644 --- a/src/modules/key/mod.rs +++ b/src/modules/key/mod.rs @@ -1,6 +1,12 @@ mod models; mod traits; -pub mod error; +mod error; + +pub use traits::jwk_private_params::JwkPrivateParams; +pub use traits::jwk_public_params::JwkPublicParams; +pub use traits::jwk_type::JwkType; +pub use models::jwk::Jwk; +pub use error::JwkError; #[cfg(all(test, feature = "rs256"))] mod tests { diff --git a/src/modules/key/traits.rs b/src/modules/key/traits.rs index 5a80ba2..5180b53 100644 --- a/src/modules/key/traits.rs +++ b/src/modules/key/traits.rs @@ -1 +1,3 @@ -pub mod jw_key_type; +pub mod jwk_public_params; +pub mod jwk_private_params; +pub mod jwk_type; diff --git a/src/modules/key/traits/jw_key_type.rs b/src/modules/key/traits/jw_key_type.rs deleted file mode 100644 index 0c7e3dc..0000000 --- a/src/modules/key/traits/jw_key_type.rs +++ /dev/null @@ -1,8 +0,0 @@ -use serde::{Deserialize, Serialize}; - -pub trait JwKeyType<'a> { - type Params: Serialize + Deserialize<'a>; - - fn kty() -> impl AsRef; - fn parms(&self) -> Self::Params; -} diff --git a/src/modules/key/traits/jwk_private_params.rs b/src/modules/key/traits/jwk_private_params.rs new file mode 100644 index 0000000..f13dc84 --- /dev/null +++ b/src/modules/key/traits/jwk_private_params.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + +pub trait JwkPrivateParams<'a> { + type PrivateParams: Serialize + Deserialize<'a>; + + fn get_private_params(&self) -> Self::PrivateParams; +} \ No newline at end of file diff --git a/src/modules/key/traits/jwk_public_params.rs b/src/modules/key/traits/jwk_public_params.rs new file mode 100644 index 0000000..ae6e485 --- /dev/null +++ b/src/modules/key/traits/jwk_public_params.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + +pub trait JwkPublicParams<'a> { + type PublicParams: Serialize + Deserialize<'a>; + + fn get_public_params(&self) -> Self::PublicParams; +} \ No newline at end of file diff --git a/src/modules/key/traits/jwk_type.rs b/src/modules/key/traits/jwk_type.rs new file mode 100644 index 0000000..e53aa7f --- /dev/null +++ b/src/modules/key/traits/jwk_type.rs @@ -0,0 +1,3 @@ +pub trait JwkType { + fn kty() -> impl AsRef; +} \ No newline at end of file From 94cee55a3e254cade2b60f2af2feeecf76d66143 Mon Sep 17 00:00:00 2001 From: rster2002 Date: Fri, 20 Mar 2026 15:09:07 +0100 Subject: [PATCH 12/17] =?UTF-8?q?=F0=9F=9A=A7=20Tiny=20progress?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/algorithm/algorithms/es256.rs | 13 +++++++++++-- .../algorithm/models/es_algorithm/es_private.rs | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/modules/algorithm/algorithms/es256.rs b/src/modules/algorithm/algorithms/es256.rs index cfed7ff..2560a6d 100644 --- a/src/modules/algorithm/algorithms/es256.rs +++ b/src/modules/algorithm/algorithms/es256.rs @@ -1,3 +1,6 @@ +use base64::Engine; +use base64::prelude::BASE64_URL_SAFE; +use ecdsa::elliptic_curve::point::{AffineCoordinates, DecompressPoint}; use crate::algorithm::{JwAlg, PartialJwAlg}; use crate::algorithm::models::es_algorithm::es_curve::EsCurve; use crate::algorithm::models::es_algorithm::es_private::ESPrivate; @@ -22,12 +25,18 @@ impl JwkPrivateParams<'_> for ESPrivate { type PrivateParams = EsPrivateParams; fn get_private_params(&self) -> Self::PrivateParams { + let verifying_key = self.0.verifying_key(); + let affine_point = verifying_key.as_affine(); + let x = affine_point.x(); + // let y = affine_point.decompact(); + + let d_bytes = self.0.as_nonzero_scalar().to_bytes(); EsPrivateParams { crv: EsCurve::P256, - x: "".to_string(), + x: BASE64_URL_SAFE.encode(&x), y: "".to_string(), - d: "".to_string(), + d: BASE64_URL_SAFE.encode(&d_bytes), } } } diff --git a/src/modules/algorithm/models/es_algorithm/es_private.rs b/src/modules/algorithm/models/es_algorithm/es_private.rs index 54a5d94..c21b743 100644 --- a/src/modules/algorithm/models/es_algorithm/es_private.rs +++ b/src/modules/algorithm/models/es_algorithm/es_private.rs @@ -13,7 +13,7 @@ use crate::algorithm::traits::partial_jw_alg::PartialJwAlg; use crate::modules::key::{JwkPrivateParams, JwkType}; #[derive(Clone)] -pub struct ESPrivate(pub SigningKey) +pub struct ESPrivate(pub(crate) SigningKey) where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, AffinePoint: VerifyPrimitive, From 68dffb190dfe608a8835007e765640d0f443961a Mon Sep 17 00:00:00 2001 From: rster2002 Date: Sun, 22 Mar 2026 21:41:38 +0100 Subject: [PATCH 13/17] =?UTF-8?q?=F0=9F=9A=A7=20Continue=20working=20on=20?= =?UTF-8?q?JWKs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib.rs | 16 +++++++- src/modules/algorithm/algorithms/es256.rs | 38 ++++++++++++++----- .../algorithm/models/es_algorithm/es_curve.rs | 2 +- .../models/es_algorithm/es_private_params.rs | 4 +- .../models/es_algorithm/es_public.rs | 2 +- .../models/es_algorithm/es_public_params.rs | 6 +-- .../algorithm/models/hs_algorithm/mod.rs | 2 +- src/modules/key/models/jwk.rs | 3 ++ src/modules/key/traits/jwk_private_params.rs | 2 +- 9 files changed, 56 insertions(+), 19 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d7956e8..169ee0e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,18 @@ mod modules; pub use modules::token; -pub use modules::algorithm; \ No newline at end of file +pub use modules::algorithm; + +#[cfg(test)] +mod tests { + use crate::algorithm::ES256Private; + use crate::modules::key::JwkPrivateParams; + + #[test] + fn test_es256() { + let key = ES256Private::rand(); + let params = key.get_private_params(); + + dbg!(¶ms); + } +} \ No newline at end of file diff --git a/src/modules/algorithm/algorithms/es256.rs b/src/modules/algorithm/algorithms/es256.rs index 2560a6d..cb1e823 100644 --- a/src/modules/algorithm/algorithms/es256.rs +++ b/src/modules/algorithm/algorithms/es256.rs @@ -1,11 +1,13 @@ use base64::Engine; use base64::prelude::BASE64_URL_SAFE; use ecdsa::elliptic_curve::point::{AffineCoordinates, DecompressPoint}; +use ecdsa::EncodedPoint; use crate::algorithm::{JwAlg, PartialJwAlg}; -use crate::algorithm::models::es_algorithm::es_curve::EsCurve; +use crate::algorithm::models::es_algorithm::es_curve::ESCurve; use crate::algorithm::models::es_algorithm::es_private::ESPrivate; use crate::algorithm::models::es_algorithm::es_private_params::EsPrivateParams; use crate::algorithm::models::es_algorithm::es_public::ESPublic; +use crate::algorithm::models::es_algorithm::es_public_params::ESPublicParams; use crate::modules::key::JwkPrivateParams; // Private @@ -24,20 +26,21 @@ impl PartialJwAlg for ESPrivate { impl JwkPrivateParams<'_> for ESPrivate { type PrivateParams = EsPrivateParams; - fn get_private_params(&self) -> Self::PrivateParams { + fn get_private_params(&self) -> Option { let verifying_key = self.0.verifying_key(); let affine_point = verifying_key.as_affine(); - let x = affine_point.x(); - // let y = affine_point.decompact(); - + let encoded_point: EncodedPoint = affine_point.clone().into(); // TODO remove clone + let x = encoded_point.x()?; + let y = encoded_point.y()?; + let d_bytes = self.0.as_nonzero_scalar().to_bytes(); - EsPrivateParams { - crv: EsCurve::P256, + Some(EsPrivateParams { + crv: ESCurve::P256, x: BASE64_URL_SAFE.encode(&x), - y: "".to_string(), + y: BASE64_URL_SAFE.encode(&y), d: BASE64_URL_SAFE.encode(&d_bytes), - } + }) } } @@ -52,4 +55,21 @@ impl PartialJwAlg for ESPublic { fn partial_alg() -> Option> { Some(Self::alg()) } +} + +impl JwkPrivateParams<'_> for ESPublic { + type PrivateParams = ESPublicParams; + + fn get_private_params(&self) -> Option { + let affine_point = self.0.as_affine(); + let encoded_point: EncodedPoint = affine_point.clone().into(); // TODO remove clone + let x = encoded_point.x()?; + let y = encoded_point.y()?; + + Some(ESPublicParams { + crv: ESCurve::P256, + x: BASE64_URL_SAFE.encode(&x), + y: BASE64_URL_SAFE.encode(&y), + }) + } } \ No newline at end of file diff --git a/src/modules/algorithm/models/es_algorithm/es_curve.rs b/src/modules/algorithm/models/es_algorithm/es_curve.rs index 4b3e397..4f8e729 100644 --- a/src/modules/algorithm/models/es_algorithm/es_curve.rs +++ b/src/modules/algorithm/models/es_algorithm/es_curve.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] -pub enum EsCurve { +pub enum ESCurve { #[serde(rename = "P-256")] P256, diff --git a/src/modules/algorithm/models/es_algorithm/es_private_params.rs b/src/modules/algorithm/models/es_algorithm/es_private_params.rs index 4aed235..d92b88c 100644 --- a/src/modules/algorithm/models/es_algorithm/es_private_params.rs +++ b/src/modules/algorithm/models/es_algorithm/es_private_params.rs @@ -1,9 +1,9 @@ use serde::{Deserialize, Serialize}; -use crate::algorithm::models::es_algorithm::es_curve::EsCurve; +use crate::algorithm::models::es_algorithm::es_curve::ESCurve; #[derive(Debug, Serialize, Deserialize)] pub struct EsPrivateParams { - pub crv: EsCurve, + pub crv: ESCurve, pub x: String, pub y: String, pub d: String, diff --git a/src/modules/algorithm/models/es_algorithm/es_public.rs b/src/modules/algorithm/models/es_algorithm/es_public.rs index cff2154..c705d64 100644 --- a/src/modules/algorithm/models/es_algorithm/es_public.rs +++ b/src/modules/algorithm/models/es_algorithm/es_public.rs @@ -7,7 +7,7 @@ use ecdsa::signature::Verifier; use crate::algorithm::traits::partial_jw_alg::PartialJwAlg; #[derive(Debug, Clone, PartialEq, Eq)] -pub struct ESPublic(VerifyingKey) +pub struct ESPublic(pub(crate) VerifyingKey) where C: PrimeCurve + CurveArithmetic + DigestPrimitive, AffinePoint: VerifyPrimitive, diff --git a/src/modules/algorithm/models/es_algorithm/es_public_params.rs b/src/modules/algorithm/models/es_algorithm/es_public_params.rs index 1a443d3..9f53c66 100644 --- a/src/modules/algorithm/models/es_algorithm/es_public_params.rs +++ b/src/modules/algorithm/models/es_algorithm/es_public_params.rs @@ -1,9 +1,9 @@ use serde::{Deserialize, Serialize}; -use crate::algorithm::models::es_algorithm::es_curve::EsCurve; +use crate::algorithm::models::es_algorithm::es_curve::ESCurve; #[derive(Debug, Serialize, Deserialize)] -pub struct EsPublicParams { - pub crv: EsCurve, +pub struct ESPublicParams { + pub crv: ESCurve, pub x: String, pub y: String, } \ No newline at end of file diff --git a/src/modules/algorithm/models/hs_algorithm/mod.rs b/src/modules/algorithm/models/hs_algorithm/mod.rs index f7dfd9b..2421446 100644 --- a/src/modules/algorithm/models/hs_algorithm/mod.rs +++ b/src/modules/algorithm/models/hs_algorithm/mod.rs @@ -118,6 +118,6 @@ mod tests { #[test] fn hs256_can_be_generated_randomly() { - HS256Private::rand().unwrap(); + // HS256Private::rand().unwrap(); } } diff --git a/src/modules/key/models/jwk.rs b/src/modules/key/models/jwk.rs index 26720b0..1b5c015 100644 --- a/src/modules/key/models/jwk.rs +++ b/src/modules/key/models/jwk.rs @@ -19,6 +19,9 @@ pub struct Jwk { #[serde(rename = "x5t#S256")] pub x5t_s256: Option, + + #[serde(flatten)] + pub params: Option, } impl TryFrom for Jwk { diff --git a/src/modules/key/traits/jwk_private_params.rs b/src/modules/key/traits/jwk_private_params.rs index f13dc84..d941a2c 100644 --- a/src/modules/key/traits/jwk_private_params.rs +++ b/src/modules/key/traits/jwk_private_params.rs @@ -3,5 +3,5 @@ use serde::{Deserialize, Serialize}; pub trait JwkPrivateParams<'a> { type PrivateParams: Serialize + Deserialize<'a>; - fn get_private_params(&self) -> Self::PrivateParams; + fn get_private_params(&self) -> Option; } \ No newline at end of file From b1b6da24acf82c1529ea0ce3e36ba94bb8b1e349 Mon Sep 17 00:00:00 2001 From: rster2002 Date: Mon, 23 Mar 2026 20:31:35 +0100 Subject: [PATCH 14/17] =?UTF-8?q?=F0=9F=9A=A7=20Continue=20working=20on=20?= =?UTF-8?q?JWKs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/algorithm/algorithms.rs | 6 ---- src/modules/algorithm/mod.rs | 18 +----------- src/modules/algorithm/models.rs | 2 +- .../{algorithms => models/es}/es256.rs | 29 ++++++++++--------- .../{algorithms => models/es}/es384.rs | 13 +++++---- .../models/{es_algorithm => es}/es_curve.rs | 0 .../models/{es_algorithm => es}/es_private.rs | 2 +- .../{es_algorithm => es}/es_private_params.rs | 4 +-- .../models/{es_algorithm => es}/es_public.rs | 0 .../{es_algorithm => es}/es_public_params.rs | 2 +- src/modules/algorithm/models/es/mod.rs | 17 +++++++++++ .../algorithm/models/es_algorithm/mod.rs | 5 ---- src/modules/key/models.rs | 4 ++- src/modules/key/models/jwk.rs | 11 ++----- src/modules/key/models/jwk_params.rs | 9 ++++++ 15 files changed, 62 insertions(+), 60 deletions(-) rename src/modules/algorithm/{algorithms => models/es}/es256.rs (69%) rename src/modules/algorithm/{algorithms => models/es}/es384.rs (56%) rename src/modules/algorithm/models/{es_algorithm => es}/es_curve.rs (100%) rename src/modules/algorithm/models/{es_algorithm => es}/es_private.rs (98%) rename src/modules/algorithm/models/{es_algorithm => es}/es_private_params.rs (63%) rename src/modules/algorithm/models/{es_algorithm => es}/es_public.rs (100%) rename src/modules/algorithm/models/{es_algorithm => es}/es_public_params.rs (72%) create mode 100644 src/modules/algorithm/models/es/mod.rs delete mode 100644 src/modules/algorithm/models/es_algorithm/mod.rs create mode 100644 src/modules/key/models/jwk_params.rs diff --git a/src/modules/algorithm/algorithms.rs b/src/modules/algorithm/algorithms.rs index b1da59e..642394a 100644 --- a/src/modules/algorithm/algorithms.rs +++ b/src/modules/algorithm/algorithms.rs @@ -1,9 +1,3 @@ -#[cfg(feature = "es256")] -pub mod es256; - -#[cfg(feature = "es384")] -pub mod es384; - #[cfg(feature = "hs256")] pub mod hs256; diff --git a/src/modules/algorithm/mod.rs b/src/modules/algorithm/mod.rs index 38a53d8..0111a2e 100644 --- a/src/modules/algorithm/mod.rs +++ b/src/modules/algorithm/mod.rs @@ -9,24 +9,8 @@ pub use traits::partial_jw_alg::PartialJwAlg; pub use models::none_algorithm::NoneAlgorithm; -// ES #[cfg(any(feature = "es256", feature = "es384"))] -use crate::algorithm::models::es_algorithm::es_private::ESPrivate; - -#[cfg(any(feature = "es256", feature = "es384"))] -use crate::algorithm::models::es_algorithm::es_public::ESPublic; - -#[cfg(feature = "es256")] -pub type ES256Private = ESPrivate; - -#[cfg(feature = "es256")] -pub type ES256Public = ESPublic; - -#[cfg(feature = "es384")] -pub type ES384Private = ESPrivate; - -#[cfg(feature = "es384")] -pub type ES384Public = ESPublic; +pub use models::es; // HS #[cfg(any(feature = "hs256", feature = "hs384", feature = "hs512"))] diff --git a/src/modules/algorithm/models.rs b/src/modules/algorithm/models.rs index 0fc1af2..9891367 100644 --- a/src/modules/algorithm/models.rs +++ b/src/modules/algorithm/models.rs @@ -7,4 +7,4 @@ pub mod hs_algorithm; pub mod rs_algorithm; #[cfg(any(feature = "es256", feature = "es384"))] -pub mod es_algorithm; \ No newline at end of file +pub mod es; \ No newline at end of file diff --git a/src/modules/algorithm/algorithms/es256.rs b/src/modules/algorithm/models/es/es256.rs similarity index 69% rename from src/modules/algorithm/algorithms/es256.rs rename to src/modules/algorithm/models/es/es256.rs index cb1e823..9a9b529 100644 --- a/src/modules/algorithm/algorithms/es256.rs +++ b/src/modules/algorithm/models/es/es256.rs @@ -3,28 +3,29 @@ use base64::prelude::BASE64_URL_SAFE; use ecdsa::elliptic_curve::point::{AffineCoordinates, DecompressPoint}; use ecdsa::EncodedPoint; use crate::algorithm::{JwAlg, PartialJwAlg}; -use crate::algorithm::models::es_algorithm::es_curve::ESCurve; -use crate::algorithm::models::es_algorithm::es_private::ESPrivate; -use crate::algorithm::models::es_algorithm::es_private_params::EsPrivateParams; -use crate::algorithm::models::es_algorithm::es_public::ESPublic; -use crate::algorithm::models::es_algorithm::es_public_params::ESPublicParams; +use crate::algorithm::es::{ESPublic, ESPublicParams}; +use crate::algorithm::models::es::es_curve::ESCurve; +use crate::algorithm::models::es::es_private::ESPrivate; +use crate::algorithm::models::es::es_private_params::ESPrivateParams; use crate::modules::key::JwkPrivateParams; // Private -impl JwAlg for ESPrivate { +pub type ES256Private = ESPrivate; + +impl JwAlg for ES256Private { fn alg() -> impl AsRef { "ES256" } } -impl PartialJwAlg for ESPrivate { +impl PartialJwAlg for ES256Private { fn partial_alg() -> Option> { Some(Self::alg()) } } -impl JwkPrivateParams<'_> for ESPrivate { - type PrivateParams = EsPrivateParams; +impl JwkPrivateParams<'_> for ES256Private { + type PrivateParams = ESPrivateParams; fn get_private_params(&self) -> Option { let verifying_key = self.0.verifying_key(); @@ -35,7 +36,7 @@ impl JwkPrivateParams<'_> for ESPrivate { let d_bytes = self.0.as_nonzero_scalar().to_bytes(); - Some(EsPrivateParams { + Some(ESPrivateParams { crv: ESCurve::P256, x: BASE64_URL_SAFE.encode(&x), y: BASE64_URL_SAFE.encode(&y), @@ -45,19 +46,21 @@ impl JwkPrivateParams<'_> for ESPrivate { } // Public -impl JwAlg for ESPublic { +pub type ES256Public = ESPublic; + +impl JwAlg for ES256Public { fn alg() -> impl AsRef { "ES256" } } -impl PartialJwAlg for ESPublic { +impl PartialJwAlg for ES256Public { fn partial_alg() -> Option> { Some(Self::alg()) } } -impl JwkPrivateParams<'_> for ESPublic { +impl JwkPrivateParams<'_> for ES256Public { type PrivateParams = ESPublicParams; fn get_private_params(&self) -> Option { diff --git a/src/modules/algorithm/algorithms/es384.rs b/src/modules/algorithm/models/es/es384.rs similarity index 56% rename from src/modules/algorithm/algorithms/es384.rs rename to src/modules/algorithm/models/es/es384.rs index 11ecffc..f3a17c1 100644 --- a/src/modules/algorithm/algorithms/es384.rs +++ b/src/modules/algorithm/models/es/es384.rs @@ -1,9 +1,11 @@ use crate::algorithm::{JwAlg, PartialJwAlg}; -use crate::algorithm::models::es_algorithm::es_private::ESPrivate; -use crate::algorithm::models::es_algorithm::es_public::ESPublic; +use crate::algorithm::models::es::es_private::ESPrivate; +use crate::algorithm::models::es::es_public::ESPublic; // Private -impl JwAlg for ESPrivate { +pub type ES384Private = ESPrivate; + +impl JwAlg for ES384Private { fn alg() -> impl AsRef { "ES384" } @@ -16,14 +18,15 @@ impl PartialJwAlg for ESPrivate { } // Public +pub type ES384Public = ESPublic; -impl JwAlg for ESPublic { +impl JwAlg for ES384Public { fn alg() -> impl AsRef { "ES384" } } -impl PartialJwAlg for ESPublic { +impl PartialJwAlg for ES384Public { fn partial_alg() -> Option> { Some(Self::alg()) } diff --git a/src/modules/algorithm/models/es_algorithm/es_curve.rs b/src/modules/algorithm/models/es/es_curve.rs similarity index 100% rename from src/modules/algorithm/models/es_algorithm/es_curve.rs rename to src/modules/algorithm/models/es/es_curve.rs diff --git a/src/modules/algorithm/models/es_algorithm/es_private.rs b/src/modules/algorithm/models/es/es_private.rs similarity index 98% rename from src/modules/algorithm/models/es_algorithm/es_private.rs rename to src/modules/algorithm/models/es/es_private.rs index c21b743..a13ce76 100644 --- a/src/modules/algorithm/models/es_algorithm/es_private.rs +++ b/src/modules/algorithm/models/es/es_private.rs @@ -93,7 +93,7 @@ where C: PrimeCurve + CurveArithmetic + DigestPrimitive, #[cfg(test)] mod tests { - use crate::algorithm::ES256Private; + use crate::algorithm::es::es256::ES256Private; #[test] fn es256_can_be_generated_randomly() { diff --git a/src/modules/algorithm/models/es_algorithm/es_private_params.rs b/src/modules/algorithm/models/es/es_private_params.rs similarity index 63% rename from src/modules/algorithm/models/es_algorithm/es_private_params.rs rename to src/modules/algorithm/models/es/es_private_params.rs index d92b88c..ce7a26d 100644 --- a/src/modules/algorithm/models/es_algorithm/es_private_params.rs +++ b/src/modules/algorithm/models/es/es_private_params.rs @@ -1,8 +1,8 @@ use serde::{Deserialize, Serialize}; -use crate::algorithm::models::es_algorithm::es_curve::ESCurve; +use crate::algorithm::es::ESCurve; #[derive(Debug, Serialize, Deserialize)] -pub struct EsPrivateParams { +pub struct ESPrivateParams { pub crv: ESCurve, pub x: String, pub y: String, diff --git a/src/modules/algorithm/models/es_algorithm/es_public.rs b/src/modules/algorithm/models/es/es_public.rs similarity index 100% rename from src/modules/algorithm/models/es_algorithm/es_public.rs rename to src/modules/algorithm/models/es/es_public.rs diff --git a/src/modules/algorithm/models/es_algorithm/es_public_params.rs b/src/modules/algorithm/models/es/es_public_params.rs similarity index 72% rename from src/modules/algorithm/models/es_algorithm/es_public_params.rs rename to src/modules/algorithm/models/es/es_public_params.rs index 9f53c66..fd542b0 100644 --- a/src/modules/algorithm/models/es_algorithm/es_public_params.rs +++ b/src/modules/algorithm/models/es/es_public_params.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use crate::algorithm::models::es_algorithm::es_curve::ESCurve; +use crate::algorithm::models::es::es_curve::ESCurve; #[derive(Debug, Serialize, Deserialize)] pub struct ESPublicParams { diff --git a/src/modules/algorithm/models/es/mod.rs b/src/modules/algorithm/models/es/mod.rs new file mode 100644 index 0000000..847ff99 --- /dev/null +++ b/src/modules/algorithm/models/es/mod.rs @@ -0,0 +1,17 @@ +mod es_private; +mod es_public; +mod es_private_params; +mod es_curve; +mod es_public_params; + +pub use es_private::*; +pub use es_public::*; +pub use es_private_params::*; +pub use es_curve::*; +pub use es_public_params::*; + +#[cfg(feature = "es256")] +pub mod es256; + +#[cfg(feature = "es384")] +pub mod es384; \ No newline at end of file diff --git a/src/modules/algorithm/models/es_algorithm/mod.rs b/src/modules/algorithm/models/es_algorithm/mod.rs deleted file mode 100644 index ddeeeb8..0000000 --- a/src/modules/algorithm/models/es_algorithm/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod es_private; -pub mod es_public; -pub mod es_private_params; -pub mod es_curve; -pub mod es_public_params; \ No newline at end of file diff --git a/src/modules/key/models.rs b/src/modules/key/models.rs index ddda118..ba1f7ab 100644 --- a/src/modules/key/models.rs +++ b/src/modules/key/models.rs @@ -5,4 +5,6 @@ pub mod jwk; pub mod jwt_use; pub mod jwt_key_op; -pub mod jwt_set; \ No newline at end of file +pub mod jwt_set; +pub mod jwk_params; +pub mod jwk_qualified; \ No newline at end of file diff --git a/src/modules/key/models/jwk.rs b/src/modules/key/models/jwk.rs index 1b5c015..e44452f 100644 --- a/src/modules/key/models/jwk.rs +++ b/src/modules/key/models/jwk.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; +use crate::modules::key::models::jwk_params::JwkParams; use crate::modules::key::models::jwt_key_op::JwtKeyOp; use crate::modules::key::models::jwt_use::JwtUse; use crate::token::JwtError; @@ -21,13 +22,7 @@ pub struct Jwk { pub x5t_s256: Option, #[serde(flatten)] - pub params: Option, + pub params: JwkParams, } -impl TryFrom for Jwk { - type Error = JwtError; - - fn try_from(value: Value) -> Result { - todo!() - } -} \ No newline at end of file +impl diff --git a/src/modules/key/models/jwk_params.rs b/src/modules/key/models/jwk_params.rs new file mode 100644 index 0000000..1328e21 --- /dev/null +++ b/src/modules/key/models/jwk_params.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; +use crate::algorithm::es::{ESPrivateParams, ESPublicParams}; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum JwkParams { + ESPrivateParams(ESPrivateParams), + ESPublicParams(ESPublicParams), +} \ No newline at end of file From 885cfdcf0da0ebebb52f666d1d6bd9d8559fd366 Mon Sep 17 00:00:00 2001 From: rster2002 Date: Wed, 25 Mar 2026 17:34:19 +0100 Subject: [PATCH 15/17] =?UTF-8?q?=F0=9F=9A=A7=20Continue=20breaking=20thin?= =?UTF-8?q?gs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib.rs | 2 +- src/modules/algorithm/algorithms.rs | 9 -- src/modules/algorithm/mod.rs | 82 ++++++++++--------- src/modules/algorithm/models.rs | 4 +- src/modules/algorithm/models/es/mod.rs | 5 +- .../{algorithms => models/hs}/hs256.rs | 8 +- .../models/{hs_algorithm => hs}/mod.rs | 10 ++- src/modules/algorithm/models/rs/mod.rs | 20 +++++ .../{algorithms => models/rs}/rs256.rs | 16 ++-- .../{algorithms => models/rs}/rs384.rs | 16 ++-- .../{algorithms => models/rs}/rs512.rs | 16 ++-- .../models/{rs_algorithm => rs}/rs_private.rs | 3 +- .../models/{rs_algorithm => rs}/rs_public.rs | 0 .../algorithm/models/rs_algorithm/mod.rs | 2 - src/modules/key/mod.rs | 13 ++- src/modules/key/models.rs | 3 +- src/modules/key/models/jwk.rs | 7 +- src/modules/token/mod.rs | 3 +- 18 files changed, 129 insertions(+), 90 deletions(-) rename src/modules/algorithm/{algorithms => models/hs}/hs256.rs (58%) rename src/modules/algorithm/models/{hs_algorithm => hs}/mod.rs (94%) create mode 100644 src/modules/algorithm/models/rs/mod.rs rename src/modules/algorithm/{algorithms => models/rs}/rs256.rs (50%) rename src/modules/algorithm/{algorithms => models/rs}/rs384.rs (50%) rename src/modules/algorithm/{algorithms => models/rs}/rs512.rs (50%) rename src/modules/algorithm/models/{rs_algorithm => rs}/rs_private.rs (97%) rename src/modules/algorithm/models/{rs_algorithm => rs}/rs_public.rs (100%) delete mode 100644 src/modules/algorithm/models/rs_algorithm/mod.rs diff --git a/src/lib.rs b/src/lib.rs index 169ee0e..2590169 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ pub use modules::algorithm; #[cfg(test)] mod tests { - use crate::algorithm::ES256Private; + use crate::algorithm::es::ES256Private; use crate::modules::key::JwkPrivateParams; #[test] diff --git a/src/modules/algorithm/algorithms.rs b/src/modules/algorithm/algorithms.rs index 642394a..139597f 100644 --- a/src/modules/algorithm/algorithms.rs +++ b/src/modules/algorithm/algorithms.rs @@ -1,11 +1,2 @@ -#[cfg(feature = "hs256")] -pub mod hs256; -#[cfg(feature = "rs256")] -pub mod rs256; -#[cfg(feature = "rs384")] -pub mod rs384; - -#[cfg(feature = "rs512")] -pub mod rs512; \ No newline at end of file diff --git a/src/modules/algorithm/mod.rs b/src/modules/algorithm/mod.rs index 0111a2e..c640e45 100644 --- a/src/modules/algorithm/mod.rs +++ b/src/modules/algorithm/mod.rs @@ -9,43 +9,45 @@ pub use traits::partial_jw_alg::PartialJwAlg; pub use models::none_algorithm::NoneAlgorithm; -#[cfg(any(feature = "es256", feature = "es384"))] -pub use models::es; - -// HS -#[cfg(any(feature = "hs256", feature = "hs384", feature = "hs512"))] -use crate::algorithm::models::hs_algorithm::HSPrivate; - -#[cfg(feature = "hs256")] -pub type HS256Private = HSPrivate; - -#[cfg(feature = "hs384")] -pub type HS384Private = HSPrivate; - -#[cfg(feature = "hs512")] -pub type HS512Private = HSPrivate; - -// RS -#[cfg(any(feature = "rs256", feature = "rs384", feature = "rs512"))] -use crate::algorithm::models::rs_algorithm::rs_private::RSPrivate; - -#[cfg(any(feature = "rs256", feature = "rs384", feature = "rs512"))] -use crate::algorithm::models::rs_algorithm::rs_public::RSPublic; - -#[cfg(feature = "rs256")] -pub type RS256Private = RSPrivate; - -#[cfg(feature = "rs256")] -pub type RS256Public = RSPublic; - -#[cfg(feature = "rs384")] -pub type RS384Private = RSPrivate; - -#[cfg(feature = "rs384")] -pub type RS384Public = RSPublic; - -#[cfg(feature = "rs512")] -pub type RS512Private = RSPrivate; - -#[cfg(feature = "rs512")] -pub type RS512Public = RSPublic; +pub use models::*; + +// #[cfg(any(feature = "es256", feature = "es384"))] +// pub use models::es; + +// // HS +// #[cfg(any(feature = "hs256", feature = "hs384", feature = "hs512"))] +// use crate::algorithm::models::hs_algorithm::HSPrivate; +// +// #[cfg(feature = "hs256")] +// pub type HS256Private = HSPrivate; +// +// #[cfg(feature = "hs384")] +// pub type HS384Private = HSPrivate; +// +// #[cfg(feature = "hs512")] +// pub type HS512Private = HSPrivate; +// +// // RS +// #[cfg(any(feature = "rs256", feature = "rs384", feature = "rs512"))] +// use crate::algorithm::models::rs::rs_private::RSPrivate; +// +// #[cfg(any(feature = "rs256", feature = "rs384", feature = "rs512"))] +// use crate::algorithm::models::rs_algorithm::rs_public::RSPublic; +// +// #[cfg(feature = "rs256")] +// pub type RS256Private = RSPrivate; +// +// #[cfg(feature = "rs256")] +// pub type RS256Public = RSPublic; +// +// #[cfg(feature = "rs384")] +// pub type RS384Private = RSPrivate; +// +// #[cfg(feature = "rs384")] +// pub type RS384Public = RSPublic; +// +// #[cfg(feature = "rs512")] +// pub type RS512Private = RSPrivate; +// +// #[cfg(feature = "rs512")] +// pub type RS512Public = RSPublic; diff --git a/src/modules/algorithm/models.rs b/src/modules/algorithm/models.rs index 9891367..0de9f3f 100644 --- a/src/modules/algorithm/models.rs +++ b/src/modules/algorithm/models.rs @@ -1,10 +1,10 @@ pub mod none_algorithm; #[cfg(any(feature = "hs256", feature = "hs384", feature = "hs512"))] -pub mod hs_algorithm; +pub mod hs; #[cfg(any(feature = "rs256", feature = "rs384", feature = "rs512"))] -pub mod rs_algorithm; +pub mod rs; #[cfg(any(feature = "es256", feature = "es384"))] pub mod es; \ No newline at end of file diff --git a/src/modules/algorithm/models/es/mod.rs b/src/modules/algorithm/models/es/mod.rs index 847ff99..8abc49b 100644 --- a/src/modules/algorithm/models/es/mod.rs +++ b/src/modules/algorithm/models/es/mod.rs @@ -11,7 +11,10 @@ pub use es_curve::*; pub use es_public_params::*; #[cfg(feature = "es256")] -pub mod es256; +mod es256; + +#[cfg(feature = "es256")] +pub use es256::*; #[cfg(feature = "es384")] pub mod es384; \ No newline at end of file diff --git a/src/modules/algorithm/algorithms/hs256.rs b/src/modules/algorithm/models/hs/hs256.rs similarity index 58% rename from src/modules/algorithm/algorithms/hs256.rs rename to src/modules/algorithm/models/hs/hs256.rs index 938c491..a993d9a 100644 --- a/src/modules/algorithm/algorithms/hs256.rs +++ b/src/modules/algorithm/models/hs/hs256.rs @@ -1,14 +1,16 @@ use sha2::Sha256; use crate::algorithm::{JwAlg, PartialJwAlg}; -use crate::algorithm::models::hs_algorithm::HSPrivate; +use crate::algorithm::hs::HSPrivate; -impl JwAlg for HSPrivate { +pub type HS256Private = HSPrivate; + +impl JwAlg for HS256Private { fn alg() -> impl AsRef { "HS256" } } -impl PartialJwAlg for HSPrivate { +impl PartialJwAlg for HS256Private { fn partial_alg() -> Option> { Some(Self::alg()) } diff --git a/src/modules/algorithm/models/hs_algorithm/mod.rs b/src/modules/algorithm/models/hs/mod.rs similarity index 94% rename from src/modules/algorithm/models/hs_algorithm/mod.rs rename to src/modules/algorithm/models/hs/mod.rs index 2421446..b957666 100644 --- a/src/modules/algorithm/models/hs_algorithm/mod.rs +++ b/src/modules/algorithm/models/hs/mod.rs @@ -12,6 +12,12 @@ use hmac::digest::InvalidLength; #[cfg(feature = "rand")] use rand::RngCore; +#[cfg(feature = "hs256")] +mod hs256; + +#[cfg(feature = "hs256")] +pub use hs256::HS256Private; + #[derive(Clone)] pub struct HSPrivate(Hmac) where D: CoreProxy, @@ -97,9 +103,9 @@ where D: CoreProxy, mod tests { use base64::Engine; use base64::prelude::BASE64_URL_SAFE_NO_PAD; + use crate::algorithm::hs::HS256Private; use crate::algorithm::JwAlgSign; - use crate::algorithm::models::hs_algorithm::HSPrivate; - use crate::modules::algorithm::{HS256Private, JwAlgVerify}; + use crate::modules::algorithm::{JwAlgVerify}; #[test] fn hs256_algorithm_works_as_expected() { diff --git a/src/modules/algorithm/models/rs/mod.rs b/src/modules/algorithm/models/rs/mod.rs new file mode 100644 index 0000000..521af42 --- /dev/null +++ b/src/modules/algorithm/models/rs/mod.rs @@ -0,0 +1,20 @@ +pub mod rs_public; +pub mod rs_private; + +#[cfg(feature = "rs256")] +mod rs256; + +#[cfg(feature = "rs256")] +pub use rs256::*; + +#[cfg(feature = "rs384")] +mod rs384; + +#[cfg(feature = "rs384")] +pub use rs384::*; + +#[cfg(feature = "rs512")] +pub mod rs512; + +#[cfg(feature = "rs512")] +pub use rs512::*; \ No newline at end of file diff --git a/src/modules/algorithm/algorithms/rs256.rs b/src/modules/algorithm/models/rs/rs256.rs similarity index 50% rename from src/modules/algorithm/algorithms/rs256.rs rename to src/modules/algorithm/models/rs/rs256.rs index cddd270..53f2913 100644 --- a/src/modules/algorithm/algorithms/rs256.rs +++ b/src/modules/algorithm/models/rs/rs256.rs @@ -1,28 +1,32 @@ use crate::algorithm::{JwAlg, PartialJwAlg}; -use crate::algorithm::models::rs_algorithm::rs_private::RSPrivate; -use crate::algorithm::models::rs_algorithm::rs_public::RSPublic; +use crate::algorithm::models::rs::rs_private::RSPrivate; +use crate::algorithm::models::rs::rs_public::RSPublic; // Private -impl JwAlg for RSPrivate { +pub type RS256Private = RSPrivate; + +impl JwAlg for RS256Private { fn alg() -> impl AsRef { "RS256" } } -impl PartialJwAlg for RSPrivate { +impl PartialJwAlg for RS256Private { fn partial_alg() -> Option> { Some(Self::alg()) } } // Public -impl JwAlg for RSPublic { +pub type RS256Public = RSPublic; + +impl JwAlg for RS256Public { fn alg() -> impl AsRef { "RS256" } } -impl PartialJwAlg for RSPublic { +impl PartialJwAlg for RS256Public { fn partial_alg() -> Option> { Some(Self::alg()) } diff --git a/src/modules/algorithm/algorithms/rs384.rs b/src/modules/algorithm/models/rs/rs384.rs similarity index 50% rename from src/modules/algorithm/algorithms/rs384.rs rename to src/modules/algorithm/models/rs/rs384.rs index 0bfa07d..6b4a762 100644 --- a/src/modules/algorithm/algorithms/rs384.rs +++ b/src/modules/algorithm/models/rs/rs384.rs @@ -1,28 +1,32 @@ use crate::algorithm::{JwAlg, PartialJwAlg}; -use crate::algorithm::models::rs_algorithm::rs_private::RSPrivate; -use crate::algorithm::models::rs_algorithm::rs_public::RSPublic; +use crate::algorithm::models::rs::rs_private::RSPrivate; +use crate::algorithm::models::rs::rs_public::RSPublic; // Private -impl JwAlg for RSPrivate { +pub type RS384Private = RSPrivate; + +impl JwAlg for RS384Private { fn alg() -> impl AsRef { "RS384" } } -impl PartialJwAlg for RSPrivate { +impl PartialJwAlg for RS384Private { fn partial_alg() -> Option> { Some(Self::alg()) } } // Public -impl JwAlg for RSPublic { +pub type RS384Public = RSPublic; + +impl JwAlg for RS384Public { fn alg() -> impl AsRef { "RS384" } } -impl PartialJwAlg for RSPublic { +impl PartialJwAlg for RS384Public { fn partial_alg() -> Option> { Some(Self::alg()) } diff --git a/src/modules/algorithm/algorithms/rs512.rs b/src/modules/algorithm/models/rs/rs512.rs similarity index 50% rename from src/modules/algorithm/algorithms/rs512.rs rename to src/modules/algorithm/models/rs/rs512.rs index b2e71ee..3d63ea6 100644 --- a/src/modules/algorithm/algorithms/rs512.rs +++ b/src/modules/algorithm/models/rs/rs512.rs @@ -1,28 +1,32 @@ use crate::algorithm::{JwAlg, PartialJwAlg}; -use crate::algorithm::models::rs_algorithm::rs_private::RSPrivate; -use crate::algorithm::models::rs_algorithm::rs_public::RSPublic; +use crate::algorithm::models::rs::rs_private::RSPrivate; +use crate::algorithm::models::rs::rs_public::RSPublic; // Private -impl JwAlg for RSPrivate { +pub type RS512Private = RSPrivate; + +impl JwAlg for RS512Private { fn alg() -> impl AsRef { "RS512" } } -impl PartialJwAlg for RSPrivate { +impl PartialJwAlg for RS512Private { fn partial_alg() -> Option> { Some(Self::alg()) } } // Public -impl JwAlg for RSPublic { +pub type RS512Public = RSPublic; + +impl JwAlg for RS512Public { fn alg() -> impl AsRef { "RS512" } } -impl PartialJwAlg for RSPublic { +impl PartialJwAlg for RS512Public { fn partial_alg() -> Option> { Some(Self::alg()) } diff --git a/src/modules/algorithm/models/rs_algorithm/rs_private.rs b/src/modules/algorithm/models/rs/rs_private.rs similarity index 97% rename from src/modules/algorithm/models/rs_algorithm/rs_private.rs rename to src/modules/algorithm/models/rs/rs_private.rs index 62c347e..9896a47 100644 --- a/src/modules/algorithm/models/rs_algorithm/rs_private.rs +++ b/src/modules/algorithm/models/rs/rs_private.rs @@ -79,11 +79,12 @@ where D: Digest + AssociatedOid, #[cfg(all(test, feature = "pkcs1"))] mod tests { - use crate::algorithm::{JwAlgSign, JwAlgVerify, RS256Private}; + use crate::algorithm::{JwAlgSign, JwAlgVerify}; use base64::prelude::BASE64_URL_SAFE_NO_PAD; use base64::Engine; use pkcs1::DecodeRsaPrivateKey; pub use rsa::RsaPrivateKey; + use crate::algorithm::rs::RS256Private; #[test] fn rs256_algorithm_works_as_expected() { diff --git a/src/modules/algorithm/models/rs_algorithm/rs_public.rs b/src/modules/algorithm/models/rs/rs_public.rs similarity index 100% rename from src/modules/algorithm/models/rs_algorithm/rs_public.rs rename to src/modules/algorithm/models/rs/rs_public.rs diff --git a/src/modules/algorithm/models/rs_algorithm/mod.rs b/src/modules/algorithm/models/rs_algorithm/mod.rs deleted file mode 100644 index 01a4417..0000000 --- a/src/modules/algorithm/models/rs_algorithm/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod rs_public; -pub mod rs_private; \ No newline at end of file diff --git a/src/modules/key/mod.rs b/src/modules/key/mod.rs index 9f4ead1..83afd63 100644 --- a/src/modules/key/mod.rs +++ b/src/modules/key/mod.rs @@ -13,16 +13,15 @@ mod tests { use pkcs1::DecodeRsaPrivateKey; use rsa::RsaPrivateKey; use serde_json::json; - use crate::algorithm::{RS256Private}; use crate::modules::key::models::jwk::Jwk; #[test] fn jwk_is_created_correctly() { - let ec_key = Jwk::try_from(json!({ - "kty": "EC", - "crv": "P-256", - "x": "", - "y": "", - })); + // let ec_key = Jwk::try_from(json!({ + // "kty": "EC", + // "crv": "P-256", + // "x": "", + // "y": "", + // })); } } diff --git a/src/modules/key/models.rs b/src/modules/key/models.rs index ba1f7ab..8bcff28 100644 --- a/src/modules/key/models.rs +++ b/src/modules/key/models.rs @@ -6,5 +6,4 @@ pub mod jwk; pub mod jwt_use; pub mod jwt_key_op; pub mod jwt_set; -pub mod jwk_params; -pub mod jwk_qualified; \ No newline at end of file +pub mod jwk_params; \ No newline at end of file diff --git a/src/modules/key/models/jwk.rs b/src/modules/key/models/jwk.rs index e44452f..93702c9 100644 --- a/src/modules/key/models/jwk.rs +++ b/src/modules/key/models/jwk.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; +use crate::algorithm::JwAlgVerify; use crate::modules::key::models::jwk_params::JwkParams; use crate::modules::key::models::jwt_key_op::JwtKeyOp; use crate::modules::key::models::jwt_use::JwtUse; @@ -25,4 +26,8 @@ pub struct Jwk { pub params: JwkParams, } -impl +impl Jwk { + // pub fn public_alg() -> Option> { + // + // } +} diff --git a/src/modules/token/mod.rs b/src/modules/token/mod.rs index 2835525..a6d0cd7 100644 --- a/src/modules/token/mod.rs +++ b/src/modules/token/mod.rs @@ -8,7 +8,8 @@ pub use error::JwtError; #[cfg(test)] mod tests { - use crate::algorithm::{HS256Private, JwAlgVerify}; + use crate::algorithm::{JwAlgVerify}; + use crate::algorithm::hs::HS256Private; use crate::token::Jwt; #[test] From 1acf09f06c00e154fab0505a169ea27764535f58 Mon Sep 17 00:00:00 2001 From: rster2002 Date: Sat, 28 Mar 2026 12:14:58 +0100 Subject: [PATCH 16/17] =?UTF-8?q?=F0=9F=9A=A7=20Continue=20working=20on=20?= =?UTF-8?q?JWKs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/algorithm/models.rs | 3 +- src/modules/algorithm/models/any/any_error.rs | 8 +++++ .../algorithm/models/any/any_private.rs | 19 +++++++++++ .../algorithm/models/any/any_public.rs | 33 +++++++++++++++++++ src/modules/algorithm/models/any/mod.rs | 7 ++++ src/modules/algorithm/models/es/es256.rs | 8 ++--- src/modules/algorithm/models/es/es384.rs | 26 +++++++++++++++ src/modules/key/traits/jwk_public_params.rs | 2 +- 8 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 src/modules/algorithm/models/any/any_error.rs create mode 100644 src/modules/algorithm/models/any/any_private.rs create mode 100644 src/modules/algorithm/models/any/any_public.rs create mode 100644 src/modules/algorithm/models/any/mod.rs diff --git a/src/modules/algorithm/models.rs b/src/modules/algorithm/models.rs index 0de9f3f..23988db 100644 --- a/src/modules/algorithm/models.rs +++ b/src/modules/algorithm/models.rs @@ -7,4 +7,5 @@ pub mod hs; pub mod rs; #[cfg(any(feature = "es256", feature = "es384"))] -pub mod es; \ No newline at end of file +pub mod es; +pub mod any; \ No newline at end of file diff --git a/src/modules/algorithm/models/any/any_error.rs b/src/modules/algorithm/models/any/any_error.rs new file mode 100644 index 0000000..b498436 --- /dev/null +++ b/src/modules/algorithm/models/any/any_error.rs @@ -0,0 +1,8 @@ +use thiserror::Error; + +#[derive(Debug, Error)] +#[error(transparent)] +pub enum AnyError { + #[cfg(any(feature = "es256", feature = "es384"))] + Ecdsa(#[from] ecdsa::Error), +} \ No newline at end of file diff --git a/src/modules/algorithm/models/any/any_private.rs b/src/modules/algorithm/models/any/any_private.rs new file mode 100644 index 0000000..d2d82fb --- /dev/null +++ b/src/modules/algorithm/models/any/any_private.rs @@ -0,0 +1,19 @@ +pub enum AnyPrivate { + #[cfg(feature = "es256")] + ES256Private(crate::algorithm::es::ES256Private), + + #[cfg(feature = "es384")] + ES384Private(crate::algorithm::es::es384::ES384Private), + + #[cfg(feature = "hs256")] + HS256Private(crate::algorithm::hs::HS256Private), + + #[cfg(feature = "rs256")] + RS256Private(crate::algorithm::rs::RS256Private), + + #[cfg(feature = "rs384")] + RS384Private(crate::algorithm::rs::RS384Private), + + #[cfg(feature = "rs512")] + RS512Private(crate::algorithm::rs::RS512Private), +} \ No newline at end of file diff --git a/src/modules/algorithm/models/any/any_public.rs b/src/modules/algorithm/models/any/any_public.rs new file mode 100644 index 0000000..80e940d --- /dev/null +++ b/src/modules/algorithm/models/any/any_public.rs @@ -0,0 +1,33 @@ +use crate::algorithm::any::any_error::AnyError; +use crate::algorithm::JwAlgVerify; + +pub enum AnyPublic { + #[cfg(feature = "es256")] + ES256Public(crate::algorithm::es::ES256Public), + + #[cfg(feature = "es384")] + ES384Public(crate::algorithm::es::es384::ES384Public), + + #[cfg(feature = "rs256")] + RS256Public(crate::algorithm::rs::RS256Public), + + #[cfg(feature = "rs384")] + RS384Public(crate::algorithm::rs::RS384Public), + + #[cfg(feature = "rs512")] + RS512Public(crate::algorithm::rs::RS512Public), +} + +impl JwAlgVerify for AnyPublic { + type Error = AnyError; + + fn verify(&self, payload: &str, signature: &[u8]) -> Result { + Ok(match self { + AnyPublic::ES256Public(inner) => inner.verify(payload, signature)?, + AnyPublic::ES384Public(inner) => inner.verify(payload, signature)?, + AnyPublic::RS256Public(inner) => inner.verify(payload, signature)?, + AnyPublic::RS384Public(inner) => inner.verify(payload, signature)?, + AnyPublic::RS512Public(inner) => inner.verify(payload, signature)?, + }) + } +} diff --git a/src/modules/algorithm/models/any/mod.rs b/src/modules/algorithm/models/any/mod.rs new file mode 100644 index 0000000..cb1c785 --- /dev/null +++ b/src/modules/algorithm/models/any/mod.rs @@ -0,0 +1,7 @@ +mod any_private; +mod any_public; +mod any_error; + +pub use any_private::*; +pub use any_public::*; +pub use any_error::*; \ No newline at end of file diff --git a/src/modules/algorithm/models/es/es256.rs b/src/modules/algorithm/models/es/es256.rs index 9a9b529..cad1116 100644 --- a/src/modules/algorithm/models/es/es256.rs +++ b/src/modules/algorithm/models/es/es256.rs @@ -7,7 +7,7 @@ use crate::algorithm::es::{ESPublic, ESPublicParams}; use crate::algorithm::models::es::es_curve::ESCurve; use crate::algorithm::models::es::es_private::ESPrivate; use crate::algorithm::models::es::es_private_params::ESPrivateParams; -use crate::modules::key::JwkPrivateParams; +use crate::modules::key::{JwkPrivateParams, JwkPublicParams}; // Private pub type ES256Private = ESPrivate; @@ -60,10 +60,10 @@ impl PartialJwAlg for ES256Public { } } -impl JwkPrivateParams<'_> for ES256Public { - type PrivateParams = ESPublicParams; +impl JwkPublicParams<'_> for ES256Public { + type PublicParams = ESPublicParams; - fn get_private_params(&self) -> Option { + fn get_public_params(&self) -> Option { let affine_point = self.0.as_affine(); let encoded_point: EncodedPoint = affine_point.clone().into(); // TODO remove clone let x = encoded_point.x()?; diff --git a/src/modules/algorithm/models/es/es384.rs b/src/modules/algorithm/models/es/es384.rs index f3a17c1..fdf708c 100644 --- a/src/modules/algorithm/models/es/es384.rs +++ b/src/modules/algorithm/models/es/es384.rs @@ -1,6 +1,11 @@ +use base64::Engine; +use base64::prelude::BASE64_URL_SAFE; +use ecdsa::EncodedPoint; use crate::algorithm::{JwAlg, PartialJwAlg}; +use crate::algorithm::es::{ES256Private, ESCurve, ESPrivateParams, ESPublicParams}; use crate::algorithm::models::es::es_private::ESPrivate; use crate::algorithm::models::es::es_public::ESPublic; +use crate::modules::key::{JwkPrivateParams, JwkPublicParams}; // Private pub type ES384Private = ESPrivate; @@ -17,6 +22,27 @@ impl PartialJwAlg for ESPrivate { } } +impl JwkPrivateParams<'_> for ES384Private { + type PrivateParams = ESPrivateParams; + + fn get_private_params(&self) -> Option { + let verifying_key = self.0.verifying_key(); + let affine_point = verifying_key.as_affine(); + let encoded_point: EncodedPoint = affine_point.clone().into(); // TODO remove clone + let x = encoded_point.x()?; + let y = encoded_point.y()?; + + let d_bytes = self.0.as_nonzero_scalar().to_bytes(); + + Some(ESPrivateParams { + crv: ESCurve::P384, + x: BASE64_URL_SAFE.encode(&x), + y: BASE64_URL_SAFE.encode(&y), + d: BASE64_URL_SAFE.encode(&d_bytes), + }) + } +} + // Public pub type ES384Public = ESPublic; diff --git a/src/modules/key/traits/jwk_public_params.rs b/src/modules/key/traits/jwk_public_params.rs index ae6e485..06c8936 100644 --- a/src/modules/key/traits/jwk_public_params.rs +++ b/src/modules/key/traits/jwk_public_params.rs @@ -3,5 +3,5 @@ use serde::{Deserialize, Serialize}; pub trait JwkPublicParams<'a> { type PublicParams: Serialize + Deserialize<'a>; - fn get_public_params(&self) -> Self::PublicParams; + fn get_public_params(&self) -> Option; } \ No newline at end of file From 500c19ef7c1c66a5a75654e4a29ad5050744aeb1 Mon Sep 17 00:00:00 2001 From: rster2002 Date: Sat, 28 Mar 2026 12:56:53 +0100 Subject: [PATCH 17/17] =?UTF-8?q?=F0=9F=9A=A7=20Continue=20working=20on=20?= =?UTF-8?q?JWKs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/algorithm/models/es/es256.rs | 20 ++++++++----- src/modules/algorithm/models/es/es384.rs | 29 +++++++++++++++++-- src/modules/algorithm/models/es/es_curve.rs | 2 +- .../algorithm/models/es/es_private_params.rs | 2 +- .../algorithm/models/es/es_public_params.rs | 2 +- src/modules/algorithm/models/hs/hs256.rs | 2 +- src/modules/algorithm/models/rs/mod.rs | 2 ++ src/modules/algorithm/models/rs/rs256.rs | 22 +++++++++++++- src/modules/algorithm/models/rs/rs_private.rs | 2 +- .../algorithm/models/rs/rs_private_params.rs | 8 +++++ src/modules/algorithm/models/rs/rs_public.rs | 2 +- .../algorithm/models/rs/rs_public_params.rs | 7 +++++ 12 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 src/modules/algorithm/models/rs/rs_private_params.rs create mode 100644 src/modules/algorithm/models/rs/rs_public_params.rs diff --git a/src/modules/algorithm/models/es/es256.rs b/src/modules/algorithm/models/es/es256.rs index cad1116..3ec8721 100644 --- a/src/modules/algorithm/models/es/es256.rs +++ b/src/modules/algorithm/models/es/es256.rs @@ -1,5 +1,5 @@ use base64::Engine; -use base64::prelude::BASE64_URL_SAFE; +use base64::prelude::{BASE64_URL_SAFE, BASE64_URL_SAFE_NO_PAD}; use ecdsa::elliptic_curve::point::{AffineCoordinates, DecompressPoint}; use ecdsa::EncodedPoint; use crate::algorithm::{JwAlg, PartialJwAlg}; @@ -38,9 +38,9 @@ impl JwkPrivateParams<'_> for ES256Private { Some(ESPrivateParams { crv: ESCurve::P256, - x: BASE64_URL_SAFE.encode(&x), - y: BASE64_URL_SAFE.encode(&y), - d: BASE64_URL_SAFE.encode(&d_bytes), + x: BASE64_URL_SAFE_NO_PAD.encode(&x), + y: BASE64_URL_SAFE_NO_PAD.encode(&y), + d: BASE64_URL_SAFE_NO_PAD.encode(&d_bytes), }) } } @@ -71,8 +71,14 @@ impl JwkPublicParams<'_> for ES256Public { Some(ESPublicParams { crv: ESCurve::P256, - x: BASE64_URL_SAFE.encode(&x), - y: BASE64_URL_SAFE.encode(&y), + x: BASE64_URL_SAFE_NO_PAD.encode(&x), + y: BASE64_URL_SAFE_NO_PAD.encode(&y), }) } -} \ No newline at end of file +} + +impl From for ES256Public { + fn from(value: ES256Private) -> Self { + Self(value.0.into()) + } +} diff --git a/src/modules/algorithm/models/es/es384.rs b/src/modules/algorithm/models/es/es384.rs index fdf708c..ee8269e 100644 --- a/src/modules/algorithm/models/es/es384.rs +++ b/src/modules/algorithm/models/es/es384.rs @@ -1,8 +1,8 @@ use base64::Engine; -use base64::prelude::BASE64_URL_SAFE; +use base64::prelude::{BASE64_URL_SAFE, BASE64_URL_SAFE_NO_PAD}; use ecdsa::EncodedPoint; use crate::algorithm::{JwAlg, PartialJwAlg}; -use crate::algorithm::es::{ES256Private, ESCurve, ESPrivateParams, ESPublicParams}; +use crate::algorithm::es::{ES256Private, ES256Public, ESCurve, ESPrivateParams, ESPublicParams}; use crate::algorithm::models::es::es_private::ESPrivate; use crate::algorithm::models::es::es_public::ESPublic; use crate::modules::key::{JwkPrivateParams, JwkPublicParams}; @@ -56,4 +56,27 @@ impl PartialJwAlg for ES384Public { fn partial_alg() -> Option> { Some(Self::alg()) } -} \ No newline at end of file +} + +impl JwkPublicParams<'_> for ES384Public { + type PublicParams = ESPublicParams; + + fn get_public_params(&self) -> Option { + let affine_point = self.0.as_affine(); + let encoded_point: EncodedPoint = affine_point.clone().into(); // TODO remove clone + let x = encoded_point.x()?; + let y = encoded_point.y()?; + + Some(ESPublicParams { + crv: ESCurve::P384, + x: BASE64_URL_SAFE_NO_PAD.encode(&x), + y: BASE64_URL_SAFE_NO_PAD.encode(&y), + }) + } +} + +impl From for ES384Public { + fn from(value: ES384Private) -> Self { + Self(value.0.into()) + } +} diff --git a/src/modules/algorithm/models/es/es_curve.rs b/src/modules/algorithm/models/es/es_curve.rs index 4f8e729..dc809e9 100644 --- a/src/modules/algorithm/models/es/es_curve.rs +++ b/src/modules/algorithm/models/es/es_curve.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum ESCurve { #[serde(rename = "P-256")] P256, diff --git a/src/modules/algorithm/models/es/es_private_params.rs b/src/modules/algorithm/models/es/es_private_params.rs index ce7a26d..9d54b27 100644 --- a/src/modules/algorithm/models/es/es_private_params.rs +++ b/src/modules/algorithm/models/es/es_private_params.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::algorithm::es::ESCurve; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ESPrivateParams { pub crv: ESCurve, pub x: String, diff --git a/src/modules/algorithm/models/es/es_public_params.rs b/src/modules/algorithm/models/es/es_public_params.rs index fd542b0..75f00b5 100644 --- a/src/modules/algorithm/models/es/es_public_params.rs +++ b/src/modules/algorithm/models/es/es_public_params.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::algorithm::models::es::es_curve::ESCurve; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ESPublicParams { pub crv: ESCurve, pub x: String, diff --git a/src/modules/algorithm/models/hs/hs256.rs b/src/modules/algorithm/models/hs/hs256.rs index a993d9a..2d75008 100644 --- a/src/modules/algorithm/models/hs/hs256.rs +++ b/src/modules/algorithm/models/hs/hs256.rs @@ -14,4 +14,4 @@ impl PartialJwAlg for HS256Private { fn partial_alg() -> Option> { Some(Self::alg()) } -} \ No newline at end of file +} diff --git a/src/modules/algorithm/models/rs/mod.rs b/src/modules/algorithm/models/rs/mod.rs index 521af42..d86dba3 100644 --- a/src/modules/algorithm/models/rs/mod.rs +++ b/src/modules/algorithm/models/rs/mod.rs @@ -15,6 +15,8 @@ pub use rs384::*; #[cfg(feature = "rs512")] pub mod rs512; +pub mod rs_private_params; +pub mod rs_public_params; #[cfg(feature = "rs512")] pub use rs512::*; \ No newline at end of file diff --git a/src/modules/algorithm/models/rs/rs256.rs b/src/modules/algorithm/models/rs/rs256.rs index 53f2913..e204b00 100644 --- a/src/modules/algorithm/models/rs/rs256.rs +++ b/src/modules/algorithm/models/rs/rs256.rs @@ -1,6 +1,11 @@ +use base64::Engine; +use base64::prelude::BASE64_URL_SAFE_NO_PAD; +use rsa::traits::PublicKeyParts; use crate::algorithm::{JwAlg, PartialJwAlg}; use crate::algorithm::models::rs::rs_private::RSPrivate; use crate::algorithm::models::rs::rs_public::RSPublic; +use crate::algorithm::rs::rs_public_params::RSPublicParams; +use crate::modules::key::JwkPublicParams; // Private pub type RS256Private = RSPrivate; @@ -30,4 +35,19 @@ impl PartialJwAlg for RS256Public { fn partial_alg() -> Option> { Some(Self::alg()) } -} \ No newline at end of file +} + +impl JwkPublicParams<'_> for RS256Public { + type PublicParams = RSPublicParams; + + fn get_public_params(&self) -> Option { + let public_key = self.0.as_ref(); + let n = public_key.n().to_bytes_be(); + let e = public_key.e().to_bytes_be(); + + Some(RSPublicParams { + n: BASE64_URL_SAFE_NO_PAD.encode(&n), + e: BASE64_URL_SAFE_NO_PAD.encode(&e), + }) + } +} diff --git a/src/modules/algorithm/models/rs/rs_private.rs b/src/modules/algorithm/models/rs/rs_private.rs index 9896a47..32e4228 100644 --- a/src/modules/algorithm/models/rs/rs_private.rs +++ b/src/modules/algorithm/models/rs/rs_private.rs @@ -11,7 +11,7 @@ use pkcs1::{DecodeRsaPrivateKey, EncodeRsaPrivateKey}; use crate::algorithm::traits::partial_jw_alg::PartialJwAlg; #[derive(Clone)] -pub struct RSPrivate(SigningKey) +pub struct RSPrivate(pub(crate) SigningKey) where D: Digest + AssociatedOid; impl RSPrivate diff --git a/src/modules/algorithm/models/rs/rs_private_params.rs b/src/modules/algorithm/models/rs/rs_private_params.rs new file mode 100644 index 0000000..ee21b65 --- /dev/null +++ b/src/modules/algorithm/models/rs/rs_private_params.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RSPrivateParams { + pub n: String, + pub e: String, + pub d: String, +} \ No newline at end of file diff --git a/src/modules/algorithm/models/rs/rs_public.rs b/src/modules/algorithm/models/rs/rs_public.rs index 37feb3b..021378b 100644 --- a/src/modules/algorithm/models/rs/rs_public.rs +++ b/src/modules/algorithm/models/rs/rs_public.rs @@ -4,7 +4,7 @@ use rsa::signature::Verifier; use crate::algorithm::{JwAlgVerify}; #[derive(Clone)] -pub struct RSPublic(VerifyingKey) +pub struct RSPublic(pub(crate) VerifyingKey) where D : Digest; impl JwAlgVerify for RSPublic diff --git a/src/modules/algorithm/models/rs/rs_public_params.rs b/src/modules/algorithm/models/rs/rs_public_params.rs new file mode 100644 index 0000000..d78626d --- /dev/null +++ b/src/modules/algorithm/models/rs/rs_public_params.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RSPublicParams { + pub n: String, + pub e: String, +} \ No newline at end of file