1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-08-07 17:03:01 +03:00

JWK signer

This commit is contained in:
Quentin Gliech
2022-08-29 15:43:06 +02:00
parent dd51c4a51b
commit 945719a5a1
7 changed files with 600 additions and 91 deletions

View File

@@ -198,25 +198,25 @@ mod rsa_impls {
use super::RsaPrivateParameters;
impl TryInto<RsaPrivateKey> for RsaPrivateParameters {
impl TryFrom<RsaPrivateParameters> for RsaPrivateKey {
type Error = rsa::errors::Error;
fn try_into(self) -> Result<RsaPrivateKey, Self::Error> {
(&self).try_into()
fn try_from(value: RsaPrivateParameters) -> Result<Self, Self::Error> {
Self::try_from(&value)
}
}
impl TryInto<RsaPrivateKey> for &RsaPrivateParameters {
impl TryFrom<&RsaPrivateParameters> for RsaPrivateKey {
type Error = rsa::errors::Error;
#[allow(clippy::many_single_char_names)]
fn try_into(self) -> Result<RsaPrivateKey, Self::Error> {
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<Self, Self::Error> {
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<UrlSafe, Unpadded>")]
@@ -265,39 +265,68 @@ impl From<EcPrivateParameters> 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<C> TryInto<SecretKey<C>> for EcPrivateParameters
impl<C> TryFrom<EcPrivateParameters> for SigningKey<C>
where
C: Curve + ValidatePublicKey,
FieldSize<C>: ModulusSize,
C: PrimeCurve + ProjectiveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
type Error = elliptic_curve::Error;
fn try_into(self) -> Result<SecretKey<C>, Self::Error> {
(&self).try_into()
type Error = ecdsa::Error;
fn try_from(value: EcPrivateParameters) -> Result<Self, Self::Error> {
Self::try_from(&value)
}
}
impl<C> TryInto<SecretKey<C>> for &EcPrivateParameters
impl<C> TryFrom<&EcPrivateParameters> for SigningKey<C>
where
C: PrimeCurve + ProjectiveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
type Error = ecdsa::Error;
fn try_from(value: &EcPrivateParameters) -> Result<Self, Self::Error> {
SigningKey::from_bytes(&value.d)
}
}
impl<C> TryFrom<EcPrivateParameters> for SecretKey<C>
where
C: Curve + ValidatePublicKey,
FieldSize<C>: ModulusSize,
{
type Error = elliptic_curve::Error;
fn try_from(value: EcPrivateParameters) -> Result<Self, Self::Error> {
Self::try_from(&value)
}
}
impl<C> TryFrom<&EcPrivateParameters> for SecretKey<C>
where
C: Curve + ValidatePublicKey,
FieldSize<C>: ModulusSize,
{
type Error = elliptic_curve::Error;
fn try_into(self) -> Result<SecretKey<C>, Self::Error> {
let x = self
fn try_from(value: &EcPrivateParameters) -> Result<Self, Self::Error> {
let x = value
.x
.get(..FieldSize::<C>::USIZE)
.ok_or(elliptic_curve::Error)?;
let y = self
let y = value
.x
.get(..FieldSize::<C>::USIZE)
.ok_or(elliptic_curve::Error)?;
@@ -305,7 +334,7 @@ mod ec_impls {
let x = FieldBytes::<C>::from_slice(x);
let y = FieldBytes::<C>::from_slice(y);
let pubkey = EncodedPoint::<C>::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)
}

View File

@@ -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

View File

@@ -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,

View File

@@ -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<H> {
@@ -78,14 +78,69 @@ pub(crate) mod pkcs1v15 {
self.inner.verify(&digest, signature)
}
}
pub struct SigningKey<H> {
inner: rsa::pkcs1v15::SigningKey,
hash: PhantomData<H>,
}
impl From<RsaPrivateKey> for SigningKey<Sha256> {
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<RsaPrivateKey> for SigningKey<Sha384> {
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<RsaPrivateKey> for SigningKey<Sha512> {
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) -> T
where
T: signature::Signer<rsa::pkcs1v15::Signature>,
{
t
}
impl<H> signature::Signer<rsa::pkcs1v15::Signature> for SigningKey<H>
where
H: Digest,
{
fn try_sign(&self, msg: &[u8]) -> Result<rsa::pkcs1v15::Signature, signature::Error> {
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<H> {
inner: rsa::pss::VerifyingKey,
@@ -143,4 +198,57 @@ pub(crate) mod pss {
self.inner.verify(&digest, signature)
}
}
pub struct SigningKey<H> {
inner: rsa::pss::SigningKey,
hash: PhantomData<H>,
}
impl From<RsaPrivateKey> for SigningKey<Sha256> {
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<RsaPrivateKey> for SigningKey<Sha384> {
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<RsaPrivateKey> for SigningKey<Sha512> {
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) -> T
where
T: signature::Signer<rsa::pss::Signature>,
{
t
}
impl<H> signature::Signer<rsa::pss::Signature> for SigningKey<H>
where
H: Digest,
{
fn try_sign(&self, msg: &[u8]) -> Result<rsa::pss::Signature, signature::Error> {
let digest = H::digest(msg);
self.inner.try_sign_with_rng(thread_rng(), &digest)
}
}
}

268
crates/jose/src/signer.rs Normal file
View File

@@ -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<Sha256>,
},
Hs384 {
key: crate::hmac::Hmac<Sha384>,
},
Hs512 {
key: crate::hmac::Hmac<Sha512>,
},
Rs256 {
key: crate::rsa::pkcs1v15::SigningKey<Sha256>,
},
Rs384 {
key: crate::rsa::pkcs1v15::SigningKey<Sha384>,
},
Rs512 {
key: crate::rsa::pkcs1v15::SigningKey<Sha512>,
},
Ps256 {
key: crate::rsa::pss::SigningKey<Sha256>,
},
Ps384 {
key: crate::rsa::pss::SigningKey<Sha384>,
},
Ps512 {
key: crate::rsa::pss::SigningKey<Sha512>,
},
Es256 {
key: ecdsa::SigningKey<p256::NistP256>,
},
Es384 {
key: ecdsa::SigningKey<p384::NistP384>,
},
Es256K {
key: ecdsa::SigningKey<k256::Secp256k1>,
},
}
#[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<u8>,
alg: JsonWebSignatureAlg,
) -> Result<Self, SignerFromOctError> {
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<Self, SignerFromJwkError> {
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<u8>,
}
impl AsRef<[u8]> for GenericSignature {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
impl signature::Signature for GenericSignature {
fn from_bytes(bytes: &[u8]) -> Result<Self, signature::Error> {
Ok(Self {
bytes: bytes.to_vec(),
})
}
}
impl signature::Signer<GenericSignature> for Signer {
fn try_sign(&self, msg: &[u8]) -> Result<GenericSignature, signature::Error> {
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())
}
}
}
}

View File

@@ -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")]

View File

@@ -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<u8> {
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());
}