You've already forked authentication-service
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:
@@ -198,25 +198,25 @@ mod rsa_impls {
|
|||||||
|
|
||||||
use super::RsaPrivateParameters;
|
use super::RsaPrivateParameters;
|
||||||
|
|
||||||
impl TryInto<RsaPrivateKey> for RsaPrivateParameters {
|
impl TryFrom<RsaPrivateParameters> for RsaPrivateKey {
|
||||||
type Error = rsa::errors::Error;
|
type Error = rsa::errors::Error;
|
||||||
fn try_into(self) -> Result<RsaPrivateKey, Self::Error> {
|
fn try_from(value: RsaPrivateParameters) -> Result<Self, Self::Error> {
|
||||||
(&self).try_into()
|
Self::try_from(&value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryInto<RsaPrivateKey> for &RsaPrivateParameters {
|
impl TryFrom<&RsaPrivateParameters> for RsaPrivateKey {
|
||||||
type Error = rsa::errors::Error;
|
type Error = rsa::errors::Error;
|
||||||
|
|
||||||
#[allow(clippy::many_single_char_names)]
|
#[allow(clippy::many_single_char_names)]
|
||||||
fn try_into(self) -> Result<RsaPrivateKey, Self::Error> {
|
fn try_from(value: &RsaPrivateParameters) -> Result<Self, Self::Error> {
|
||||||
let n = BigUint::from_bytes_be(&self.n);
|
let n = BigUint::from_bytes_be(&value.n);
|
||||||
let e = BigUint::from_bytes_be(&self.e);
|
let e = BigUint::from_bytes_be(&value.e);
|
||||||
let d = BigUint::from_bytes_be(&self.d);
|
let d = BigUint::from_bytes_be(&value.d);
|
||||||
|
|
||||||
let primes = [&self.p, &self.q]
|
let primes = [&value.p, &value.q]
|
||||||
.into_iter()
|
.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))
|
.map(|i| BigUint::from_bytes_be(i))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -228,7 +228,7 @@ mod rsa_impls {
|
|||||||
#[serde_as]
|
#[serde_as]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct EcPrivateParameters {
|
pub struct EcPrivateParameters {
|
||||||
crv: JsonWebKeyEcEllipticCurve,
|
pub(crate) crv: JsonWebKeyEcEllipticCurve,
|
||||||
|
|
||||||
#[schemars(with = "String")]
|
#[schemars(with = "String")]
|
||||||
#[serde_as(as = "Base64<UrlSafe, Unpadded>")]
|
#[serde_as(as = "Base64<UrlSafe, Unpadded>")]
|
||||||
@@ -265,39 +265,68 @@ impl From<EcPrivateParameters> for super::public_parameters::EcPublicParameters
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod ec_impls {
|
mod ec_impls {
|
||||||
|
use crypto_mac::generic_array::ArrayLength;
|
||||||
use digest::typenum::Unsigned;
|
use digest::typenum::Unsigned;
|
||||||
use ecdsa::EncodedPoint;
|
use ecdsa::{hazmat::SignPrimitive, EncodedPoint, PrimeCurve, SignatureSize, SigningKey};
|
||||||
use elliptic_curve::{
|
use elliptic_curve::{
|
||||||
|
ops::{Invert, Reduce},
|
||||||
sec1::{Coordinates, FromEncodedPoint, ModulusSize, ToEncodedPoint, ValidatePublicKey},
|
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};
|
use super::{super::JwkEcCurve, EcPrivateParameters};
|
||||||
|
|
||||||
impl<C> TryInto<SecretKey<C>> for EcPrivateParameters
|
impl<C> TryFrom<EcPrivateParameters> for SigningKey<C>
|
||||||
where
|
where
|
||||||
C: Curve + ValidatePublicKey,
|
C: PrimeCurve + ProjectiveArithmetic,
|
||||||
FieldSize<C>: ModulusSize,
|
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
|
||||||
|
SignatureSize<C>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
type Error = elliptic_curve::Error;
|
type Error = ecdsa::Error;
|
||||||
fn try_into(self) -> Result<SecretKey<C>, Self::Error> {
|
|
||||||
(&self).try_into()
|
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
|
where
|
||||||
C: Curve + ValidatePublicKey,
|
C: Curve + ValidatePublicKey,
|
||||||
FieldSize<C>: ModulusSize,
|
FieldSize<C>: ModulusSize,
|
||||||
{
|
{
|
||||||
type Error = elliptic_curve::Error;
|
type Error = elliptic_curve::Error;
|
||||||
|
|
||||||
fn try_into(self) -> Result<SecretKey<C>, Self::Error> {
|
fn try_from(value: &EcPrivateParameters) -> Result<Self, Self::Error> {
|
||||||
let x = self
|
let x = value
|
||||||
.x
|
.x
|
||||||
.get(..FieldSize::<C>::USIZE)
|
.get(..FieldSize::<C>::USIZE)
|
||||||
.ok_or(elliptic_curve::Error)?;
|
.ok_or(elliptic_curve::Error)?;
|
||||||
let y = self
|
let y = value
|
||||||
.x
|
.x
|
||||||
.get(..FieldSize::<C>::USIZE)
|
.get(..FieldSize::<C>::USIZE)
|
||||||
.ok_or(elliptic_curve::Error)?;
|
.ok_or(elliptic_curve::Error)?;
|
||||||
@@ -305,7 +334,7 @@ mod ec_impls {
|
|||||||
let x = FieldBytes::<C>::from_slice(x);
|
let x = FieldBytes::<C>::from_slice(x);
|
||||||
let y = FieldBytes::<C>::from_slice(y);
|
let y = FieldBytes::<C>::from_slice(y);
|
||||||
let pubkey = EncodedPoint::<C>::from_affine_coordinates(x, y, false);
|
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)?;
|
C::validate_public_key(&privkey, &pubkey)?;
|
||||||
Ok(privkey)
|
Ok(privkey)
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,10 @@ use signature::{Signature, Signer, Verifier};
|
|||||||
use tower::Service;
|
use tower::Service;
|
||||||
|
|
||||||
use super::{SigningKeystore, VerifyingKeystore};
|
use super::{SigningKeystore, VerifyingKeystore};
|
||||||
use crate::{jwk::PublicJsonWebKeySet, JsonWebKey, JsonWebKeySet, JsonWebSignatureHeader};
|
use crate::{
|
||||||
|
jwk::{JsonWebKey, JsonWebKeySet, PublicJsonWebKeySet},
|
||||||
|
JsonWebSignatureHeader,
|
||||||
|
};
|
||||||
|
|
||||||
// Generate with
|
// Generate with
|
||||||
// openssl genrsa 2048
|
// openssl genrsa 2048
|
||||||
|
@@ -24,12 +24,12 @@ pub mod jwk;
|
|||||||
pub(crate) mod jwt;
|
pub(crate) mod jwt;
|
||||||
mod keystore;
|
mod keystore;
|
||||||
pub(crate) mod rsa;
|
pub(crate) mod rsa;
|
||||||
|
pub mod signer;
|
||||||
pub mod verifier;
|
pub mod verifier;
|
||||||
|
|
||||||
pub use futures_util::future::Either;
|
pub use futures_util::future::Either;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
jwk::{JsonWebKey, JsonWebKeySet},
|
|
||||||
jwt::{DecodedJsonWebToken, JsonWebSignatureHeader, JsonWebTokenParts, Jwt, JwtSignatureError},
|
jwt::{DecodedJsonWebToken, JsonWebSignatureHeader, JsonWebTokenParts, Jwt, JwtSignatureError},
|
||||||
keystore::{
|
keystore::{
|
||||||
DynamicJwksStore, SharedSecret, SigningKeystore, StaticJwksStore, StaticKeystore,
|
DynamicJwksStore, SharedSecret, SigningKeystore, StaticJwksStore, StaticKeystore,
|
||||||
|
@@ -19,7 +19,7 @@ pub(crate) mod pkcs1v15 {
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use digest::Digest;
|
use digest::Digest;
|
||||||
use rsa::RsaPublicKey;
|
use rsa::{RsaPrivateKey, RsaPublicKey};
|
||||||
use sha2::{Sha256, Sha384, Sha512};
|
use sha2::{Sha256, Sha384, Sha512};
|
||||||
|
|
||||||
pub struct VerifyingKey<H> {
|
pub struct VerifyingKey<H> {
|
||||||
@@ -78,14 +78,69 @@ pub(crate) mod pkcs1v15 {
|
|||||||
self.inner.verify(&digest, signature)
|
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 {
|
pub(crate) mod pss {
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use digest::Digest;
|
use digest::Digest;
|
||||||
use rsa::RsaPublicKey;
|
use rand::thread_rng;
|
||||||
|
use rsa::{RsaPrivateKey, RsaPublicKey};
|
||||||
use sha2::{Sha256, Sha384, Sha512};
|
use sha2::{Sha256, Sha384, Sha512};
|
||||||
|
use signature::RandomizedSigner;
|
||||||
|
|
||||||
pub struct VerifyingKey<H> {
|
pub struct VerifyingKey<H> {
|
||||||
inner: rsa::pss::VerifyingKey,
|
inner: rsa::pss::VerifyingKey,
|
||||||
@@ -143,4 +198,57 @@ pub(crate) mod pss {
|
|||||||
self.inner.verify(&digest, signature)
|
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
268
crates/jose/src/signer.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -72,18 +72,13 @@ pub enum VerifierFromJwkError {
|
|||||||
inner: ecdsa::Error,
|
inner: ecdsa::Error,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("invalid curve parameter X")]
|
|
||||||
InvalidCurveParameterX,
|
|
||||||
|
|
||||||
#[error("invalid curve parameter Y")]
|
|
||||||
InvalidCurveParameterY,
|
|
||||||
|
|
||||||
#[error("algorithm {algorithm} is not supported")]
|
#[error("algorithm {algorithm} is not supported")]
|
||||||
UnsupportedAlgorithm { algorithm: JsonWebSignatureAlg },
|
UnsupportedAlgorithm { algorithm: JsonWebSignatureAlg },
|
||||||
|
|
||||||
#[error("key is not suitable for algorithm {algorithm}")]
|
#[error("key is not suitable for algorithm {algorithm}")]
|
||||||
KeyNotSuitable { algorithm: JsonWebSignatureAlg },
|
KeyNotSuitable { algorithm: JsonWebSignatureAlg },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum VerifierFromOctError {
|
pub enum VerifierFromOctError {
|
||||||
#[error("algorithm {algorithm} is not supported")]
|
#[error("algorithm {algorithm} is not supported")]
|
||||||
|
@@ -12,15 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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 HS256_JWT: &str = include_str!("./jwts/hs256.jwt");
|
||||||
static HS384_JWT: &str = include_str!("./jwts/hs384.jwt");
|
static HS384_JWT: &str = include_str!("./jwts/hs384.jwt");
|
||||||
static HS512_JWT: &str = include_str!("./jwts/hs512.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 EDDSA_ED448_JWT: &str = include_str!("./jwts/eddsa-ed448.jwt");
|
||||||
static OCT_KEY: &[u8] = include_bytes!("./keys/oct.bin");
|
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()
|
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()
|
serde_json::from_str(include_str!("./keys/jwks.priv.json")).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,74 +41,189 @@ fn oct_key() -> Vec<u8> {
|
|||||||
OCT_KEY.to_vec()
|
OCT_KEY.to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(serde::Deserialize, serde::Serialize)]
|
||||||
struct Payload {
|
struct Payload {
|
||||||
hello: String,
|
hello: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! asymetric_jwt_test {
|
macro_rules! conditional {
|
||||||
($test_name:ident, $jwt:ident) => {
|
{ true => $($tt:tt)* } => {
|
||||||
asymetric_jwt_test!($test_name, $jwt, verify = true);
|
$($tt)*
|
||||||
};
|
};
|
||||||
($test_name:ident, $jwt:ident, verify = $verify:ident) => {
|
{ false => $($tt:tt)* } => {};
|
||||||
#[test]
|
}
|
||||||
fn $test_name() {
|
|
||||||
let jwks = public_jwks();
|
|
||||||
let jwt: Jwt<'_, Payload> = Jwt::try_from($jwt).unwrap();
|
|
||||||
assert_eq!(jwt.payload().hello, "world");
|
|
||||||
|
|
||||||
let constraints = ConstraintSet::from(jwt.header());
|
macro_rules! asymetric_jwt_test {
|
||||||
let candidates = constraints.filter(jwks.deref());
|
($test_name:ident, $alg:ident, $jwt:ident) => {
|
||||||
assert_eq!(candidates.len(), 1);
|
asymetric_jwt_test!($test_name, $alg, $jwt, supported = true);
|
||||||
let candidate = candidates[0];
|
};
|
||||||
|
($test_name:ident, $alg:ident, $jwt:ident, supported = $supported:ident) => {
|
||||||
|
mod $test_name {
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
if $verify {
|
use mas_iana::jose::JsonWebSignatureAlg;
|
||||||
let verifier = mas_jose::verifier::Verifier::for_jwk_and_alg(
|
use mas_jose::{constraints::ConstraintSet, Jwt};
|
||||||
candidate.params(),
|
|
||||||
jwt.header().alg(),
|
use super::*;
|
||||||
)
|
|
||||||
.unwrap();
|
#[test]
|
||||||
jwt.verify(&verifier).unwrap();
|
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 {
|
macro_rules! symetric_jwt_test {
|
||||||
($test_name:ident, $jwt:ident) => {
|
($test_name:ident, $alg:ident, $jwt:ident) => {
|
||||||
#[test]
|
mod $test_name {
|
||||||
fn $test_name() {
|
use mas_iana::jose::JsonWebSignatureAlg;
|
||||||
let jwt: Jwt<'_, Payload> = Jwt::try_from($jwt).unwrap();
|
use mas_jose::{JsonWebSignatureHeader, Jwt};
|
||||||
let verifier =
|
|
||||||
mas_jose::verifier::Verifier::for_oct_and_alg(oct_key(), jwt.header().alg())
|
use super::*;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(jwt.payload().hello, "world");
|
#[test]
|
||||||
jwt.verify(&verifier).unwrap();
|
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!(hs256, Hs256, HS256_JWT);
|
||||||
symetric_jwt_test!(test_hs384, HS384_JWT);
|
symetric_jwt_test!(hs384, Hs384, HS384_JWT);
|
||||||
symetric_jwt_test!(test_hs512, HS512_JWT);
|
symetric_jwt_test!(hs512, Hs512, HS512_JWT);
|
||||||
|
|
||||||
asymetric_jwt_test!(test_rs256, RS256_JWT);
|
asymetric_jwt_test!(rs256, Rs256, RS256_JWT);
|
||||||
asymetric_jwt_test!(test_rs384, RS384_JWT);
|
asymetric_jwt_test!(rs384, Rs384, RS384_JWT);
|
||||||
asymetric_jwt_test!(test_rs512, RS512_JWT);
|
asymetric_jwt_test!(rs512, Rs512, RS512_JWT);
|
||||||
asymetric_jwt_test!(test_ps256, PS256_JWT);
|
asymetric_jwt_test!(ps256, Ps256, PS256_JWT);
|
||||||
asymetric_jwt_test!(test_ps384, PS384_JWT);
|
asymetric_jwt_test!(ps384, Ps384, PS384_JWT);
|
||||||
asymetric_jwt_test!(test_ps512, PS512_JWT);
|
asymetric_jwt_test!(ps512, Ps512, PS512_JWT);
|
||||||
asymetric_jwt_test!(test_es256, ES256_JWT);
|
asymetric_jwt_test!(es256, Es256, ES256_JWT);
|
||||||
asymetric_jwt_test!(test_es384, ES384_JWT);
|
asymetric_jwt_test!(es384, Es384, ES384_JWT);
|
||||||
asymetric_jwt_test!(test_es512, ES512_JWT, verify = false);
|
asymetric_jwt_test!(es512, Es512, ES512_JWT, supported = false);
|
||||||
asymetric_jwt_test!(test_es256k, ES256K_JWT);
|
asymetric_jwt_test!(es256k, Es256K, ES256K_JWT);
|
||||||
asymetric_jwt_test!(test_eddsa_ed25519, EDDSA_ED25519_JWT, verify = false);
|
asymetric_jwt_test!(eddsa_ed25519, EdDsa, EDDSA_ED25519_JWT, supported = false);
|
||||||
asymetric_jwt_test!(test_eddsa_ed448, EDDSA_ED448_JWT, verify = false);
|
asymetric_jwt_test!(eddsa_ed448, EdDsa, EDDSA_ED448_JWT, supported = false);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_private_to_public_jwks() {
|
fn test_private_to_public_jwks() {
|
||||||
let priv_jwks = private_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());
|
assert_eq!(pub_jwks, public_jwks());
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user