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

Add a dedicated keystore crate

This commit is contained in:
Quentin Gliech
2022-08-31 18:25:20 +02:00
parent 768c48234a
commit e1d50b818e
49 changed files with 1455 additions and 819 deletions

View File

@@ -16,7 +16,7 @@ use std::collections::HashSet;
use mas_iana::jose::{JsonWebKeyType, JsonWebKeyUse, JsonWebSignatureAlg};
use crate::JsonWebSignatureHeader;
use crate::jwt::JsonWebSignatureHeader;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Constraint<'a> {

View File

@@ -104,17 +104,7 @@ impl TryFrom<PrivateJsonWebKey> for PublicJsonWebKey {
type Error = SymetricKeyError;
fn try_from(value: PrivateJsonWebKey) -> Result<Self, Self::Error> {
Ok(Self {
parameters: value.parameters.try_into()?,
r#use: value.r#use,
key_ops: value.key_ops,
alg: value.alg,
kid: value.kid,
x5u: value.x5u,
x5c: value.x5c,
x5t: value.x5t,
x5t_s256: value.x5t_s256,
})
value.try_map(JsonWebKeyPublicParameters::try_from)
}
}
@@ -134,6 +124,74 @@ impl<P> JsonWebKey<P> {
}
}
pub fn try_map<M, O, E>(self, mapper: M) -> Result<JsonWebKey<O>, E>
where
M: FnOnce(P) -> Result<O, E>,
{
Ok(JsonWebKey {
parameters: mapper(self.parameters)?,
r#use: self.r#use,
key_ops: self.key_ops,
alg: self.alg,
kid: self.kid,
x5u: self.x5u,
x5c: self.x5c,
x5t: self.x5t,
x5t_s256: self.x5t_s256,
})
}
pub fn map<M, O>(self, mapper: M) -> JsonWebKey<O>
where
M: FnOnce(P) -> O,
{
JsonWebKey {
parameters: mapper(self.parameters),
r#use: self.r#use,
key_ops: self.key_ops,
alg: self.alg,
kid: self.kid,
x5u: self.x5u,
x5c: self.x5c,
x5t: self.x5t,
x5t_s256: self.x5t_s256,
}
}
pub fn try_cloned_map<M, O, E>(&self, mapper: M) -> Result<JsonWebKey<O>, E>
where
M: FnOnce(&P) -> Result<O, E>,
{
Ok(JsonWebKey {
parameters: mapper(&self.parameters)?,
r#use: self.r#use,
key_ops: self.key_ops.clone(),
alg: self.alg,
kid: self.kid.clone(),
x5u: self.x5u.clone(),
x5c: self.x5c.clone(),
x5t: self.x5t.clone(),
x5t_s256: self.x5t_s256.clone(),
})
}
pub fn cloned_map<M, O>(&self, mapper: M) -> JsonWebKey<O>
where
M: FnOnce(&P) -> O,
{
JsonWebKey {
parameters: mapper(&self.parameters),
r#use: self.r#use,
key_ops: self.key_ops.clone(),
alg: self.alg,
kid: self.kid.clone(),
x5u: self.x5u.clone(),
x5c: self.x5c.clone(),
x5t: self.x5t.clone(),
x5t_s256: self.x5t_s256.clone(),
}
}
#[must_use]
pub const fn with_use(mut self, value: JsonWebKeyUse) -> Self {
self.r#use = Some(value);
@@ -199,6 +257,14 @@ pub struct JsonWebKeySet<P> {
keys: Vec<JsonWebKey<P>>,
}
impl<P> Default for JsonWebKeySet<P> {
fn default() -> Self {
Self {
keys: Vec::default(),
}
}
}
pub type PublicJsonWebKeySet = JsonWebKeySet<self::public_parameters::JsonWebKeyPublicParameters>;
pub type PrivateJsonWebKeySet =
JsonWebKeySet<self::private_parameters::JsonWebKeyPrivateParameters>;
@@ -229,6 +295,13 @@ impl<P> JsonWebKeySet<P> {
}
}
impl<P> FromIterator<JsonWebKey<P>> for JsonWebKeySet<P> {
fn from_iter<T: IntoIterator<Item = JsonWebKey<P>>>(iter: T) -> Self {
let keys = iter.into_iter().collect();
Self { keys }
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -12,181 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::str::FromStr;
use base64ct::{Base64UrlUnpadded, Encoding};
use serde::{de::DeserializeOwned, Serialize};
use thiserror::Error;
use crate::{SigningKeystore, VerifyingKeystore};
mod header;
mod raw;
mod signed;
pub use self::{header::JsonWebSignatureHeader, signed::Jwt};
#[derive(Debug, PartialEq, Eq)]
pub struct JsonWebTokenParts {
payload: String,
signature: Vec<u8>,
}
#[derive(Error, Debug)]
#[error("failed to decode JWT")]
pub enum JwtPartsDecodeError {
#[error("no dots found in the JWT")]
NoDots,
#[error("could not decode signature")]
SignatureEncoding {
#[from]
inner: base64ct::Error,
},
}
impl FromStr for JsonWebTokenParts {
type Err = JwtPartsDecodeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (payload, signature) = s.rsplit_once('.').ok_or(JwtPartsDecodeError::NoDots)?;
let signature = Base64UrlUnpadded::decode_vec(signature)?;
let payload = payload.to_owned();
Ok(Self { payload, signature })
}
}
#[derive(Error, Debug)]
#[error("failed to serialize JWT")]
pub enum JwtSerializeError {
#[error("failed to serialize JWT header")]
Header {
#[source]
inner: serde_json::Error,
},
#[error("failed to serialize payload")]
Payload {
#[source]
inner: serde_json::Error,
},
}
#[derive(Error, Debug)]
#[error("failed to serialize JWT")]
pub enum JwtSignatureError {
Serialize {
#[from]
inner: JwtSerializeError,
},
Sign {
#[source]
inner: anyhow::Error,
},
}
pub struct DecodedJsonWebToken<T> {
header: JsonWebSignatureHeader,
payload: T,
}
impl<T> DecodedJsonWebToken<T>
where
T: Serialize,
{
fn serialize(&self) -> Result<String, JwtSerializeError> {
let header = serde_json::to_vec(&self.header)
.map_err(|inner| JwtSerializeError::Header { inner })?;
let header = Base64UrlUnpadded::encode_string(&header);
let payload = serde_json::to_vec(&self.payload)
.map_err(|inner| JwtSerializeError::Payload { inner })?;
let payload = Base64UrlUnpadded::encode_string(&payload);
Ok(format!("{}.{}", header, payload))
}
pub async fn sign<S: SigningKeystore>(
&self,
store: &S,
) -> Result<JsonWebTokenParts, JwtSignatureError> {
let payload = self.serialize()?;
let signature = store
.sign(&self.header, payload.as_bytes())
.await
.map_err(|inner| JwtSignatureError::Sign { inner })?;
Ok(JsonWebTokenParts { payload, signature })
}
}
impl<T> DecodedJsonWebToken<T> {
pub fn new(header: JsonWebSignatureHeader, payload: T) -> Self {
Self { header, payload }
}
pub fn claims(&self) -> &T {
&self.payload
}
pub fn header(&self) -> &JsonWebSignatureHeader {
&self.header
}
pub fn split(self) -> (JsonWebSignatureHeader, T) {
(self.header, self.payload)
}
}
impl<T> FromStr for DecodedJsonWebToken<T>
where
T: DeserializeOwned,
{
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (header, payload) = s
.split_once('.')
.ok_or_else(|| anyhow::anyhow!("invalid payload"))?;
let header = Base64UrlUnpadded::decode_vec(header)?;
let header = serde_json::from_slice(&header)?;
let payload = Base64UrlUnpadded::decode_vec(payload)?;
let payload = serde_json::from_slice(&payload)?;
Ok(Self { header, payload })
}
}
impl JsonWebTokenParts {
pub fn decode<T: DeserializeOwned>(&self) -> anyhow::Result<DecodedJsonWebToken<T>> {
let decoded = self.payload.parse()?;
Ok(decoded)
}
pub fn verify<S: VerifyingKeystore>(
&self,
header: &JsonWebSignatureHeader,
store: &S,
) -> S::Future {
store.verify(header, self.payload.as_bytes(), &self.signature)
}
pub async fn decode_and_verify<T: DeserializeOwned, S: VerifyingKeystore>(
&self,
store: &S,
) -> anyhow::Result<DecodedJsonWebToken<T>>
where
S::Error: std::error::Error + Send + Sync + 'static,
{
let decoded = self.decode()?;
self.verify(&decoded.header, store).await?;
Ok(decoded)
}
#[must_use]
pub fn serialize(&self) -> String {
let payload = &self.payload;
let signature = Base64UrlUnpadded::encode_string(&self.signature);
format!("{}.{}", payload, signature)
}
}
pub use self::{
header::JsonWebSignatureHeader,
signed::{Jwt, JwtDecodeError, JwtSignatureError, JwtVerificationError},
};

View File

@@ -1,21 +0,0 @@
// 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.
mod static_keystore;
mod traits;
pub use self::{
static_keystore::StaticKeystore,
traits::{SigningKeystore, VerifyingKeystore},
};

View File

@@ -1,413 +0,0 @@
// 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::{HashMap, HashSet},
future::Ready,
};
use anyhow::bail;
use async_trait::async_trait;
use base64ct::{Base64UrlUnpadded, Encoding};
use digest::Digest;
use ecdsa::{SigningKey, VerifyingKey};
use mas_iana::jose::{JsonWebKeyUse, JsonWebSignatureAlg};
use p256::{NistP256, PublicKey};
use pkcs1::{DecodeRsaPrivateKey, EncodeRsaPublicKey};
use pkcs8::{DecodePrivateKey, EncodePublicKey};
use rsa::{PublicKey as _, RsaPrivateKey, RsaPublicKey};
use sha2::{Sha256, Sha384, Sha512};
use signature::{Signature, Signer, Verifier};
use super::{SigningKeystore, VerifyingKeystore};
use crate::{
jwk::{JsonWebKey, PublicJsonWebKeySet},
JsonWebSignatureHeader,
};
// Generate with
// openssl genrsa 2048
const TEST_RSA_PKCS1_PEM: &str = "-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA1j7Y2CH6Ss8tgaNvcQPaRJKnCZD8ABqNPyKDWLQLph6Zi7gZ
GqmRtTzMuevo2ezpkbCiQAPEp1ms022P92bB+uqG7xmzHTzbwLtnq3OAdjmrnaFV
I4v89WHUsTXX9hiYOK5dOM81bNZ6muxWZ0L/xw4jVWe7xkqnp2Lluq0HknlzP5yJ
UEikf5BkpX0iyIu2/X4r8YVp8uzG34l/8qBx6k3rO2VkOQOSybZj1oij5KZCusnu
QjJLKWXCqJToWE6iVn+Q0N6ySDLgmJ7Zq0Sou/9N/oWKn94FOsouQgET5NuzoIFR
qTb321fQ8gbqt/OupBbBKEo1qUU+cS77TD/AuQIDAQABAoIBAQDLSZzmD+93lnf+
f36ZxOcRk/nNGPYUfx0xH+VzgHthJ73YFlozs1xflQ5JB/DM/4BsziZWCX1KsctM
XrRxMt6y4GAidcc/4eQ+T1RCGfl1tKkDi/bGIOloSGjRsV5208V0WvZ3lh2CZUy2
vbQKjUc3sFGUkzZYI7RLHosPA2mg78IVuSnqvNaU0TgA2KkaxWs6Ecr/ys80cUvj
KKj04DmX5xaXwUKmz353i5gIt3aY3G5CAw5fU/ocDKR8nzVCpBAGbRRiUaVKIT06
APSkLDTUnxSYtHtDJGHjgU/TsvAwTA92J3ue5Ysu9xTE+WyHA6Rgux7RQSD/wWHr
LdRPwxPFAoGBAOytMPh/f2zKmotanjho0QNfhAUHoQUfPudYT0nnDceOsi1jYWbQ
c/wPeQQC4Hp/pTUrkSIQPEz/hSxzZ6RPxxuGB8O94I0uLwQK4V1UwbgfsRa9zQzW
n0kgKZ8w8h8B7qyiKyIAnZzvKtNEnKrzrct4HsN3OEoXTwuAUYlvWtQTAoGBAOe8
0liNaH9V6ecZiojkRR1tiQkr/dCV+a13eXXaRA/8y/3wKCQ4idYncclQJTLKsAwW
hHuDd4uLgtifREVIBD2jGdlznNr9HQNuZgwjuUoH+r1YLGgiMWeVYSr0m8lyDlQl
BJKTAphrqo6VJWDAnM18v+by//yRleSjVMqZ3zmDAoGBAMpA0rl5EyagGON/g/hG
sl8Ej+hQdazP38yJbfCEsATaD6+z3rei6Yr8mfjwkG5+iGrgmT0XzMAsF909ndMP
jeIabqY6rBtZ3TnCJobAeG9lPctmVUVkX2h5QLhWdoJC/3iteNis2AQVam5yksOQ
S/O16ew2BHdkZds5Q/SDoYXbAoGAK9tVZ8LjWu30hXMU/9FLr0USoTS9JWOszAKH
byFuriPmq1lvD2PP2kK+yx2q3JD1fmQokIOR9Uvi6IJD1mTJwKyEcN3reppailKz
Z2q/X15hOsJcLR0DgpoHuKxwa1B1m8Ehu2etHxGJRtC9MTFiu5T3cIrenXskBhBP
NMSoNWcCgYAD3u3zdeVo3gVoxneS7GNVI2WBhjtqgNIbINuxGZvfztm7+vNPE6sQ
VL8i+09uoM1H6sXbe2XXORmtW0j/6MmYhSoBXNdqWTNAiyNRhwEQtowqgl5R7PBu
//QZTF1z62R9IKDMRG3f5Wn8e1Dys6tXBuG603g+Dkkc/km476mrgw==
-----END RSA PRIVATE KEY-----";
// Generate with
// openssl ecparam -genkey -name prime256v1 | openssl pkcs8 -topk8 -nocrypt
const TEST_ECDSA_PKCS8_PEM: &str = "-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg+tHxet7G+uar2Cef
iYPb7jv3uzncFtwJ7RhDOvEA0fChRANCAATCKn2AEqa9785k+TmwkeCvLub8XGrF
ezE6bA/blaPVE3nu4SUVYKULRJQxNjeOSra8TQrlIS8e5ItbMn8Tv9KV
-----END PRIVATE KEY-----";
#[derive(Default)]
pub struct StaticKeystore {
rsa_keys: HashMap<String, rsa::RsaPrivateKey>,
es256_keys: HashMap<String, SigningKey<NistP256>>,
}
impl StaticKeystore {
#[must_use]
pub fn new() -> Self {
StaticKeystore::default()
}
pub fn add_test_rsa_key(&mut self) -> anyhow::Result<()> {
let rsa = RsaPrivateKey::from_pkcs1_pem(TEST_RSA_PKCS1_PEM)?;
self.add_rsa_key(rsa)?;
Ok(())
}
pub fn add_test_ecdsa_key(&mut self) -> anyhow::Result<()> {
let ecdsa = SigningKey::from_pkcs8_pem(TEST_ECDSA_PKCS8_PEM)?;
self.add_ecdsa_key(ecdsa)?;
Ok(())
}
pub fn add_rsa_key(&mut self, key: rsa::RsaPrivateKey) -> anyhow::Result<()> {
let pubkey: &RsaPublicKey = &key;
let der = pubkey.to_pkcs1_der()?;
let digest = {
let mut digest = Sha256::new();
digest.update(&der);
digest.finalize()
};
// Truncate the digest to the 120 first bits
let digest = &digest[0..15];
let digest = Base64UrlUnpadded::encode_string(digest);
let kid = format!("rsa-{}", digest);
self.rsa_keys.insert(kid, key);
Ok(())
}
pub fn add_ecdsa_key(&mut self, key: SigningKey<NistP256>) -> anyhow::Result<()> {
let pubkey: PublicKey = key.verifying_key().into();
let der = EncodePublicKey::to_public_key_der(&pubkey)?;
let digest = {
let mut digest = Sha256::new();
digest.update(&der);
digest.finalize()
};
// Truncate the digest to the 120 first bits
let digest = &digest[0..15];
let digest = Base64UrlUnpadded::encode_string(digest);
let kid = format!("ec-{}", digest);
self.es256_keys.insert(kid, key);
Ok(())
}
#[must_use]
pub fn to_public_jwks(&self) -> PublicJsonWebKeySet {
let rsa = self.rsa_keys.iter().map(|(kid, key)| {
let pubkey = RsaPublicKey::from(key);
JsonWebKey::new(pubkey.into())
.with_kid(kid)
.with_use(JsonWebKeyUse::Sig)
});
let es256 = self.es256_keys.iter().map(|(kid, key)| {
let pubkey = ecdsa::VerifyingKey::from(key);
JsonWebKey::new(pubkey.into())
.with_kid(kid)
.with_use(JsonWebKeyUse::Sig)
.with_alg(JsonWebSignatureAlg::Es256)
});
let keys = rsa.chain(es256).collect();
PublicJsonWebKeySet::new(keys)
}
fn verify_sync(
&self,
header: &JsonWebSignatureHeader,
payload: &[u8],
signature: &[u8],
) -> anyhow::Result<()> {
let kid = header
.kid()
.ok_or_else(|| anyhow::anyhow!("missing kid claim in JWT header"))?;
// TODO: do the verification in a blocking task
match header.alg() {
JsonWebSignatureAlg::Rs256 => {
let key = self
.rsa_keys
.get(kid)
.ok_or_else(|| anyhow::anyhow!("could not find RSA key in key store"))?;
let pubkey = rsa::RsaPublicKey::from(key);
let digest = {
let mut digest = Sha256::new();
digest.update(&payload);
digest.finalize()
};
pubkey.verify(
rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_256)),
&digest,
signature,
)?;
}
JsonWebSignatureAlg::Rs384 => {
let key = self
.rsa_keys
.get(kid)
.ok_or_else(|| anyhow::anyhow!("could not find RSA key in key store"))?;
let pubkey = rsa::RsaPublicKey::from(key);
let digest = {
let mut digest = Sha384::new();
digest.update(&payload);
digest.finalize()
};
pubkey.verify(
rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_384)),
&digest,
signature,
)?;
}
JsonWebSignatureAlg::Rs512 => {
let key = self
.rsa_keys
.get(kid)
.ok_or_else(|| anyhow::anyhow!("could not find RSA key in key store"))?;
let pubkey = rsa::RsaPublicKey::from(key);
let digest = {
let mut digest = Sha512::new();
digest.update(&payload);
digest.finalize()
};
pubkey.verify(
rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_512)),
&digest,
signature,
)?;
}
JsonWebSignatureAlg::Es256 => {
let key = self
.es256_keys
.get(kid)
.ok_or_else(|| anyhow::anyhow!("could not find ECDSA key in key store"))?;
let pubkey = VerifyingKey::from(key);
let signature = ecdsa::Signature::from_bytes(signature)?;
pubkey.verify(payload, &signature)?;
}
_ => bail!("unsupported algorithm"),
}
Ok(())
}
}
#[async_trait]
impl SigningKeystore for StaticKeystore {
fn supported_algorithms(&self) -> HashSet<JsonWebSignatureAlg> {
let has_rsa = !self.rsa_keys.is_empty();
let has_es256 = !self.es256_keys.is_empty();
let capacity = (if has_rsa { 3 } else { 0 }) + (if has_es256 { 1 } else { 0 });
let mut algorithms = HashSet::with_capacity(capacity);
if has_rsa {
algorithms.insert(JsonWebSignatureAlg::Rs256);
algorithms.insert(JsonWebSignatureAlg::Rs384);
algorithms.insert(JsonWebSignatureAlg::Rs512);
}
if has_es256 {
algorithms.insert(JsonWebSignatureAlg::Es256);
}
algorithms
}
async fn prepare_header(
&self,
alg: JsonWebSignatureAlg,
) -> anyhow::Result<JsonWebSignatureHeader> {
let header = JsonWebSignatureHeader::new(alg);
let kid = match alg {
JsonWebSignatureAlg::Rs256
| JsonWebSignatureAlg::Rs384
| JsonWebSignatureAlg::Rs512 => self
.rsa_keys
.keys()
.next()
.ok_or_else(|| anyhow::anyhow!("no RSA keys in keystore"))?,
JsonWebSignatureAlg::Es256 => self
.es256_keys
.keys()
.next()
.ok_or_else(|| anyhow::anyhow!("no ECDSA keys in keystore"))?,
_ => bail!("unsupported algorithm"),
};
Ok(header.with_kid(kid))
}
async fn sign(&self, header: &JsonWebSignatureHeader, msg: &[u8]) -> anyhow::Result<Vec<u8>> {
let kid = header
.kid()
.ok_or_else(|| anyhow::anyhow!("missing kid from the JWT header"))?;
// TODO: do the signing in a blocking task
let signature = match header.alg() {
JsonWebSignatureAlg::Rs256 => {
let key = self
.rsa_keys
.get(kid)
.ok_or_else(|| anyhow::anyhow!("RSA key not found in key store"))?;
let digest = {
let mut digest = Sha256::new();
digest.update(&msg);
digest.finalize()
};
key.sign(
rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_256)),
&digest,
)?
}
JsonWebSignatureAlg::Rs384 => {
let key = self
.rsa_keys
.get(kid)
.ok_or_else(|| anyhow::anyhow!("RSA key not found in key store"))?;
let digest = {
let mut digest = Sha384::new();
digest.update(&msg);
digest.finalize()
};
key.sign(
rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_384)),
&digest,
)?
}
JsonWebSignatureAlg::Rs512 => {
let key = self
.rsa_keys
.get(kid)
.ok_or_else(|| anyhow::anyhow!("RSA key not found in key store"))?;
let digest = {
let mut digest = Sha512::new();
digest.update(&msg);
digest.finalize()
};
key.sign(
rsa::PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_512)),
&digest,
)?
}
JsonWebSignatureAlg::Es256 => {
let key = self
.es256_keys
.get(kid)
.ok_or_else(|| anyhow::anyhow!("ECDSA key not found in key store"))?;
let signature = key.try_sign(msg)?;
let signature: &[u8] = signature.as_ref();
signature.to_vec()
}
_ => bail!("Unsupported algorithm"),
};
Ok(signature)
}
}
impl VerifyingKeystore for StaticKeystore {
type Error = anyhow::Error;
type Future = Ready<Result<(), Self::Error>>;
fn verify(
&self,
header: &JsonWebSignatureHeader,
msg: &[u8],
signature: &[u8],
) -> Self::Future {
std::future::ready(self.verify_sync(header, msg, signature))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_static_store() {
let message = "this is the message to sign".as_bytes();
let store = {
let mut s = StaticKeystore::new();
s.add_test_rsa_key().unwrap();
s.add_test_ecdsa_key().unwrap();
s
};
for alg in [
JsonWebSignatureAlg::Rs256,
JsonWebSignatureAlg::Rs384,
JsonWebSignatureAlg::Rs512,
JsonWebSignatureAlg::Es256,
] {
let header = store.prepare_header(alg).await.unwrap();
assert_eq!(header.alg(), alg);
let signature = store.sign(&header, message).await.unwrap();
store.verify(&header, message, &signature).await.unwrap();
}
}
}

View File

@@ -1,40 +0,0 @@
// 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, future::Future};
use async_trait::async_trait;
use mas_iana::jose::JsonWebSignatureAlg;
use crate::JsonWebSignatureHeader;
#[async_trait]
pub trait SigningKeystore {
fn supported_algorithms(&self) -> HashSet<JsonWebSignatureAlg>;
async fn prepare_header(
&self,
alg: JsonWebSignatureAlg,
) -> anyhow::Result<JsonWebSignatureHeader>;
async fn sign(&self, header: &JsonWebSignatureHeader, msg: &[u8]) -> anyhow::Result<Vec<u8>>;
}
pub trait VerifyingKeystore {
type Error;
type Future: Future<Output = Result<(), Self::Error>>;
fn verify(&self, header: &JsonWebSignatureHeader, msg: &[u8], signature: &[u8])
-> Self::Future;
}

View File

@@ -19,14 +19,8 @@
pub mod claims;
pub mod constraints;
pub(crate) mod jwa;
pub mod jwa;
pub mod jwk;
pub(crate) mod jwt;
mod keystore;
pub mod jwt;
pub mod signer;
pub mod verifier;
pub use self::{
jwt::{DecodedJsonWebToken, JsonWebSignatureHeader, JsonWebTokenParts, Jwt, JwtSignatureError},
keystore::{SigningKeystore, StaticKeystore, VerifyingKeystore},
};

View File

@@ -36,6 +36,78 @@ pub enum Signer {
Es256K { key: jwa::Es256KSigningKey },
}
impl From<jwa::Hs256Key> for Signer {
fn from(key: jwa::Hs256Key) -> Self {
Self::Hs256 { key }
}
}
impl From<jwa::Hs384Key> for Signer {
fn from(key: jwa::Hs384Key) -> Self {
Self::Hs384 { key }
}
}
impl From<jwa::Hs512Key> for Signer {
fn from(key: jwa::Hs512Key) -> Self {
Self::Hs512 { key }
}
}
impl From<jwa::Rs256SigningKey> for Signer {
fn from(key: jwa::Rs256SigningKey) -> Self {
Self::Rs256 { key }
}
}
impl From<jwa::Rs384SigningKey> for Signer {
fn from(key: jwa::Rs384SigningKey) -> Self {
Self::Rs384 { key }
}
}
impl From<jwa::Rs512SigningKey> for Signer {
fn from(key: jwa::Rs512SigningKey) -> Self {
Self::Rs512 { key }
}
}
impl From<jwa::Ps256SigningKey> for Signer {
fn from(key: jwa::Ps256SigningKey) -> Self {
Self::Ps256 { key }
}
}
impl From<jwa::Ps384SigningKey> for Signer {
fn from(key: jwa::Ps384SigningKey) -> Self {
Self::Ps384 { key }
}
}
impl From<jwa::Ps512SigningKey> for Signer {
fn from(key: jwa::Ps512SigningKey) -> Self {
Self::Ps512 { key }
}
}
impl From<jwa::Es256SigningKey> for Signer {
fn from(key: jwa::Es256SigningKey) -> Self {
Self::Es256 { key }
}
}
impl From<jwa::Es384SigningKey> for Signer {
fn from(key: jwa::Es384SigningKey) -> Self {
Self::Es384 { key }
}
}
impl From<jwa::Es256KSigningKey> for Signer {
fn from(key: jwa::Es256KSigningKey) -> Self {
Self::Es256K { key }
}
}
#[derive(Debug, Error)]
pub enum SignerFromJwkError {
#[error("invalid RSA key")]

View File

@@ -36,6 +36,78 @@ pub enum Verifier {
Es256K { key: jwa::Es256KVerifyingKey },
}
impl From<jwa::Hs256Key> for Verifier {
fn from(key: jwa::Hs256Key) -> Self {
Self::Hs256 { key }
}
}
impl From<jwa::Hs384Key> for Verifier {
fn from(key: jwa::Hs384Key) -> Self {
Self::Hs384 { key }
}
}
impl From<jwa::Hs512Key> for Verifier {
fn from(key: jwa::Hs512Key) -> Self {
Self::Hs512 { key }
}
}
impl From<jwa::Rs256VerifyingKey> for Verifier {
fn from(key: jwa::Rs256VerifyingKey) -> Self {
Self::Rs256 { key }
}
}
impl From<jwa::Rs384VerifyingKey> for Verifier {
fn from(key: jwa::Rs384VerifyingKey) -> Self {
Self::Rs384 { key }
}
}
impl From<jwa::Rs512VerifyingKey> for Verifier {
fn from(key: jwa::Rs512VerifyingKey) -> Self {
Self::Rs512 { key }
}
}
impl From<jwa::Ps256VerifyingKey> for Verifier {
fn from(key: jwa::Ps256VerifyingKey) -> Self {
Self::Ps256 { key }
}
}
impl From<jwa::Ps384VerifyingKey> for Verifier {
fn from(key: jwa::Ps384VerifyingKey) -> Self {
Self::Ps384 { key }
}
}
impl From<jwa::Ps512VerifyingKey> for Verifier {
fn from(key: jwa::Ps512VerifyingKey) -> Self {
Self::Ps512 { key }
}
}
impl From<jwa::Es256VerifyingKey> for Verifier {
fn from(key: jwa::Es256VerifyingKey) -> Self {
Self::Es256 { key }
}
}
impl From<jwa::Es384VerifyingKey> for Verifier {
fn from(key: jwa::Es384VerifyingKey) -> Self {
Self::Es384 { key }
}
}
impl From<jwa::Es256KVerifyingKey> for Verifier {
fn from(key: jwa::Es256KVerifyingKey) -> Self {
Self::Es256K { key }
}
}
#[derive(Debug, Error)]
pub enum VerifierFromJwkError {
#[error("invalid RSA key")]