From 945719a5a1b916e751bd8cc4cbfc2b0c98203c6b Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Mon, 29 Aug 2022 15:43:06 +0200 Subject: [PATCH] JWK signer --- crates/jose/src/jwk/private_parameters.rs | 77 ++++-- crates/jose/src/keystore/static_keystore.rs | 5 +- crates/jose/src/lib.rs | 2 +- crates/jose/src/rsa.rs | 112 +++++++- crates/jose/src/signer.rs | 268 ++++++++++++++++++++ crates/jose/src/verifier.rs | 7 +- crates/jose/tests/jws.rs | 220 +++++++++++----- 7 files changed, 600 insertions(+), 91 deletions(-) create mode 100644 crates/jose/src/signer.rs diff --git a/crates/jose/src/jwk/private_parameters.rs b/crates/jose/src/jwk/private_parameters.rs index 6b9f1ea1..c6835419 100644 --- a/crates/jose/src/jwk/private_parameters.rs +++ b/crates/jose/src/jwk/private_parameters.rs @@ -198,25 +198,25 @@ mod rsa_impls { use super::RsaPrivateParameters; - impl TryInto for RsaPrivateParameters { + impl TryFrom for RsaPrivateKey { type Error = rsa::errors::Error; - fn try_into(self) -> Result { - (&self).try_into() + fn try_from(value: RsaPrivateParameters) -> Result { + Self::try_from(&value) } } - impl TryInto for &RsaPrivateParameters { + impl TryFrom<&RsaPrivateParameters> for RsaPrivateKey { type Error = rsa::errors::Error; #[allow(clippy::many_single_char_names)] - fn try_into(self) -> Result { - let n = BigUint::from_bytes_be(&self.n); - let e = BigUint::from_bytes_be(&self.e); - let d = BigUint::from_bytes_be(&self.d); + fn try_from(value: &RsaPrivateParameters) -> Result { + let n = BigUint::from_bytes_be(&value.n); + let e = BigUint::from_bytes_be(&value.e); + let d = BigUint::from_bytes_be(&value.d); - let primes = [&self.p, &self.q] + let primes = [&value.p, &value.q] .into_iter() - .chain(self.oth.iter().flatten().map(|o| &o.r)) + .chain(value.oth.iter().flatten().map(|o| &o.r)) .map(|i| BigUint::from_bytes_be(i)) .collect(); @@ -228,7 +228,7 @@ mod rsa_impls { #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] pub struct EcPrivateParameters { - crv: JsonWebKeyEcEllipticCurve, + pub(crate) crv: JsonWebKeyEcEllipticCurve, #[schemars(with = "String")] #[serde_as(as = "Base64")] @@ -265,39 +265,68 @@ impl From for super::public_parameters::EcPublicParameters } mod ec_impls { + use crypto_mac::generic_array::ArrayLength; use digest::typenum::Unsigned; - use ecdsa::EncodedPoint; + use ecdsa::{hazmat::SignPrimitive, EncodedPoint, PrimeCurve, SignatureSize, SigningKey}; use elliptic_curve::{ + ops::{Invert, Reduce}, sec1::{Coordinates, FromEncodedPoint, ModulusSize, ToEncodedPoint, ValidatePublicKey}, - AffinePoint, Curve, FieldBytes, FieldSize, SecretKey, + subtle::CtOption, + AffinePoint, Curve, FieldBytes, FieldSize, ProjectiveArithmetic, Scalar, SecretKey, }; use super::{super::JwkEcCurve, EcPrivateParameters}; - impl TryInto> for EcPrivateParameters + impl TryFrom for SigningKey where - C: Curve + ValidatePublicKey, - FieldSize: ModulusSize, + C: PrimeCurve + ProjectiveArithmetic, + Scalar: Invert>> + Reduce + SignPrimitive, + SignatureSize: ArrayLength, { - type Error = elliptic_curve::Error; - fn try_into(self) -> Result, Self::Error> { - (&self).try_into() + type Error = ecdsa::Error; + + fn try_from(value: EcPrivateParameters) -> Result { + Self::try_from(&value) } } - impl TryInto> for &EcPrivateParameters + impl TryFrom<&EcPrivateParameters> for SigningKey + where + C: PrimeCurve + ProjectiveArithmetic, + Scalar: Invert>> + Reduce + SignPrimitive, + SignatureSize: ArrayLength, + { + type Error = ecdsa::Error; + + fn try_from(value: &EcPrivateParameters) -> Result { + SigningKey::from_bytes(&value.d) + } + } + + impl TryFrom for SecretKey + where + C: Curve + ValidatePublicKey, + FieldSize: ModulusSize, + { + type Error = elliptic_curve::Error; + fn try_from(value: EcPrivateParameters) -> Result { + Self::try_from(&value) + } + } + + impl TryFrom<&EcPrivateParameters> for SecretKey where C: Curve + ValidatePublicKey, FieldSize: ModulusSize, { type Error = elliptic_curve::Error; - fn try_into(self) -> Result, Self::Error> { - let x = self + fn try_from(value: &EcPrivateParameters) -> Result { + let x = value .x .get(..FieldSize::::USIZE) .ok_or(elliptic_curve::Error)?; - let y = self + let y = value .x .get(..FieldSize::::USIZE) .ok_or(elliptic_curve::Error)?; @@ -305,7 +334,7 @@ mod ec_impls { let x = FieldBytes::::from_slice(x); let y = FieldBytes::::from_slice(y); let pubkey = EncodedPoint::::from_affine_coordinates(x, y, false); - let privkey = SecretKey::from_be_bytes(&self.d)?; + let privkey = SecretKey::from_be_bytes(&value.d)?; C::validate_public_key(&privkey, &pubkey)?; Ok(privkey) } diff --git a/crates/jose/src/keystore/static_keystore.rs b/crates/jose/src/keystore/static_keystore.rs index 7b432703..09697f63 100644 --- a/crates/jose/src/keystore/static_keystore.rs +++ b/crates/jose/src/keystore/static_keystore.rs @@ -34,7 +34,10 @@ use signature::{Signature, Signer, Verifier}; use tower::Service; use super::{SigningKeystore, VerifyingKeystore}; -use crate::{jwk::PublicJsonWebKeySet, JsonWebKey, JsonWebKeySet, JsonWebSignatureHeader}; +use crate::{ + jwk::{JsonWebKey, JsonWebKeySet, PublicJsonWebKeySet}, + JsonWebSignatureHeader, +}; // Generate with // openssl genrsa 2048 diff --git a/crates/jose/src/lib.rs b/crates/jose/src/lib.rs index 8f05b4fa..0d68f5ef 100644 --- a/crates/jose/src/lib.rs +++ b/crates/jose/src/lib.rs @@ -24,12 +24,12 @@ pub mod jwk; pub(crate) mod jwt; mod keystore; pub(crate) mod rsa; +pub mod signer; pub mod verifier; pub use futures_util::future::Either; pub use self::{ - jwk::{JsonWebKey, JsonWebKeySet}, jwt::{DecodedJsonWebToken, JsonWebSignatureHeader, JsonWebTokenParts, Jwt, JwtSignatureError}, keystore::{ DynamicJwksStore, SharedSecret, SigningKeystore, StaticJwksStore, StaticKeystore, diff --git a/crates/jose/src/rsa.rs b/crates/jose/src/rsa.rs index 3998a92f..2d9bdd0a 100644 --- a/crates/jose/src/rsa.rs +++ b/crates/jose/src/rsa.rs @@ -19,7 +19,7 @@ pub(crate) mod pkcs1v15 { use std::marker::PhantomData; use digest::Digest; - use rsa::RsaPublicKey; + use rsa::{RsaPrivateKey, RsaPublicKey}; use sha2::{Sha256, Sha384, Sha512}; pub struct VerifyingKey { @@ -78,14 +78,69 @@ pub(crate) mod pkcs1v15 { self.inner.verify(&digest, signature) } } + + pub struct SigningKey { + inner: rsa::pkcs1v15::SigningKey, + hash: PhantomData, + } + + impl From for SigningKey { + fn from(key: RsaPrivateKey) -> Self { + let inner = rsa::pkcs1v15::SigningKey::new_with_hash(key, rsa::Hash::SHA2_256); + ensure_signer(Self { + inner, + hash: PhantomData::default(), + }) + } + } + + impl From for SigningKey { + fn from(key: RsaPrivateKey) -> Self { + let inner = rsa::pkcs1v15::SigningKey::new_with_hash(key, rsa::Hash::SHA2_384); + ensure_signer(Self { + inner, + hash: PhantomData::default(), + }) + } + } + + impl From for SigningKey { + fn from(key: RsaPrivateKey) -> Self { + let inner = rsa::pkcs1v15::SigningKey::new_with_hash(key, rsa::Hash::SHA2_512); + ensure_signer(Self { + inner, + hash: PhantomData::default(), + }) + } + } + + #[inline] + fn ensure_signer(t: T) -> T + where + T: signature::Signer, + { + t + } + + impl signature::Signer for SigningKey + where + H: Digest, + { + fn try_sign(&self, msg: &[u8]) -> Result { + let digest = H::digest(msg); + self.inner.try_sign(&digest) + } + } } pub(crate) mod pss { use std::marker::PhantomData; use digest::Digest; - use rsa::RsaPublicKey; + use rand::thread_rng; + use rsa::{RsaPrivateKey, RsaPublicKey}; use sha2::{Sha256, Sha384, Sha512}; + use signature::RandomizedSigner; pub struct VerifyingKey { inner: rsa::pss::VerifyingKey, @@ -143,4 +198,57 @@ pub(crate) mod pss { self.inner.verify(&digest, signature) } } + + pub struct SigningKey { + inner: rsa::pss::SigningKey, + hash: PhantomData, + } + + impl From for SigningKey { + fn from(key: RsaPrivateKey) -> Self { + let inner = rsa::pss::SigningKey::new(key, Box::new(Sha256::new())); + ensure_signer(Self { + inner, + hash: PhantomData::default(), + }) + } + } + + impl From for SigningKey { + fn from(key: RsaPrivateKey) -> Self { + let inner = rsa::pss::SigningKey::new(key, Box::new(Sha384::new())); + ensure_signer(Self { + inner, + hash: PhantomData::default(), + }) + } + } + + impl From for SigningKey { + fn from(key: RsaPrivateKey) -> Self { + let inner = rsa::pss::SigningKey::new(key, Box::new(Sha512::new())); + ensure_signer(Self { + inner, + hash: PhantomData::default(), + }) + } + } + + #[inline] + fn ensure_signer(t: T) -> T + where + T: signature::Signer, + { + t + } + + impl signature::Signer for SigningKey + where + H: Digest, + { + fn try_sign(&self, msg: &[u8]) -> Result { + let digest = H::digest(msg); + self.inner.try_sign_with_rng(thread_rng(), &digest) + } + } } diff --git a/crates/jose/src/signer.rs b/crates/jose/src/signer.rs new file mode 100644 index 00000000..0a65556e --- /dev/null +++ b/crates/jose/src/signer.rs @@ -0,0 +1,268 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use mas_iana::jose::{JsonWebKeyEcEllipticCurve, JsonWebSignatureAlg}; +use sha2::{Sha256, Sha384, Sha512}; +use signature::Signature; +use thiserror::Error; + +use crate::jwk::private_parameters::{EcPrivateParameters, JsonWebKeyPrivateParameters}; + +pub enum Signer { + Hs256 { + key: crate::hmac::Hmac, + }, + Hs384 { + key: crate::hmac::Hmac, + }, + Hs512 { + key: crate::hmac::Hmac, + }, + Rs256 { + key: crate::rsa::pkcs1v15::SigningKey, + }, + Rs384 { + key: crate::rsa::pkcs1v15::SigningKey, + }, + Rs512 { + key: crate::rsa::pkcs1v15::SigningKey, + }, + Ps256 { + key: crate::rsa::pss::SigningKey, + }, + Ps384 { + key: crate::rsa::pss::SigningKey, + }, + Ps512 { + key: crate::rsa::pss::SigningKey, + }, + Es256 { + key: ecdsa::SigningKey, + }, + Es384 { + key: ecdsa::SigningKey, + }, + Es256K { + key: ecdsa::SigningKey, + }, +} + +#[derive(Debug, Error)] +pub enum SignerFromJwkError { + #[error("invalid RSA key")] + InvalidRsaKey { + #[from] + inner: rsa::errors::Error, + }, + + #[error("invalid elliptic curve key")] + InvalidEcKey { + #[from] + inner: ecdsa::Error, + }, + + #[error("algorithm {algorithm} is not supported")] + UnsupportedAlgorithm { algorithm: JsonWebSignatureAlg }, + + #[error("key is not suitable for algorithm {algorithm}")] + KeyNotSuitable { algorithm: JsonWebSignatureAlg }, +} + +#[derive(Debug, Error)] +pub enum SignerFromOctError { + #[error("algorithm {algorithm} is not supported")] + UnsupportedAlgorithm { algorithm: JsonWebSignatureAlg }, +} + +impl Signer { + pub fn for_oct_and_alg( + key: Vec, + alg: JsonWebSignatureAlg, + ) -> Result { + match alg { + JsonWebSignatureAlg::Hs256 => Ok(Self::Hs256 { key: key.into() }), + JsonWebSignatureAlg::Hs384 => Ok(Self::Hs384 { key: key.into() }), + JsonWebSignatureAlg::Hs512 => Ok(Self::Hs512 { key: key.into() }), + algorithm => Err(SignerFromOctError::UnsupportedAlgorithm { algorithm }), + } + } + + pub fn for_jwk_and_alg( + key: &JsonWebKeyPrivateParameters, + alg: JsonWebSignatureAlg, + ) -> Result { + match (key, alg) { + (JsonWebKeyPrivateParameters::Rsa(params), JsonWebSignatureAlg::Rs256) => { + let key: rsa::RsaPrivateKey = params.try_into()?; + Ok(Self::Rs256 { key: key.into() }) + } + + (JsonWebKeyPrivateParameters::Rsa(params), JsonWebSignatureAlg::Rs384) => { + let key: rsa::RsaPrivateKey = params.try_into()?; + Ok(Self::Rs384 { key: key.into() }) + } + + (JsonWebKeyPrivateParameters::Rsa(params), JsonWebSignatureAlg::Rs512) => { + let key: rsa::RsaPrivateKey = params.try_into()?; + Ok(Self::Rs512 { key: key.into() }) + } + + (JsonWebKeyPrivateParameters::Rsa(params), JsonWebSignatureAlg::Ps256) => { + let key: rsa::RsaPrivateKey = params.try_into()?; + Ok(Self::Ps256 { key: key.into() }) + } + + (JsonWebKeyPrivateParameters::Rsa(params), JsonWebSignatureAlg::Ps384) => { + let key: rsa::RsaPrivateKey = params.try_into()?; + Ok(Self::Ps384 { key: key.into() }) + } + + (JsonWebKeyPrivateParameters::Rsa(params), JsonWebSignatureAlg::Ps512) => { + let key: rsa::RsaPrivateKey = params.try_into()?; + Ok(Self::Ps512 { key: key.into() }) + } + + ( + JsonWebKeyPrivateParameters::Ec( + params @ EcPrivateParameters { + crv: JsonWebKeyEcEllipticCurve::P256, + .. + }, + ), + JsonWebSignatureAlg::Es256, + ) => { + let key = ecdsa::SigningKey::try_from(params)?; + Ok(Self::Es256 { key }) + } + + ( + JsonWebKeyPrivateParameters::Ec( + params @ EcPrivateParameters { + crv: JsonWebKeyEcEllipticCurve::P384, + .. + }, + ), + JsonWebSignatureAlg::Es384, + ) => { + let key = ecdsa::SigningKey::try_from(params)?; + Ok(Self::Es384 { key }) + } + + ( + JsonWebKeyPrivateParameters::Ec(EcPrivateParameters { + crv: JsonWebKeyEcEllipticCurve::P521, + .. + }), + JsonWebSignatureAlg::Es512, + ) => Err(SignerFromJwkError::UnsupportedAlgorithm { + algorithm: JsonWebSignatureAlg::Es512, + }), + + ( + JsonWebKeyPrivateParameters::Ec( + params @ EcPrivateParameters { + crv: JsonWebKeyEcEllipticCurve::Secp256K1, + .. + }, + ), + JsonWebSignatureAlg::Es256K, + ) => { + let key = ecdsa::SigningKey::try_from(params)?; + Ok(Self::Es256K { key }) + } + + (JsonWebKeyPrivateParameters::Okp(_params), JsonWebSignatureAlg::EdDsa) => { + Err(SignerFromJwkError::UnsupportedAlgorithm { + algorithm: JsonWebSignatureAlg::EdDsa, + }) + } + + (_, algorithm) => Err(SignerFromJwkError::KeyNotSuitable { algorithm }), + } + } +} + +#[derive(Debug)] +pub struct GenericSignature { + bytes: Vec, +} + +impl AsRef<[u8]> for GenericSignature { + fn as_ref(&self) -> &[u8] { + &self.bytes + } +} + +impl signature::Signature for GenericSignature { + fn from_bytes(bytes: &[u8]) -> Result { + Ok(Self { + bytes: bytes.to_vec(), + }) + } +} + +impl signature::Signer for Signer { + fn try_sign(&self, msg: &[u8]) -> Result { + match self { + Signer::Hs256 { key } => { + let signature = key.try_sign(msg)?; + GenericSignature::from_bytes(signature.as_bytes()) + } + Signer::Hs384 { key } => { + let signature = key.try_sign(msg)?; + GenericSignature::from_bytes(signature.as_bytes()) + } + Signer::Hs512 { key } => { + let signature = key.try_sign(msg)?; + GenericSignature::from_bytes(signature.as_bytes()) + } + Signer::Rs256 { key } => { + let signature = key.try_sign(msg)?; + GenericSignature::from_bytes(signature.as_bytes()) + } + Signer::Rs384 { key } => { + let signature = key.try_sign(msg)?; + GenericSignature::from_bytes(signature.as_bytes()) + } + Signer::Rs512 { key } => { + let signature = key.try_sign(msg)?; + GenericSignature::from_bytes(signature.as_bytes()) + } + Signer::Ps256 { key } => { + let signature = key.try_sign(msg)?; + GenericSignature::from_bytes(signature.as_bytes()) + } + Signer::Ps384 { key } => { + let signature = key.try_sign(msg)?; + GenericSignature::from_bytes(signature.as_bytes()) + } + Signer::Ps512 { key } => { + let signature = key.try_sign(msg)?; + GenericSignature::from_bytes(signature.as_bytes()) + } + Signer::Es256 { key } => { + let signature = key.try_sign(msg)?; + GenericSignature::from_bytes(signature.as_bytes()) + } + Signer::Es384 { key } => { + let signature = key.try_sign(msg)?; + GenericSignature::from_bytes(signature.as_bytes()) + } + Signer::Es256K { key } => { + let signature = key.try_sign(msg)?; + GenericSignature::from_bytes(signature.as_bytes()) + } + } + } +} diff --git a/crates/jose/src/verifier.rs b/crates/jose/src/verifier.rs index 556dde45..2cc8b99b 100644 --- a/crates/jose/src/verifier.rs +++ b/crates/jose/src/verifier.rs @@ -72,18 +72,13 @@ pub enum VerifierFromJwkError { inner: ecdsa::Error, }, - #[error("invalid curve parameter X")] - InvalidCurveParameterX, - - #[error("invalid curve parameter Y")] - InvalidCurveParameterY, - #[error("algorithm {algorithm} is not supported")] UnsupportedAlgorithm { algorithm: JsonWebSignatureAlg }, #[error("key is not suitable for algorithm {algorithm}")] KeyNotSuitable { algorithm: JsonWebSignatureAlg }, } + #[derive(Debug, Error)] pub enum VerifierFromOctError { #[error("algorithm {algorithm} is not supported")] diff --git a/crates/jose/tests/jws.rs b/crates/jose/tests/jws.rs index 82078e41..d82249a8 100644 --- a/crates/jose/tests/jws.rs +++ b/crates/jose/tests/jws.rs @@ -12,15 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::ops::Deref; - -use mas_jose::{ - constraints::ConstraintSet, - jwk::{PrivateJsonWebKeySet, PublicJsonWebKeySet}, - Jwt, -}; -use serde::Deserialize; - static HS256_JWT: &str = include_str!("./jwts/hs256.jwt"); static HS384_JWT: &str = include_str!("./jwts/hs384.jwt"); static HS512_JWT: &str = include_str!("./jwts/hs512.jwt"); @@ -38,11 +29,11 @@ static EDDSA_ED25519_JWT: &str = include_str!("./jwts/eddsa-ed25519.jwt"); static EDDSA_ED448_JWT: &str = include_str!("./jwts/eddsa-ed448.jwt"); static OCT_KEY: &[u8] = include_bytes!("./keys/oct.bin"); -fn public_jwks() -> PublicJsonWebKeySet { +fn public_jwks() -> mas_jose::jwk::PublicJsonWebKeySet { serde_json::from_str(include_str!("./keys/jwks.pub.json")).unwrap() } -fn private_jwks() -> PrivateJsonWebKeySet { +fn private_jwks() -> mas_jose::jwk::PrivateJsonWebKeySet { serde_json::from_str(include_str!("./keys/jwks.priv.json")).unwrap() } @@ -50,74 +41,189 @@ fn oct_key() -> Vec { OCT_KEY.to_vec() } -#[derive(Deserialize)] +#[derive(serde::Deserialize, serde::Serialize)] struct Payload { hello: String, } -macro_rules! asymetric_jwt_test { - ($test_name:ident, $jwt:ident) => { - asymetric_jwt_test!($test_name, $jwt, verify = true); +macro_rules! conditional { + { true => $($tt:tt)* } => { + $($tt)* }; - ($test_name:ident, $jwt:ident, verify = $verify:ident) => { - #[test] - fn $test_name() { - let jwks = public_jwks(); - let jwt: Jwt<'_, Payload> = Jwt::try_from($jwt).unwrap(); - assert_eq!(jwt.payload().hello, "world"); + { false => $($tt:tt)* } => {}; +} - let constraints = ConstraintSet::from(jwt.header()); - let candidates = constraints.filter(jwks.deref()); - assert_eq!(candidates.len(), 1); - let candidate = candidates[0]; +macro_rules! asymetric_jwt_test { + ($test_name:ident, $alg:ident, $jwt:ident) => { + asymetric_jwt_test!($test_name, $alg, $jwt, supported = true); + }; + ($test_name:ident, $alg:ident, $jwt:ident, supported = $supported:ident) => { + mod $test_name { + use std::ops::Deref; - if $verify { - let verifier = mas_jose::verifier::Verifier::for_jwk_and_alg( - candidate.params(), - jwt.header().alg(), - ) - .unwrap(); - jwt.verify(&verifier).unwrap(); + use mas_iana::jose::JsonWebSignatureAlg; + use mas_jose::{constraints::ConstraintSet, Jwt}; + + use super::*; + + #[test] + fn validate_jwt() { + let jwt: Jwt<'_, Payload> = Jwt::try_from($jwt).unwrap(); + assert_eq!(jwt.payload().hello, "world"); + assert_eq!(jwt.header().alg(), JsonWebSignatureAlg::$alg); + } + + #[test] + fn find_public_key() { + let jwks = public_jwks(); + let jwt: Jwt<'_, Payload> = Jwt::try_from($jwt).unwrap(); + + let constraints = ConstraintSet::from(jwt.header()); + let candidates = constraints.filter(jwks.deref()); + assert_eq!(candidates.len(), 1); + } + + #[test] + fn find_private_key() { + let jwks = private_jwks(); + let jwt: Jwt<'_, Payload> = Jwt::try_from($jwt).unwrap(); + + let constraints = ConstraintSet::from(jwt.header()); + let candidates = constraints.filter(jwks.deref()); + assert_eq!(candidates.len(), 1); + } + + conditional! { $supported => + use mas_iana::jose::JsonWebKeyUse; + use mas_jose::{constraints::Constraint, JsonWebSignatureHeader}; + + #[test] + fn verify_jwt() { + let jwks = public_jwks(); + let jwt: Jwt<'_, Payload> = Jwt::try_from($jwt).unwrap(); + + let key = ConstraintSet::from(jwt.header()) + .filter(jwks.deref())[0]; + + let verifier = mas_jose::verifier::Verifier::for_jwk_and_alg( + key.params(), + JsonWebSignatureAlg::$alg + ) + .unwrap(); + + jwt.verify(&verifier).unwrap(); + } + + #[test] + fn sign_and_verify_jwt() { + let payload = Payload { hello: "world".to_string() }; + let header = JsonWebSignatureHeader::new(JsonWebSignatureAlg::$alg); + + let jwks = private_jwks(); + let key = ConstraintSet::new(vec![ + Constraint::alg(JsonWebSignatureAlg::$alg), + Constraint::use_(JsonWebKeyUse::Sig), + ]) + .filter(jwks.deref())[0]; + + let signer = mas_jose::signer::Signer::for_jwk_and_alg( + key.params(), + JsonWebSignatureAlg::$alg, + ) + .unwrap(); + + let jwks = public_jwks(); + let jwt: Jwt<'_, Payload> = Jwt::sign(header, payload, &signer).unwrap(); + let jwt: Jwt<'_, Payload> = Jwt::try_from(jwt.as_str()).unwrap(); + + let key = ConstraintSet::from(jwt.header()) + .filter(jwks.deref())[0]; + + let verifier = mas_jose::verifier::Verifier::for_jwk_and_alg( + key.params(), + JsonWebSignatureAlg::$alg + ) + .unwrap(); + + jwt.verify(&verifier).unwrap(); + } } } }; } macro_rules! symetric_jwt_test { - ($test_name:ident, $jwt:ident) => { - #[test] - fn $test_name() { - let jwt: Jwt<'_, Payload> = Jwt::try_from($jwt).unwrap(); - let verifier = - mas_jose::verifier::Verifier::for_oct_and_alg(oct_key(), jwt.header().alg()) - .unwrap(); - assert_eq!(jwt.payload().hello, "world"); - jwt.verify(&verifier).unwrap(); + ($test_name:ident, $alg:ident, $jwt:ident) => { + mod $test_name { + use mas_iana::jose::JsonWebSignatureAlg; + use mas_jose::{JsonWebSignatureHeader, Jwt}; + + use super::*; + + #[test] + fn validate_jwt() { + let jwt: Jwt<'_, Payload> = Jwt::try_from($jwt).unwrap(); + assert_eq!(jwt.payload().hello, "world"); + assert_eq!(jwt.header().alg(), JsonWebSignatureAlg::$alg); + } + + #[test] + fn verify_jwt() { + let jwt: Jwt<'_, Payload> = Jwt::try_from($jwt).unwrap(); + let verifier = + mas_jose::verifier::Verifier::for_oct_and_alg(oct_key(), jwt.header().alg()) + .unwrap(); + assert_eq!(jwt.payload().hello, "world"); + jwt.verify(&verifier).unwrap(); + } + + #[test] + fn sign_and_verify_jwt() { + let payload = Payload { + hello: "world".to_string(), + }; + let header = JsonWebSignatureHeader::new(JsonWebSignatureAlg::$alg); + + let signer = + mas_jose::signer::Signer::for_oct_and_alg(oct_key(), JsonWebSignatureAlg::$alg) + .unwrap(); + + let jwt: Jwt<'_, Payload> = Jwt::sign(header, payload, &signer).unwrap(); + let jwt: Jwt<'_, Payload> = Jwt::try_from(jwt.as_str()).unwrap(); + + let verifier = mas_jose::verifier::Verifier::for_oct_and_alg( + oct_key(), + JsonWebSignatureAlg::$alg, + ) + .unwrap(); + + jwt.verify(&verifier).unwrap(); + } } }; } -symetric_jwt_test!(test_hs256, HS256_JWT); -symetric_jwt_test!(test_hs384, HS384_JWT); -symetric_jwt_test!(test_hs512, HS512_JWT); +symetric_jwt_test!(hs256, Hs256, HS256_JWT); +symetric_jwt_test!(hs384, Hs384, HS384_JWT); +symetric_jwt_test!(hs512, Hs512, HS512_JWT); -asymetric_jwt_test!(test_rs256, RS256_JWT); -asymetric_jwt_test!(test_rs384, RS384_JWT); -asymetric_jwt_test!(test_rs512, RS512_JWT); -asymetric_jwt_test!(test_ps256, PS256_JWT); -asymetric_jwt_test!(test_ps384, PS384_JWT); -asymetric_jwt_test!(test_ps512, PS512_JWT); -asymetric_jwt_test!(test_es256, ES256_JWT); -asymetric_jwt_test!(test_es384, ES384_JWT); -asymetric_jwt_test!(test_es512, ES512_JWT, verify = false); -asymetric_jwt_test!(test_es256k, ES256K_JWT); -asymetric_jwt_test!(test_eddsa_ed25519, EDDSA_ED25519_JWT, verify = false); -asymetric_jwt_test!(test_eddsa_ed448, EDDSA_ED448_JWT, verify = false); +asymetric_jwt_test!(rs256, Rs256, RS256_JWT); +asymetric_jwt_test!(rs384, Rs384, RS384_JWT); +asymetric_jwt_test!(rs512, Rs512, RS512_JWT); +asymetric_jwt_test!(ps256, Ps256, PS256_JWT); +asymetric_jwt_test!(ps384, Ps384, PS384_JWT); +asymetric_jwt_test!(ps512, Ps512, PS512_JWT); +asymetric_jwt_test!(es256, Es256, ES256_JWT); +asymetric_jwt_test!(es384, Es384, ES384_JWT); +asymetric_jwt_test!(es512, Es512, ES512_JWT, supported = false); +asymetric_jwt_test!(es256k, Es256K, ES256K_JWT); +asymetric_jwt_test!(eddsa_ed25519, EdDsa, EDDSA_ED25519_JWT, supported = false); +asymetric_jwt_test!(eddsa_ed448, EdDsa, EDDSA_ED448_JWT, supported = false); #[test] fn test_private_to_public_jwks() { let priv_jwks = private_jwks(); - let pub_jwks = PublicJsonWebKeySet::from(priv_jwks); + let pub_jwks = mas_jose::jwk::PublicJsonWebKeySet::from(priv_jwks); assert_eq!(pub_jwks, public_jwks()); }