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

Sign all the things

This commit is contained in:
Quentin Gliech
2022-08-26 14:00:33 +02:00
parent ca125a14c5
commit 956556b0ff
44 changed files with 1479 additions and 48 deletions

View File

@@ -0,0 +1,280 @@
// 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 std::collections::HashSet;
use futures_util::future::Either;
use mas_iana::jose::{JsonWebKeyType, JsonWebKeyUse, JsonWebSignatureAlg};
use crate::JsonWebSignatureHeader;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Constraint<'a> {
Alg {
constraint_alg: JsonWebSignatureAlg,
},
Algs {
constraint_algs: &'a [JsonWebSignatureAlg],
},
Kid {
constraint_kid: &'a str,
},
Use {
constraint_use: JsonWebKeyUse,
},
Kty {
constraint_kty: JsonWebKeyType,
},
}
impl<'a> Constraint<'a> {
#[must_use]
pub fn alg(constraint_alg: JsonWebSignatureAlg) -> Self {
Constraint::Alg { constraint_alg }
}
#[must_use]
pub fn algs(constraint_algs: &'a [JsonWebSignatureAlg]) -> Self {
Constraint::Algs { constraint_algs }
}
#[must_use]
pub fn kid(constraint_kid: &'a str) -> Self {
Constraint::Kid { constraint_kid }
}
#[must_use]
pub fn use_(constraint_use: JsonWebKeyUse) -> Self {
Constraint::Use { constraint_use }
}
#[must_use]
pub fn kty(constraint_kty: JsonWebKeyType) -> Self {
Constraint::Kty { constraint_kty }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ConstraintDecision {
Positive,
Neutral,
Negative,
}
pub trait Constrainable {
/// List of available algorithms for this key
fn algs(&self) -> Option<Vec<JsonWebSignatureAlg>> {
None
}
/// Key ID (`kid`) of this key
fn kid(&self) -> Option<&str> {
None
}
/// Usage specified for this key
fn use_(&self) -> Option<JsonWebKeyUse> {
None
}
/// Key type (`kty`) of this key
fn kty(&self) -> JsonWebKeyType;
}
impl<L, R> Constrainable for Either<L, R>
where
L: Constrainable,
R: Constrainable,
{
fn algs(&self) -> Option<Vec<JsonWebSignatureAlg>> {
match self {
Either::Left(l) => l.algs(),
Either::Right(r) => r.algs(),
}
}
fn kid(&self) -> Option<&str> {
match self {
Either::Left(l) => l.kid(),
Either::Right(r) => r.kid(),
}
}
fn use_(&self) -> Option<JsonWebKeyUse> {
match self {
Either::Left(l) => l.use_(),
Either::Right(r) => r.use_(),
}
}
fn kty(&self) -> JsonWebKeyType {
match self {
Either::Left(l) => l.kty(),
Either::Right(r) => r.kty(),
}
}
}
impl<'a> Constraint<'a> {
fn decide<T: Constrainable>(&self, constrainable: &T) -> ConstraintDecision {
match self {
Constraint::Alg { constraint_alg } => {
if let Some(algs) = constrainable.algs() {
if algs.contains(constraint_alg) {
ConstraintDecision::Positive
} else {
ConstraintDecision::Negative
}
} else {
ConstraintDecision::Neutral
}
}
Constraint::Algs { constraint_algs } => {
if let Some(algs) = constrainable.algs() {
if algs.iter().any(|alg| constraint_algs.contains(alg)) {
ConstraintDecision::Positive
} else {
ConstraintDecision::Negative
}
} else {
ConstraintDecision::Neutral
}
}
Constraint::Kid { constraint_kid } => {
if let Some(kid) = constrainable.kid() {
if kid == *constraint_kid {
ConstraintDecision::Positive
} else {
ConstraintDecision::Negative
}
} else {
ConstraintDecision::Neutral
}
}
Constraint::Use { constraint_use } => {
if let Some(use_) = constrainable.use_() {
if use_ == *constraint_use {
ConstraintDecision::Positive
} else {
ConstraintDecision::Negative
}
} else {
ConstraintDecision::Neutral
}
}
Constraint::Kty { constraint_kty } => {
if *constraint_kty == constrainable.kty() {
ConstraintDecision::Positive
} else {
ConstraintDecision::Negative
}
}
}
}
}
#[derive(Default)]
pub struct ConstraintSet<'a> {
constraints: HashSet<Constraint<'a>>,
}
impl<'a> FromIterator<Constraint<'a>> for ConstraintSet<'a> {
fn from_iter<T: IntoIterator<Item = Constraint<'a>>>(iter: T) -> Self {
Self {
constraints: HashSet::from_iter(iter),
}
}
}
#[allow(dead_code)]
impl<'a> ConstraintSet<'a> {
pub fn new(constraints: impl IntoIterator<Item = Constraint<'a>>) -> Self {
constraints.into_iter().collect()
}
pub fn filter<'b, T: Constrainable, I: IntoIterator<Item = &'b T>>(
&self,
constrainables: I,
) -> Vec<&'b T> {
let mut selected = Vec::new();
'outer: for constrainable in constrainables {
let mut score = 0;
for constraint in &self.constraints {
match constraint.decide(constrainable) {
ConstraintDecision::Positive => score += 1,
ConstraintDecision::Neutral => {}
// If any constraint was negative, don't add it to the candidates
ConstraintDecision::Negative => break 'outer,
}
}
selected.push((score, constrainable));
}
selected.sort_by_key(|(score, _)| *score);
selected
.into_iter()
.map(|(_score, constrainable)| constrainable)
.collect()
}
#[must_use]
pub fn alg(mut self, constraint_alg: JsonWebSignatureAlg) -> Self {
self.constraints.insert(Constraint::alg(constraint_alg));
self
}
#[must_use]
pub fn algs(mut self, constraint_algs: &'a [JsonWebSignatureAlg]) -> Self {
self.constraints.insert(Constraint::algs(constraint_algs));
self
}
#[must_use]
pub fn kid(mut self, constraint_kid: &'a str) -> Self {
self.constraints.insert(Constraint::kid(constraint_kid));
self
}
#[must_use]
pub fn use_(mut self, constraint_use: JsonWebKeyUse) -> Self {
self.constraints.insert(Constraint::use_(constraint_use));
self
}
#[must_use]
pub fn kty(mut self, constraint_kty: JsonWebKeyType) -> Self {
self.constraints.insert(Constraint::kty(constraint_kty));
self
}
}
impl<'a> From<&'a JsonWebSignatureHeader> for ConstraintSet<'a> {
fn from(header: &'a JsonWebSignatureHeader) -> Self {
let mut constraints = Self::default().alg(header.alg());
if let Some(kid) = header.kid() {
constraints = constraints.kid(kid);
}
constraints
}
}

109
crates/jose/src/hmac.rs Normal file
View File

@@ -0,0 +1,109 @@
// 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 std::marker::PhantomData;
use digest::{
crypto_common::BlockSizeUser,
generic_array::{ArrayLength, GenericArray},
Digest, Mac, OutputSizeUser,
};
use signature::{Signer, Verifier};
use thiserror::Error;
pub struct Signature<S: ArrayLength<u8>> {
signature: GenericArray<u8, S>,
}
impl<S: ArrayLength<u8>> PartialEq for Signature<S> {
fn eq(&self, other: &Self) -> bool {
self.signature == other.signature
}
}
impl<S: ArrayLength<u8>> Eq for Signature<S> {}
impl<S: ArrayLength<u8>> std::fmt::Debug for Signature<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.signature)
}
}
impl<S: ArrayLength<u8>> signature::Signature for Signature<S> {
fn from_bytes(bytes: &[u8]) -> Result<Self, signature::Error> {
if bytes.len() != S::to_usize() {
return Err(signature::Error::new());
}
Ok(Self {
signature: GenericArray::from_slice(bytes).clone(),
})
}
}
impl<S: ArrayLength<u8>> AsRef<[u8]> for Signature<S> {
fn as_ref(&self) -> &[u8] {
self.signature.as_ref()
}
}
pub struct Hmac<D> {
key: Vec<u8>,
digest: PhantomData<D>,
}
#[derive(Error, Debug)]
#[error("invalid length")]
pub struct InvalidLength;
impl<D> From<Vec<u8>> for Hmac<D> {
fn from(key: Vec<u8>) -> Self {
Self {
key,
digest: PhantomData::default(),
}
}
}
impl<D: Digest + BlockSizeUser>
Signer<Signature<<hmac::SimpleHmac<D> as OutputSizeUser>::OutputSize>> for Hmac<D>
{
fn try_sign(
&self,
msg: &[u8],
) -> Result<Signature<<hmac::SimpleHmac<D> as OutputSizeUser>::OutputSize>, signature::Error>
{
let mut mac = <hmac::SimpleHmac<D> as Mac>::new_from_slice(&self.key)
.map_err(signature::Error::from_source)?;
mac.update(msg);
let signature = mac.finalize().into_bytes();
Ok(Signature { signature })
}
}
impl<D: Digest + BlockSizeUser>
Verifier<Signature<<hmac::SimpleHmac<D> as OutputSizeUser>::OutputSize>> for Hmac<D>
{
fn verify(
&self,
msg: &[u8],
signature: &Signature<<hmac::SimpleHmac<D> as OutputSizeUser>::OutputSize>,
) -> Result<(), signature::Error> {
let new_signature = self.try_sign(msg)?;
if &new_signature != signature {
return Err(signature::Error::new());
}
Ok(())
}
}

View File

@@ -30,6 +30,8 @@ use serde_with::{
};
use url::Url;
use crate::constraints::Constrainable;
#[serde_as]
#[skip_serializing_none]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
@@ -109,20 +111,6 @@ impl JsonWebKey {
self
}
#[must_use]
pub const fn kty(&self) -> JsonWebKeyType {
match self.parameters {
JsonWebKeyParameters::Ec { .. } => JsonWebKeyType::Ec,
JsonWebKeyParameters::Rsa { .. } => JsonWebKeyType::Rsa,
JsonWebKeyParameters::Okp { .. } => JsonWebKeyType::Okp,
}
}
#[must_use]
pub fn kid(&self) -> Option<&str> {
self.kid.as_deref()
}
#[must_use]
pub const fn alg(&self) -> Option<JsonWebSignatureAlg> {
self.alg
@@ -134,6 +122,58 @@ impl JsonWebKey {
}
}
impl Constrainable for JsonWebKey {
fn kid(&self) -> Option<&str> {
self.kid.as_deref()
}
fn kty(&self) -> JsonWebKeyType {
match self.parameters {
JsonWebKeyParameters::Ec { .. } => JsonWebKeyType::Ec,
JsonWebKeyParameters::Rsa { .. } => JsonWebKeyType::Rsa,
JsonWebKeyParameters::Okp { .. } => JsonWebKeyType::Okp,
}
}
fn algs(&self) -> Option<Vec<JsonWebSignatureAlg>> {
if let Some(alg) = self.alg {
Some(vec![alg])
} else {
match self.parameters {
JsonWebKeyParameters::Rsa { .. } => Some(vec![
JsonWebSignatureAlg::Rs256,
JsonWebSignatureAlg::Rs384,
JsonWebSignatureAlg::Rs512,
JsonWebSignatureAlg::Ps256,
JsonWebSignatureAlg::Ps384,
JsonWebSignatureAlg::Ps512,
]),
JsonWebKeyParameters::Ec {
crv: JsonWebKeyEcEllipticCurve::P256,
..
} => Some(vec![JsonWebSignatureAlg::Es256]),
JsonWebKeyParameters::Ec {
crv: JsonWebKeyEcEllipticCurve::P384,
..
} => Some(vec![JsonWebSignatureAlg::Es384]),
JsonWebKeyParameters::Ec {
crv: JsonWebKeyEcEllipticCurve::P521,
..
} => Some(vec![JsonWebSignatureAlg::Es512]),
JsonWebKeyParameters::Ec {
crv: JsonWebKeyEcEllipticCurve::Secp256K1,
..
} => Some(vec![JsonWebSignatureAlg::Es256K]),
JsonWebKeyParameters::Okp { .. } => Some(vec![JsonWebSignatureAlg::EdDsa]),
}
}
}
fn use_(&self) -> Option<JsonWebKeyUse> {
self.r#use
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct JsonWebKeySet {
keys: Vec<JsonWebKey>,
@@ -251,10 +291,11 @@ impl From<rsa::RsaPublicKey> for JsonWebKeyParameters {
#[cfg(test)]
mod tests {
use super::*;
use crate::constraints::ConstraintSet;
#[test]
fn load_google_keys() {
let jwks = r#"{
let jwks = serde_json::json!({
"keys": [
{
"alg": "RS256",
@@ -273,19 +314,33 @@ mod tests {
"alg": "RS256"
}
]
}"#;
});
let jwks: JsonWebKeySet = serde_json::from_str(jwks).unwrap();
let jwks: JsonWebKeySet = serde_json::from_value(jwks).unwrap();
// Both keys are RSA public keys
for jwk in jwks.keys {
rsa::RsaPublicKey::try_from(jwk.parameters).unwrap();
for jwk in &jwks.keys {
rsa::RsaPublicKey::try_from(jwk.parameters.clone()).unwrap();
}
let constraints = ConstraintSet::default()
.use_(JsonWebKeyUse::Sig)
.kty(JsonWebKeyType::Rsa)
.alg(JsonWebSignatureAlg::Rs256);
let candidates = constraints.filter(&jwks.keys);
assert_eq!(candidates.len(), 2);
let constraints = ConstraintSet::default()
.use_(JsonWebKeyUse::Sig)
.kty(JsonWebKeyType::Rsa)
.kid("03e84aed4ef4431014e8617567864c4efaaaede9");
let candidates = constraints.filter(&jwks.keys);
assert_eq!(candidates.len(), 1);
}
#[allow(clippy::too_many_lines)]
#[test]
fn load_keycloak_keys() {
let jwks = r#"{
let jwks = serde_json::json!({
"keys": [
{
"kid": "SuGUPE9Sr-1Gha2NLse33r5NQu3XoS_I3Qds3bcmfQE",
@@ -393,9 +448,9 @@ mod tests {
"y": "Vu3rTFNn_9zWTki95UGT1Bd9PN84KDXmttCrJ1bsYHTWQCaEONk8iwA3U6mEDrg4xtZSTXXKCFdFP13ONWB9oZ4"
}
]
}"#;
});
let jwks: JsonWebKeySet = serde_json::from_str(jwks).unwrap();
let jwks: JsonWebKeySet = serde_json::from_value(jwks).unwrap();
// The first 6 keys are RSA, 7th is P-256
let mut keys = jwks.keys.into_iter();
rsa::RsaPublicKey::try_from(keys.next().unwrap().parameters).unwrap();

View File

@@ -115,14 +115,17 @@ where
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
let raw = RawJwt::try_from(value)?;
let header_reader =
base64ct::Decoder::<'_, Base64UrlUnpadded>::new(raw.header().as_bytes())
.map_err(JwtDecodeError::decode_header)?;
let header =
Base64UrlUnpadded::decode_vec(raw.header()).map_err(JwtDecodeError::decode_header)?;
let header = serde_json::from_slice(&header).map_err(JwtDecodeError::deserialize_header)?;
serde_json::from_reader(header_reader).map_err(JwtDecodeError::deserialize_header)?;
let payload_reader =
base64ct::Decoder::<'_, Base64UrlUnpadded>::new(raw.payload().as_bytes())
.map_err(JwtDecodeError::decode_payload)?;
let payload =
Base64UrlUnpadded::decode_vec(raw.payload()).map_err(JwtDecodeError::decode_payload)?;
let payload =
serde_json::from_slice(&payload).map_err(JwtDecodeError::deserialize_payload)?;
serde_json::from_reader(payload_reader).map_err(JwtDecodeError::deserialize_payload)?;
let signature = Base64UrlUnpadded::decode_vec(raw.signature())
.map_err(JwtDecodeError::decode_signature)?;
@@ -162,6 +165,14 @@ impl JwtVerificationError {
}
impl<'a, T> Jwt<'a, T> {
pub fn header(&self) -> &JsonWebSignatureHeader {
&self.header
}
pub fn payload(&self) -> &T {
&self.payload
}
pub fn verify<K, S>(&self, key: &K) -> Result<(), JwtVerificationError>
where
K: Verifier<S>,

View File

@@ -21,7 +21,10 @@ use sha2::{Sha256, Sha384, Sha512};
use signature::{Signature, Verifier};
use thiserror::Error;
use crate::{JsonWebKey, JsonWebKeySet, JsonWebSignatureHeader, VerifyingKeystore};
use crate::{
constraints::Constrainable, JsonWebKey, JsonWebKeySet, JsonWebSignatureHeader,
VerifyingKeystore,
};
#[derive(Debug, Error)]
pub enum Error {

View File

@@ -18,9 +18,13 @@
#![allow(clippy::missing_errors_doc, clippy::module_name_repetitions)]
pub mod claims;
pub mod constraints;
pub mod hmac;
pub(crate) mod jwk;
pub(crate) mod jwt;
mod keystore;
pub(crate) mod rsa;
pub mod verifier;
pub use futures_util::future::Either;

146
crates/jose/src/rsa.rs Normal file
View File

@@ -0,0 +1,146 @@
// 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.
// This is a temporary wrapper until the RSA crate actually hashes the input
// See <https://github.com/RustCrypto/RSA/pull/174#issuecomment-1227330296>
pub(crate) mod pkcs1v15 {
use std::marker::PhantomData;
use digest::Digest;
use rsa::RsaPublicKey;
use sha2::{Sha256, Sha384, Sha512};
pub struct VerifyingKey<H> {
inner: rsa::pkcs1v15::VerifyingKey,
hash: PhantomData<H>,
}
impl From<RsaPublicKey> for VerifyingKey<Sha256> {
fn from(key: RsaPublicKey) -> Self {
let inner = rsa::pkcs1v15::VerifyingKey::new_with_hash(key, rsa::Hash::SHA2_256);
ensure_verifier(Self {
inner,
hash: PhantomData::default(),
})
}
}
impl From<RsaPublicKey> for VerifyingKey<Sha384> {
fn from(key: RsaPublicKey) -> Self {
let inner = rsa::pkcs1v15::VerifyingKey::new_with_hash(key, rsa::Hash::SHA2_384);
ensure_verifier(Self {
inner,
hash: PhantomData::default(),
})
}
}
impl From<RsaPublicKey> for VerifyingKey<Sha512> {
fn from(key: RsaPublicKey) -> Self {
let inner = rsa::pkcs1v15::VerifyingKey::new_with_hash(key, rsa::Hash::SHA2_512);
ensure_verifier(Self {
inner,
hash: PhantomData::default(),
})
}
}
#[inline]
fn ensure_verifier<T>(t: T) -> T
where
T: signature::Verifier<rsa::pkcs1v15::Signature>,
{
t
}
impl<H> signature::Verifier<rsa::pkcs1v15::Signature> for VerifyingKey<H>
where
H: Digest,
{
fn verify(
&self,
msg: &[u8],
signature: &rsa::pkcs1v15::Signature,
) -> Result<(), signature::Error> {
let digest = H::digest(msg);
self.inner.verify(&digest, signature)
}
}
}
pub(crate) mod pss {
use std::marker::PhantomData;
use digest::Digest;
use rsa::RsaPublicKey;
use sha2::{Sha256, Sha384, Sha512};
pub struct VerifyingKey<H> {
inner: rsa::pss::VerifyingKey,
hash: PhantomData<H>,
}
impl From<RsaPublicKey> for VerifyingKey<Sha256> {
fn from(key: RsaPublicKey) -> Self {
let inner = rsa::pss::VerifyingKey::new(key, Box::new(Sha256::new()));
ensure_verifier(Self {
inner,
hash: PhantomData::default(),
})
}
}
impl From<RsaPublicKey> for VerifyingKey<Sha384> {
fn from(key: RsaPublicKey) -> Self {
let inner = rsa::pss::VerifyingKey::new(key, Box::new(Sha384::new()));
ensure_verifier(Self {
inner,
hash: PhantomData::default(),
})
}
}
impl From<RsaPublicKey> for VerifyingKey<Sha512> {
fn from(key: RsaPublicKey) -> Self {
let inner = rsa::pss::VerifyingKey::new(key, Box::new(Sha512::new()));
ensure_verifier(Self {
inner,
hash: PhantomData::default(),
})
}
}
#[inline]
fn ensure_verifier<T>(t: T) -> T
where
T: signature::Verifier<rsa::pss::Signature>,
{
t
}
impl<H> signature::Verifier<rsa::pss::Signature> for VerifyingKey<H>
where
H: Digest,
{
fn verify(
&self,
msg: &[u8],
signature: &rsa::pss::Signature,
) -> Result<(), signature::Error> {
let digest = H::digest(msg);
self.inner.verify(&digest, signature)
}
}
}

330
crates/jose/src/verifier.rs Normal file
View File

@@ -0,0 +1,330 @@
// 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 rsa::BigUint;
use sha2::{Sha256, Sha384, Sha512};
use signature::Signature;
use thiserror::Error;
use crate::jwk::JsonWebKeyParameters;
pub enum Verifier {
Hs256 {
key: crate::hmac::Hmac<Sha256>,
},
Hs384 {
key: crate::hmac::Hmac<Sha384>,
},
Hs512 {
key: crate::hmac::Hmac<Sha512>,
},
Rs256 {
key: crate::rsa::pkcs1v15::VerifyingKey<Sha256>,
},
Rs384 {
key: crate::rsa::pkcs1v15::VerifyingKey<Sha384>,
},
Rs512 {
key: crate::rsa::pkcs1v15::VerifyingKey<Sha512>,
},
Ps256 {
key: crate::rsa::pss::VerifyingKey<Sha256>,
},
Ps384 {
key: crate::rsa::pss::VerifyingKey<Sha384>,
},
Ps512 {
key: crate::rsa::pss::VerifyingKey<Sha512>,
},
Es256 {
key: ecdsa::VerifyingKey<p256::NistP256>,
},
Es384 {
key: ecdsa::VerifyingKey<p384::NistP384>,
},
Es256K {
key: ecdsa::VerifyingKey<k256::Secp256k1>,
},
}
#[derive(Debug, Error)]
pub enum VerifierFromJwkError {
#[error("invalid RSA key")]
InvalidRsaKey {
#[from]
inner: rsa::errors::Error,
},
#[error("invalid elliptic curve key")]
InvalidEcKey {
#[from]
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")]
UnsupportedAlgorithm { algorithm: JsonWebSignatureAlg },
}
impl Verifier {
pub fn for_oct_and_alg(
key: Vec<u8>,
alg: JsonWebSignatureAlg,
) -> Result<Self, VerifierFromOctError> {
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(VerifierFromOctError::UnsupportedAlgorithm { algorithm }),
}
}
#[allow(clippy::too_many_lines)]
pub fn for_jwk_and_alg(
key: &JsonWebKeyParameters,
alg: JsonWebSignatureAlg,
) -> Result<Self, VerifierFromJwkError> {
match (key, alg) {
(JsonWebKeyParameters::Rsa { n, e }, JsonWebSignatureAlg::Rs256) => {
let n = BigUint::from_bytes_be(n);
let e = BigUint::from_bytes_be(e);
let key = rsa::RsaPublicKey::new(n, e)?;
Ok(Self::Rs256 { key: key.into() })
}
(JsonWebKeyParameters::Rsa { n, e }, JsonWebSignatureAlg::Rs384) => {
let n = BigUint::from_bytes_be(n);
let e = BigUint::from_bytes_be(e);
let key = rsa::RsaPublicKey::new(n, e)?;
Ok(Self::Rs384 { key: key.into() })
}
(JsonWebKeyParameters::Rsa { n, e }, JsonWebSignatureAlg::Rs512) => {
let n = BigUint::from_bytes_be(n);
let e = BigUint::from_bytes_be(e);
let key = rsa::RsaPublicKey::new(n, e)?;
Ok(Self::Rs512 { key: key.into() })
}
(JsonWebKeyParameters::Rsa { n, e }, JsonWebSignatureAlg::Ps256) => {
let n = BigUint::from_bytes_be(n);
let e = BigUint::from_bytes_be(e);
let key = rsa::RsaPublicKey::new(n, e)?;
Ok(Self::Ps256 { key: key.into() })
}
(JsonWebKeyParameters::Rsa { n, e }, JsonWebSignatureAlg::Ps384) => {
let n = BigUint::from_bytes_be(n);
let e = BigUint::from_bytes_be(e);
let key = rsa::RsaPublicKey::new(n, e)?;
Ok(Self::Ps384 { key: key.into() })
}
(JsonWebKeyParameters::Rsa { n, e }, JsonWebSignatureAlg::Ps512) => {
let n = BigUint::from_bytes_be(n);
let e = BigUint::from_bytes_be(e);
let key = rsa::RsaPublicKey::new(n, e)?;
Ok(Self::Ps512 { key: key.into() })
}
(
JsonWebKeyParameters::Ec {
crv: JsonWebKeyEcEllipticCurve::P256,
x,
y,
},
JsonWebSignatureAlg::Es256,
) => {
let x: &[u8; 32] = x
.as_slice()
.try_into()
.map_err(|_| VerifierFromJwkError::InvalidCurveParameterX)?;
let y: &[u8; 32] = y
.as_slice()
.try_into()
.map_err(|_| VerifierFromJwkError::InvalidCurveParameterY)?;
let point = sec1::EncodedPoint::from_affine_coordinates(x.into(), y.into(), false);
let key = ecdsa::VerifyingKey::from_encoded_point(&point)?;
Ok(Self::Es256 { key })
}
(
JsonWebKeyParameters::Ec {
crv: JsonWebKeyEcEllipticCurve::P384,
x,
y,
},
JsonWebSignatureAlg::Es384,
) => {
let x: &[u8; 48] = x
.as_slice()
.try_into()
.map_err(|_| VerifierFromJwkError::InvalidCurveParameterX)?;
let y: &[u8; 48] = y
.as_slice()
.try_into()
.map_err(|_| VerifierFromJwkError::InvalidCurveParameterY)?;
let point = sec1::EncodedPoint::from_affine_coordinates(x.into(), y.into(), false);
let key = ecdsa::VerifyingKey::from_encoded_point(&point)?;
Ok(Self::Es384 { key })
}
(
JsonWebKeyParameters::Ec {
crv: JsonWebKeyEcEllipticCurve::P521,
x: _,
y: _,
},
JsonWebSignatureAlg::Es512,
) => Err(VerifierFromJwkError::UnsupportedAlgorithm {
algorithm: JsonWebSignatureAlg::Es512,
}),
(
JsonWebKeyParameters::Ec {
crv: JsonWebKeyEcEllipticCurve::Secp256K1,
x,
y,
},
JsonWebSignatureAlg::Es256K,
) => {
let x: &[u8; 32] = x
.as_slice()
.try_into()
.map_err(|_| VerifierFromJwkError::InvalidCurveParameterX)?;
let y: &[u8; 32] = y
.as_slice()
.try_into()
.map_err(|_| VerifierFromJwkError::InvalidCurveParameterY)?;
let point = sec1::EncodedPoint::from_affine_coordinates(x.into(), y.into(), false);
let key = ecdsa::VerifyingKey::from_encoded_point(&point)?;
Ok(Self::Es256K { key })
}
(JsonWebKeyParameters::Okp { crv: _, x: _ }, JsonWebSignatureAlg::EdDsa) => {
Err(VerifierFromJwkError::UnsupportedAlgorithm {
algorithm: JsonWebSignatureAlg::EdDsa,
})
}
(_, algorithm) => Err(VerifierFromJwkError::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::Verifier<GenericSignature> for Verifier {
fn verify(&self, msg: &[u8], signature: &GenericSignature) -> Result<(), signature::Error> {
match self {
Verifier::Hs256 { key } => {
let signature = crate::hmac::Signature::from_bytes(signature.as_bytes())?;
key.verify(msg, &signature)?;
Ok(())
}
Verifier::Hs384 { key } => {
let signature = crate::hmac::Signature::from_bytes(signature.as_bytes())?;
key.verify(msg, &signature)?;
Ok(())
}
Verifier::Hs512 { key } => {
let signature = crate::hmac::Signature::from_bytes(signature.as_bytes())?;
key.verify(msg, &signature)?;
Ok(())
}
Verifier::Rs256 { key } => {
let signature = rsa::pkcs1v15::Signature::from_bytes(signature.as_bytes())?;
key.verify(msg, &signature)?;
Ok(())
}
Verifier::Rs384 { key } => {
let signature = rsa::pkcs1v15::Signature::from_bytes(signature.as_bytes())?;
key.verify(msg, &signature)?;
Ok(())
}
Verifier::Rs512 { key } => {
let signature = rsa::pkcs1v15::Signature::from_bytes(signature.as_bytes())?;
key.verify(msg, &signature)?;
Ok(())
}
Verifier::Ps256 { key } => {
let signature = rsa::pss::Signature::from_bytes(signature.as_bytes())?;
key.verify(msg, &signature)?;
Ok(())
}
Verifier::Ps384 { key } => {
let signature = rsa::pss::Signature::from_bytes(signature.as_bytes())?;
key.verify(msg, &signature)?;
Ok(())
}
Verifier::Ps512 { key } => {
let signature = rsa::pss::Signature::from_bytes(signature.as_bytes())?;
key.verify(msg, &signature)?;
Ok(())
}
Verifier::Es256 { key } => {
let signature = ecdsa::Signature::from_bytes(signature.as_bytes())?;
key.verify(msg, &signature)?;
Ok(())
}
Verifier::Es384 { key } => {
let signature = ecdsa::Signature::from_bytes(signature.as_bytes())?;
key.verify(msg, &signature)?;
Ok(())
}
Verifier::Es256K { key } => {
let signature = ecdsa::Signature::from_bytes(signature.as_bytes())?;
key.verify(msg, &signature)?;
Ok(())
}
}
}
}