You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-29 22:01:14 +03:00
Sign all the things
This commit is contained in:
@ -6,10 +6,9 @@ edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
async-signature = "0.2.0"
|
||||
anyhow = "1.0.62"
|
||||
async-trait = "0.1.57"
|
||||
base64ct = { version = "1.5.1", features = ["std"] }
|
||||
base64ct = { version = "1.5.2", features = ["std"] }
|
||||
chrono = { version = "0.4.22", features = ["serde"] }
|
||||
crypto-mac = { version = "0.11.1", features = ["std"] }
|
||||
digest = "0.10.3"
|
||||
@ -18,11 +17,13 @@ elliptic-curve = { version = "0.12.3", features = ["ecdh", "pem"] }
|
||||
futures-util = "0.3.23"
|
||||
hmac = "0.12.1"
|
||||
http = "0.2.8"
|
||||
k256 = { version = "0.11.2", features = ["ecdsa", "pem", "pkcs8"] }
|
||||
p256 = { version = "0.11.1", features = ["ecdsa", "pem", "pkcs8"] }
|
||||
p384 = { version = "0.11.2", features = ["ecdsa", "pem", "pkcs8"] }
|
||||
pkcs1 = { version = "0.4.0", features = ["pem", "pkcs8"] }
|
||||
pkcs8 = { version = "0.9.0", features = ["pem", "std"] }
|
||||
rand = "0.8.5"
|
||||
rsa = { git = "https://github.com/RustCrypto/RSA.git" }
|
||||
rsa = { git = "https://github.com/RustCrypto/RSA.git", features = ["std"] }
|
||||
schemars = "0.8.10"
|
||||
sec1 = "0.3.0"
|
||||
serde = { version = "1.0.144", features = ["derive"] }
|
||||
@ -37,6 +38,3 @@ tracing = "0.1.36"
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
|
||||
mas-iana = { path = "../iana" }
|
||||
|
||||
[dev-dependencies]
|
||||
mas-http = { path = "../http" }
|
||||
|
280
crates/jose/src/constraints.rs
Normal file
280
crates/jose/src/constraints.rs
Normal 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
109
crates/jose/src/hmac.rs
Normal 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(())
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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>,
|
||||
|
@ -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 {
|
||||
|
@ -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
146
crates/jose/src/rsa.rs
Normal 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
330
crates/jose/src/verifier.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
138
crates/jose/tests/generate.py
Normal file
138
crates/jose/tests/generate.py
Normal file
@ -0,0 +1,138 @@
|
||||
# Generates test keys, JWKS and JWTs
|
||||
# Required the `openssl` binary and the `authlib` python library
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from authlib.jose import JsonWebKey, JsonWebSignature, KeySet
|
||||
|
||||
output_path = Path(__file__).parent
|
||||
|
||||
keys_path = output_path / "keys"
|
||||
keys_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
jwts_path = output_path / "jwts"
|
||||
jwts_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def gen_key(
|
||||
name: str,
|
||||
priv_command: List[str],
|
||||
pub_command: List[str],
|
||||
):
|
||||
"""Generate a keypair
|
||||
|
||||
Args:
|
||||
name: Name
|
||||
priv_command: Command to generate the private key. This must write the
|
||||
key to stdout.
|
||||
pub_command: Command to convert the private key to a public one. This
|
||||
must read the private key from stdin and write the public key to
|
||||
stdout.
|
||||
"""
|
||||
priv_path = keys_path / f"{name}.priv.pem"
|
||||
pub_path = keys_path / f"{name}.pub.pem"
|
||||
|
||||
with open(priv_path, "wb") as f:
|
||||
subprocess.run(priv_command, stdout=f, stderr=subprocess.DEVNULL)
|
||||
|
||||
with open(priv_path, "rb") as priv, open(pub_path, "wb") as pub:
|
||||
subprocess.run(pub_command, stdin=priv, stdout=pub, stderr=subprocess.DEVNULL)
|
||||
|
||||
|
||||
def import_key(name: str, kty: str) -> JsonWebKey:
|
||||
"""Import a key from a file"""
|
||||
with open(keys_path / name, "r") as f:
|
||||
pem = f.read()
|
||||
return JsonWebKey.import_key(pem, {"kty": kty})
|
||||
|
||||
|
||||
def sign_jwt(alg: str, filename: str, key: JsonWebKey):
|
||||
"""Sign a JWT for the given key"""
|
||||
path = jwts_path / filename
|
||||
protected = {"alg": alg, "kid": key.thumbprint()}
|
||||
payload = '{"hello":"world"}'
|
||||
jws = JsonWebSignature(algorithms=[alg])
|
||||
jwt = jws.serialize_compact(protected, payload, key)
|
||||
with open(path, "wb") as f:
|
||||
f.write(jwt)
|
||||
|
||||
|
||||
with open(keys_path / "oct.bin", "wb") as f:
|
||||
subprocess.run(
|
||||
["openssl", "rand", "-hex", "64"], stdout=f, stderr=subprocess.DEVNULL
|
||||
)
|
||||
|
||||
gen_key("rsa", ["openssl", "genrsa", "2048"], ["openssl", "rsa", "-pubout"])
|
||||
gen_key(
|
||||
"p256",
|
||||
["openssl", "ecparam", "-genkey", "-name", "prime256v1"],
|
||||
["openssl", "ec", "-pubout"],
|
||||
)
|
||||
gen_key(
|
||||
"p384",
|
||||
["openssl", "ecparam", "-genkey", "-name", "secp384r1"],
|
||||
["openssl", "ec", "-pubout"],
|
||||
)
|
||||
gen_key(
|
||||
"p521",
|
||||
["openssl", "ecparam", "-genkey", "-name", "secp521r1"],
|
||||
["openssl", "ec", "-pubout"],
|
||||
)
|
||||
gen_key(
|
||||
"k256",
|
||||
["openssl", "ecparam", "-genkey", "-name", "secp256k1"],
|
||||
["openssl", "ec", "-pubout"],
|
||||
)
|
||||
gen_key(
|
||||
"ed25519",
|
||||
["openssl", "genpkey", "-algorithm", "ed25519"],
|
||||
["openssl", "pkey", "-pubout"],
|
||||
)
|
||||
gen_key(
|
||||
"ed448",
|
||||
["openssl", "genpkey", "-algorithm", "ed448"],
|
||||
["openssl", "pkey", "-pubout"],
|
||||
)
|
||||
|
||||
oct_key = import_key("oct.bin", "oct")
|
||||
rsa_key = import_key("rsa.priv.pem", "RSA")
|
||||
p256_key = import_key("p256.priv.pem", "EC")
|
||||
p384_key = import_key("p384.priv.pem", "EC")
|
||||
p521_key = import_key("p521.priv.pem", "EC")
|
||||
k256_key = import_key("k256.priv.pem", "EC")
|
||||
ed25519_key = import_key("ed25519.priv.pem", "OKP")
|
||||
ed448_key = import_key("ed448.priv.pem", "OKP")
|
||||
|
||||
key_set = KeySet(
|
||||
[rsa_key, p256_key, p384_key, p521_key, k256_key, ed25519_key, ed448_key]
|
||||
)
|
||||
|
||||
with open(keys_path / "jwks.pub.json", "w", encoding="utf8") as f:
|
||||
json.dump(key_set.as_dict(is_private=False), f, indent=2, sort_keys=True)
|
||||
|
||||
key_set.keys.insert(0, oct_key)
|
||||
|
||||
with open(keys_path / "jwks.priv.json", "w", encoding="utf8") as f:
|
||||
json.dump(key_set.as_dict(is_private=True), f, indent=2, sort_keys=True)
|
||||
|
||||
sign_jwt("HS256", "hs256.jwt", oct_key)
|
||||
sign_jwt("HS384", "hs384.jwt", oct_key)
|
||||
sign_jwt("HS512", "hs512.jwt", oct_key)
|
||||
sign_jwt("RS256", "rs256.jwt", rsa_key)
|
||||
sign_jwt("RS384", "rs384.jwt", rsa_key)
|
||||
sign_jwt("RS512", "rs512.jwt", rsa_key)
|
||||
sign_jwt("RS256", "rs256.jwt", rsa_key)
|
||||
sign_jwt("RS384", "rs384.jwt", rsa_key)
|
||||
sign_jwt("RS512", "rs512.jwt", rsa_key)
|
||||
sign_jwt("PS256", "ps256.jwt", rsa_key)
|
||||
sign_jwt("PS384", "ps384.jwt", rsa_key)
|
||||
sign_jwt("PS512", "ps512.jwt", rsa_key)
|
||||
sign_jwt("ES256", "es256.jwt", p256_key)
|
||||
sign_jwt("ES384", "es384.jwt", p384_key)
|
||||
sign_jwt("ES512", "es512.jwt", p521_key)
|
||||
sign_jwt("ES256K", "es256k.jwt", k256_key)
|
||||
sign_jwt("EdDSA", "eddsa-ed25519.jwt", ed25519_key)
|
||||
sign_jwt("EdDSA", "eddsa-ed448.jwt", ed448_key)
|
107
crates/jose/tests/jws.rs
Normal file
107
crates/jose/tests/jws.rs
Normal file
@ -0,0 +1,107 @@
|
||||
// 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::ops::Deref;
|
||||
|
||||
use mas_jose::{constraints::ConstraintSet, JsonWebKeySet, 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");
|
||||
static RS256_JWT: &str = include_str!("./jwts/rs256.jwt");
|
||||
static RS384_JWT: &str = include_str!("./jwts/rs384.jwt");
|
||||
static RS512_JWT: &str = include_str!("./jwts/rs512.jwt");
|
||||
static PS256_JWT: &str = include_str!("./jwts/ps256.jwt");
|
||||
static PS384_JWT: &str = include_str!("./jwts/ps384.jwt");
|
||||
static PS512_JWT: &str = include_str!("./jwts/ps512.jwt");
|
||||
static ES256_JWT: &str = include_str!("./jwts/es256.jwt");
|
||||
static ES384_JWT: &str = include_str!("./jwts/es384.jwt");
|
||||
static ES512_JWT: &str = include_str!("./jwts/es512.jwt");
|
||||
static ES256K_JWT: &str = include_str!("./jwts/es256k.jwt");
|
||||
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() -> JsonWebKeySet {
|
||||
serde_json::from_str(include_str!("./keys/jwks.pub.json")).unwrap()
|
||||
}
|
||||
|
||||
fn oct_key() -> Vec<u8> {
|
||||
OCT_KEY.to_vec()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Payload {
|
||||
hello: String,
|
||||
}
|
||||
|
||||
macro_rules! asymetric_jwt_test {
|
||||
($test_name:ident, $jwt:ident) => {
|
||||
asymetric_jwt_test!($test_name, $jwt, verify = true);
|
||||
};
|
||||
($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");
|
||||
|
||||
let constraints = ConstraintSet::from(jwt.header());
|
||||
let candidates = constraints.filter(jwks.deref());
|
||||
assert_eq!(candidates.len(), 1);
|
||||
let candidate = candidates[0];
|
||||
|
||||
if $verify {
|
||||
let verifier = mas_jose::verifier::Verifier::for_jwk_and_alg(
|
||||
candidate.params(),
|
||||
jwt.header().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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
symetric_jwt_test!(test_hs256, HS256_JWT);
|
||||
symetric_jwt_test!(test_hs384, HS384_JWT);
|
||||
symetric_jwt_test!(test_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);
|
1
crates/jose/tests/jwts/eddsa-ed25519.jwt
Normal file
1
crates/jose/tests/jwts/eddsa-ed25519.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJFZERTQSIsImtpZCI6ImlYa2l5aEVoNkU3VS1hWDBmZzd3LWVzSFdxUHZ2eFdkNmdIMUpHMnU3TjAifQ.eyJoZWxsbyI6IndvcmxkIn0.ZFiNWsheqUC_mQNztHpZXLnyb5LtvyT1dTGcMSCgG97Cobju83xCIkbJwfjOSgZrI2CpEVobVM_mfnmFIAUfBg
|
1
crates/jose/tests/jwts/eddsa-ed448.jwt
Normal file
1
crates/jose/tests/jwts/eddsa-ed448.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJFZERTQSIsImtpZCI6IlFsdGEycVZsaEhoZzNqcmlKcDBIc0lCUXFHVkIxWkgycEVueVBIemwxTXMifQ.eyJoZWxsbyI6IndvcmxkIn0.7EqBc73c8UjbZnW5LkkDmPlAnlgjVdDzfABvssoLE3FoFX3uUr1dPdX3I9Hu_rtOIdRtTLfN9eeABuG5cugUoshrYSFuHF6vy2Nim7uM3GWa6mVZx6fzOBq6goCK4JpNfwkJ3a4VyslHU7wQBfXAOxcA
|
1
crates/jose/tests/jwts/es256.jwt
Normal file
1
crates/jose/tests/jwts/es256.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJFUzI1NiIsImtpZCI6ImxNYlNJNjlhanNCSEhrSXBWQUZLUktZblI2NmtHZEd0ZWcyb3FNenAwX0UifQ.eyJoZWxsbyI6IndvcmxkIn0.YckCGhpak2hpO9EiR-X2MD6CVBnUAmQbRVKvKoYCbRnydOOksNlzWaOl0S-C4KZxGTuKG-spzFQJov5h_ob5nw
|
1
crates/jose/tests/jwts/es256k.jwt
Normal file
1
crates/jose/tests/jwts/es256k.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJFUzI1NksiLCJraWQiOiJuOWI0Z3lkNU5nSHY3cEo3UzI3QUtCcmhCUEhhM0g1cHRjaXhtWWVyU1VnIn0.eyJoZWxsbyI6IndvcmxkIn0.e0XIMec0_gvlxS8je5hVpYQGls2A5r2TUJ9eJNmdwZQbo1alRB93dgbh3yd4fh8bDOmmLhRfMKti93c7-ljPVg
|
1
crates/jose/tests/jwts/es384.jwt
Normal file
1
crates/jose/tests/jwts/es384.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJFUzM4NCIsImtpZCI6IkoxRVpKR1AxTHloWDJabHo4eFBjc3BNUUVsOFczYllHMngzTnFpTWJQeVkifQ.eyJoZWxsbyI6IndvcmxkIn0.XK3AIs0TQ1r5Wbpd14MkVIp3rvisQEb_8wlp3F4usveL23GH15y5TQ8mcU5NrxNFFylclwikyz4ozM2zmU7fkCYfjKD8AoEABOTlfjH3DRQnynVcpkvB47CsSgt8QpGe
|
1
crates/jose/tests/jwts/es512.jwt
Normal file
1
crates/jose/tests/jwts/es512.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJFUzUxMiIsImtpZCI6Il94R3lJM21zOTBBdmdGNjU4d3o5NzF3c3dTeVluR1NHX0EwZEFnbXJBTTAifQ.eyJoZWxsbyI6IndvcmxkIn0.AJ9YcP56d-1Z1wsZL0ikFRY_4Q6du7YEWsqtQDOloCLMYQ-3citw6Fm35t4kg8E5aoe8QrEj8kTqsQLloWv0eBMFAWh-Uyrupmz0Kzllc6xbOEVoWuM5DWc6AJ6Da6k0f6XHsZ_MVcayQpdmZTLcM_pyo1U6olqwLYqv1YNx-2M2GdCl
|
1
crates/jose/tests/jwts/hs256.jwt
Normal file
1
crates/jose/tests/jwts/hs256.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJIUzI1NiIsImtpZCI6ImRqSEtvV1Uzck9sV2c1RTBFSV80RmxiRVRmZDRPRlFnVjk4REZYRW1HZmcifQ.eyJoZWxsbyI6IndvcmxkIn0.GBxkJdc15D26siv1Ov_a2jgQSIsgLwiF2ZDFSUdzoFY
|
1
crates/jose/tests/jwts/hs384.jwt
Normal file
1
crates/jose/tests/jwts/hs384.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJIUzM4NCIsImtpZCI6ImRqSEtvV1Uzck9sV2c1RTBFSV80RmxiRVRmZDRPRlFnVjk4REZYRW1HZmcifQ.eyJoZWxsbyI6IndvcmxkIn0.pOZkiI4HMCNHgUf9diq6CkFxsMIMCNADvDPHmtkjerSYWy16dmlZy-FT9ZxyyD_1
|
1
crates/jose/tests/jwts/hs512.jwt
Normal file
1
crates/jose/tests/jwts/hs512.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJIUzUxMiIsImtpZCI6ImRqSEtvV1Uzck9sV2c1RTBFSV80RmxiRVRmZDRPRlFnVjk4REZYRW1HZmcifQ.eyJoZWxsbyI6IndvcmxkIn0.1kVwcE7LajF4Ph3yl2cKhJRs4FtZUMT6mxVCbtfttLPLqkxX-WAlZ0Hd7zg1JAzxNUmkeF8bsgZ9P0bPxBDSyw
|
1
crates/jose/tests/jwts/ps256.jwt
Normal file
1
crates/jose/tests/jwts/ps256.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJQUzI1NiIsImtpZCI6ImxqQXdGc1czMmV4cHlBMFJqcktvT0h1WnhmazdLTFNlajh6bGRPOXo0aVUifQ.eyJoZWxsbyI6IndvcmxkIn0.JWY1HZhLrDngEV7-V7to489hsX3muDOeCr4cedGUY2cpDNgJs0CgTe1pknXws9msZSlG4C-oA08UqgousBA2FWbcuVDhSEmSyNWM2rHekFuYcLlAupP8wucMQ3yzP425V2PzlgWV85xRe18PifNaTldMHLArbTKplMQgHHHopz28kuP1Uko99lHxpZrDVMHSLXNTyYaoQeOd81Hbx8uSx5wZO6tVIErV1RhKhSFGLP9DsbOKKW6jRgam_tKNh35VYBQZ6CIQkgsZCruDP7KFHHqC4xHTbkNQ6VlxHHHOpHz-SuRcBS901EN6NVCSPRSc0oYp1ChQCPgUeH_SrloCMg
|
1
crates/jose/tests/jwts/ps384.jwt
Normal file
1
crates/jose/tests/jwts/ps384.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJQUzM4NCIsImtpZCI6ImxqQXdGc1czMmV4cHlBMFJqcktvT0h1WnhmazdLTFNlajh6bGRPOXo0aVUifQ.eyJoZWxsbyI6IndvcmxkIn0.XLe8Fxg1wALfGIYBtGtYCSxneiReNMRsUiXukYPS3KWvIH6xcLV93GflNRBHRE1aijy1GPnqZv-mZoKjfZr4PoZMX0MalE0j0bFqrLJvfoyxlZLTIzjfyYm81JtPwlB3iU3DvqKGAYBE8aknTOnv65nyprdhGuJhFEW-_7omDzXqE03DofIGQOu-F3nkVP5Om28VKY6Vdr7PswJhKawP97VXrhN5aIubSjldv5-LcKlVwjV9_3RTiEbVGCgluyhzUUhoa-y0Y1oplJC4GMzvQ1YCYQeYJOn0bB1FjpOryJ2mxlIf8qNzlDHnpyr5MVRJ2PAlhZ31GB5JGr_ZQYTRUA
|
1
crates/jose/tests/jwts/ps512.jwt
Normal file
1
crates/jose/tests/jwts/ps512.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJQUzUxMiIsImtpZCI6ImxqQXdGc1czMmV4cHlBMFJqcktvT0h1WnhmazdLTFNlajh6bGRPOXo0aVUifQ.eyJoZWxsbyI6IndvcmxkIn0.aQ6sXJsU-U14WW7cD344aZi72Hf_XNq9LBi0_feKRVQOO98gV-jlBKWer-n_FI1qLcOUoHfitOciTOVLgvYxeJUwePCwUm9JhQ186CLAc6i_AhqpeKRlDkVOF_viQeZTFwEadHT2KMIe0ImZnPqGUb07arUdzGO67Lwsts2ob7qgG_uWgVbjXMkTUwt-JSHdXUcGIz1FgCJaFgGygfQE_I_doNiApWr2okiuIMs_4Q5BfxIlvPR-uaOcpqxk7ldukvQgUjv4rTfOGE12fCx5eLDVF4P1OXgMjgmcXH1yaV89DgTBgDPP11tQrbsFbANX004VLF9MQWoVF6esl6xwQw
|
1
crates/jose/tests/jwts/rs256.jwt
Normal file
1
crates/jose/tests/jwts/rs256.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJSUzI1NiIsImtpZCI6ImxqQXdGc1czMmV4cHlBMFJqcktvT0h1WnhmazdLTFNlajh6bGRPOXo0aVUifQ.eyJoZWxsbyI6IndvcmxkIn0.JLzSM5NDbAIb5vpbnKJeHUgU-uJ46616qzDjWXRbIAdxPk8WUqpRDRTlPoRUBXsAKn7E14r_CZmwvGAgJipS7EY0PbJYOkA_6oi8sYWykMUT1F2BlqKQGv2BvRR0LGu0tmm4XYZT2nOLRiEa4bs9l-D2jA5GRTKjDnmgUBHXpX4vIICtnkHHvZilMf1Fjsdm-3X9NFmxjtvQChg-w0h6hM3NZAt6Gd5AG8MaFf-mj3sLa40c51XXz1J1WE9iOWF8lGC6EfP5MSWunKnhyHf2xPQiH4C_Tvm529p2EiEBjjoL1f2A8WH8EYruHF8AXsz2F8HxN_7ryGmjrqLGwuw7iQ
|
1
crates/jose/tests/jwts/rs384.jwt
Normal file
1
crates/jose/tests/jwts/rs384.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJSUzM4NCIsImtpZCI6ImxqQXdGc1czMmV4cHlBMFJqcktvT0h1WnhmazdLTFNlajh6bGRPOXo0aVUifQ.eyJoZWxsbyI6IndvcmxkIn0.Qf9DbTCoisWvPvGYahn-dMe9r-escYv5cTL-5X2tz5uPRUmAEJ6D6cn0VtLCCPmTIuzSYDzeMqdEx1Is-AVkzvWMKdFRXNVL_E54bhS6Dg04a75bL4YGQOg8iaTTdRlHMaLLfClf8sXttpHmnOFhQ9C6pLcmtT5cfle8qrAw9x7Ivri7jkcjydWcR2WKsYHJxEWDwdhDiBK461F2fi9YtbZOL4qdKEoYpg08v4jH7hFf5G60W_k2oKvPQnbVJe0VcnGcEXvItMAEi8omMn3_OxIGNH-mxBf9DOpOu8Vj-kvvWuE03f31goWLqiL6-Eq8ykqqFZ3sKb23WfGPd26pDw
|
1
crates/jose/tests/jwts/rs512.jwt
Normal file
1
crates/jose/tests/jwts/rs512.jwt
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJSUzUxMiIsImtpZCI6ImxqQXdGc1czMmV4cHlBMFJqcktvT0h1WnhmazdLTFNlajh6bGRPOXo0aVUifQ.eyJoZWxsbyI6IndvcmxkIn0.UevGIlEIlrQWvLLm3Iouq6cxjWf7CtFwaDXQOUEQzdQxa3Mg9H0KD7Ztc1LRS36RFd0rnh9dWsXmeDbQ9yWNepnRvv0QP8Vxq3ty7wOHZtLn2kG1SjDQqgaU743p4n-YUpVugzSha0RHTiRN1TU4zufpx26jQBuO7ihOFof6trc8E2UG98Pgd1w3kv20Glwo_cWauhAefgDRhS-sOaH_SsOFWSBNCa8ISeIOiuKLFOEp2o1m2sla0yCDHVptERYDp3D_LHTLX-BP0dyaxpKwfQ7EuECGK1r7_yyiSq_pOwPrainC3lBKYovOgj8tYGTJxfw4Au_QSY57J96M7N4TmA
|
3
crates/jose/tests/keys/ed25519.priv.pem
Normal file
3
crates/jose/tests/keys/ed25519.priv.pem
Normal file
@ -0,0 +1,3 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MC4CAQAwBQYDK2VwBCIEIIutDmtMjMBKXN/Oxmfvxw3cNwtqgcyR2awtQYH/OS/5
|
||||
-----END PRIVATE KEY-----
|
3
crates/jose/tests/keys/ed25519.pub.pem
Normal file
3
crates/jose/tests/keys/ed25519.pub.pem
Normal file
@ -0,0 +1,3 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MCowBQYDK2VwAyEAnVo63sClAQ8qwBAZW0tttHFhXdrLiKqJnFeJ+j3nA3U=
|
||||
-----END PUBLIC KEY-----
|
4
crates/jose/tests/keys/ed448.priv.pem
Normal file
4
crates/jose/tests/keys/ed448.priv.pem
Normal file
@ -0,0 +1,4 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MEcCAQAwBQYDK2VxBDsEOdwdrXdIIxmkz/6pi3/JeOemGYvMECA+CvW5CAGXCvwi
|
||||
VXFdnXxUt22BpU8Hl1jl1+kuGe3Mx5Pt3w==
|
||||
-----END PRIVATE KEY-----
|
4
crates/jose/tests/keys/ed448.pub.pem
Normal file
4
crates/jose/tests/keys/ed448.pub.pem
Normal file
@ -0,0 +1,4 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MEMwBQYDK2VxAzoAKgaYHB+xIpPPvmH2PdbnWT+67/CfJhuD3U90sv+i5CZmGdwt
|
||||
WOErsowzNYSvuFWk8vztPOERjn4A
|
||||
-----END PUBLIC KEY-----
|
67
crates/jose/tests/keys/jwks.priv.json
Normal file
67
crates/jose/tests/keys/jwks.priv.json
Normal file
@ -0,0 +1,67 @@
|
||||
{
|
||||
"keys": [
|
||||
{
|
||||
"k": "MjNlYWE0MzdjMmFjZTA0ZTM1YTNhNzdiODEzMjEwMGYzNDM4YWE2ODk0NmEzMDU1M2NiZjhmZTgwZTJhNWYzY2E3MDA4MjA1M2Y0YmQ0NDU3NjUzYmJiMTU4NzdkODQ3YzIzMjk0OGVhNzUxMmNjZDY3OWY3MWMyNjhmMWRkMDgK",
|
||||
"kid": "djHKoWU3rOlWg5E0EI_4FlbETfd4OFQgV98DFXEmGfg",
|
||||
"kty": "oct"
|
||||
},
|
||||
{
|
||||
"d": "PvWpVR1APf5Yxf0ZO20wx7jQXOFusJ5nijKAzyWAPFNx_pjoXD82BTaM0h7odiUGP8Bhc8VqctAQudQ_eEga-YXdpbKXL5wHZG0QzEQlZ_MT8tP_S4z_3rWoUyT9S1rPqWUuQvnb-YsfMWR3RuLGpS4a-3f_zUKbUT_RA5aJUWOMSMSOqMCuh4wdhK7GJKOQlWr8yXF6STEOx7nYxWpIxRdcJK3WinBXPZ5B88wgdDpcR0Yv32u-f7h4R5GHmjPPC2NXsWPpNMe8G6CbDQapgk_1mYViq6zpo4NgkrvBUsMRhFkI__Wlcfz12Ds9dBGxg1R-T7lIPY6wIZKUKLYVQQ",
|
||||
"dp": "PlzIdqVNjdf5bJaRhW9-d3kF-sQJVV1x2prqRKr-FJK79M39fCmrlhsnyxVc7iqt6gSb3Mz_N1rsCbdn1TYUq6RFed3gJO2Bl57_K8L1N7LKjIxm2TpjLTtrMyxwQx72igxdjdvQytN1l6nmoAI76tv4dsw02dyQIsJ3BR7JiGE",
|
||||
"dq": "rYGSaiGBnWhIJfI3ajRPy5Icuq7dO3pCgdoDPTMPgib-tHxegjx-v4frCq5Uqc9s94FsgR5_FsY3sttmMrYA7MCqGT3n2Peo718DharXhaScWabONJ8wAt2LPbXh74D3931F3hU9cKLWWg9L6Vckh5QPNgSu2ICEH6f0BxCiTUE",
|
||||
"e": "AQAB",
|
||||
"kid": "ljAwFsW32expyA0RjrKoOHuZxfk7KLSej8zldO9z4iU",
|
||||
"kty": "RSA",
|
||||
"n": "qNYHjNfiXl2SPu7NYSkd5RiF45bo4c_WW4K1NwH-iBwOOb970RvwlcyhctsvrtrUAJ046Z-3LgW27MR73NdcBP6z756XWIQ6CV6XlowG9NlgnEmOolh3XujZuNig-_05anzhTJr6Xl7uxh8o61VQgBjOgDma7cnEJNz2s89nu9f-WOMNage63O02ecA17TsZjU2jYcOCnV5UhsIyVRUcB3S2Jtk8FtRGBIydbkFHCX61atyh8GjzXYpneVPxTm1fRr-qUvGgNJqvK2HhuOzP6wRpcyKS6cl47I9Mu4L36pavtB_WOTXNhtduSYbUvMnkifAGuYTJHpT2e5QzSm2p7Q",
|
||||
"p": "1djfJgLXpMaNuUaXd7V4bnkN38XC1Of8dubiGO5bFAym1qnExUQmJSq6Uubs3bRLSaa1i1EQqvIB_bBLm6we4m_jQbuNl8-m6dODaZQ2XVWmMWK84zDsRyJL2U-qDbSwt346AI3b3p-rtWlQLftx3qwj0-neAU4Jopfdz6R4HN0",
|
||||
"q": "yh3QLx9gsrVF3crgejhJiEf4DLiGAglGAJpekeSllPhXWGF_0hw3YurhTm9swLumHhj0MvDNjVHz_fm-W5uLvkO0CW2hfoOtG3cj3AbnVu34v323EK27wTsp3ADMmykKk7Vc7rRtKUNuTawL-rL7ImvRhgiA_1La0NKkIk_yKFE",
|
||||
"qi": "asGs7u5s2MXkwm0DHcyC9290wmlufSFOUrBgG-o8iahLFnYl9fCazTZJtBrSNQdfSPzr_htSSDav082q1Khaj1dCl_Jn84wuW2zTXgKhWbIvnCHM0GqgWGJ2HKHW3MKhGgitO5xlkv0nuv8znrkJtkQdxdw8x22eSNHUIPh_NtU"
|
||||
},
|
||||
{
|
||||
"crv": "P-256",
|
||||
"d": "V9_Lc6jc0hyqFuqSHVaMzXcmWMj34Ib3pG5vls3EbaM",
|
||||
"kid": "lMbSI69ajsBHHkIpVAFKRKYnR66kGdGteg2oqMzp0_E",
|
||||
"kty": "EC",
|
||||
"x": "4rnALl_X1zeOJtDmxz-YiUR1-9QGBfRE90qy_rqe0N0",
|
||||
"y": "qGl3Telg02usgXK9jQTwcNQRLLovo07vffwaaZ3Dc5o"
|
||||
},
|
||||
{
|
||||
"crv": "P-384",
|
||||
"d": "qLmbibr7wHPvUolsAC6I2rD_a78oCA3SvNiOh4C_WvZmtVJtIBDeQ3IGhz8q5lF0",
|
||||
"kid": "J1EZJGP1LyhX2Zlz8xPcspMQEl8W3bYG2x3NqiMbPyY",
|
||||
"kty": "EC",
|
||||
"x": "dT7_3-Wp3kgewAiAyoarKQ2_rL4dketqaUti8nHOIT9K0dGtMGDt5W9uThc4mALN",
|
||||
"y": "LHdC5G42RYTZcdnnEMftzcx3DOOaeTErrcdMpVTdu0gdjAQDrDwtHGu73E3AeHGp"
|
||||
},
|
||||
{
|
||||
"crv": "P-521",
|
||||
"d": "Ae6RJFt67feYXDBIsWEv32WL7MsuiNJgO3A7WEyidaHN1-CP5nIjFN5urf8MvD8fBWqaxdwrAEIA6uPfc8f2U1J8",
|
||||
"kid": "_xGyI3ms90AvgF658wz971wswSyYnGSG_A0dAgmrAM0",
|
||||
"kty": "EC",
|
||||
"x": "ARv-BW1gxTWyey_wil6Sc2iaPuu_iBsEKji2B8UBgW-vIp-JSHzTut1dR1UpkFnZe53EkS6P5kTNm5cBA-r9282J",
|
||||
"y": "AeVtvHkJslwuPji-M71Zp3DzOostp_keWMB0f1zljl0P1CVhcpC5x4T_D3nEO_zRduL1R0Fv5gE6zaLm8X4cZD5m"
|
||||
},
|
||||
{
|
||||
"crv": "secp256k1",
|
||||
"d": "_BuT_AckwrIi8AJpUTkqbxTMaViLXK5z0oIePWf9kkg",
|
||||
"kid": "n9b4gyd5NgHv7pJ7S27AKBrhBPHa3H5ptcixmYerSUg",
|
||||
"kty": "EC",
|
||||
"x": "t-mBmz-Rvh0n3W_bRL_TSOc3Vv0ZB0oGaPZBEqu4sTQ",
|
||||
"y": "PLm8asqUtHw5gVZN09vA5giJkDIPDOZ4zaG7NR77qSg"
|
||||
},
|
||||
{
|
||||
"crv": "Ed25519",
|
||||
"d": "i60Oa0yMwEpc387GZ-_HDdw3C2qBzJHZrC1Bgf85L_k",
|
||||
"kid": "iXkiyhEh6E7U-aX0fg7w-esHWqPvvxWd6gH1JG2u7N0",
|
||||
"kty": "OKP",
|
||||
"x": "nVo63sClAQ8qwBAZW0tttHFhXdrLiKqJnFeJ-j3nA3U"
|
||||
},
|
||||
{
|
||||
"crv": "Ed448",
|
||||
"d": "3B2td0gjGaTP_qmLf8l456YZi8wQID4K9bkIAZcK_CJVcV2dfFS3bYGlTweXWOXX6S4Z7czHk-3f",
|
||||
"kid": "Qlta2qVlhHhg3jriJp0HsIBQqGVB1ZH2pEnyPHzl1Ms",
|
||||
"kty": "OKP",
|
||||
"x": "KgaYHB-xIpPPvmH2PdbnWT-67_CfJhuD3U90sv-i5CZmGdwtWOErsowzNYSvuFWk8vztPOERjn4A"
|
||||
}
|
||||
]
|
||||
}
|
50
crates/jose/tests/keys/jwks.pub.json
Normal file
50
crates/jose/tests/keys/jwks.pub.json
Normal file
@ -0,0 +1,50 @@
|
||||
{
|
||||
"keys": [
|
||||
{
|
||||
"e": "AQAB",
|
||||
"kid": "ljAwFsW32expyA0RjrKoOHuZxfk7KLSej8zldO9z4iU",
|
||||
"kty": "RSA",
|
||||
"n": "qNYHjNfiXl2SPu7NYSkd5RiF45bo4c_WW4K1NwH-iBwOOb970RvwlcyhctsvrtrUAJ046Z-3LgW27MR73NdcBP6z756XWIQ6CV6XlowG9NlgnEmOolh3XujZuNig-_05anzhTJr6Xl7uxh8o61VQgBjOgDma7cnEJNz2s89nu9f-WOMNage63O02ecA17TsZjU2jYcOCnV5UhsIyVRUcB3S2Jtk8FtRGBIydbkFHCX61atyh8GjzXYpneVPxTm1fRr-qUvGgNJqvK2HhuOzP6wRpcyKS6cl47I9Mu4L36pavtB_WOTXNhtduSYbUvMnkifAGuYTJHpT2e5QzSm2p7Q"
|
||||
},
|
||||
{
|
||||
"crv": "P-256",
|
||||
"kid": "lMbSI69ajsBHHkIpVAFKRKYnR66kGdGteg2oqMzp0_E",
|
||||
"kty": "EC",
|
||||
"x": "4rnALl_X1zeOJtDmxz-YiUR1-9QGBfRE90qy_rqe0N0",
|
||||
"y": "qGl3Telg02usgXK9jQTwcNQRLLovo07vffwaaZ3Dc5o"
|
||||
},
|
||||
{
|
||||
"crv": "P-384",
|
||||
"kid": "J1EZJGP1LyhX2Zlz8xPcspMQEl8W3bYG2x3NqiMbPyY",
|
||||
"kty": "EC",
|
||||
"x": "dT7_3-Wp3kgewAiAyoarKQ2_rL4dketqaUti8nHOIT9K0dGtMGDt5W9uThc4mALN",
|
||||
"y": "LHdC5G42RYTZcdnnEMftzcx3DOOaeTErrcdMpVTdu0gdjAQDrDwtHGu73E3AeHGp"
|
||||
},
|
||||
{
|
||||
"crv": "P-521",
|
||||
"kid": "_xGyI3ms90AvgF658wz971wswSyYnGSG_A0dAgmrAM0",
|
||||
"kty": "EC",
|
||||
"x": "ARv-BW1gxTWyey_wil6Sc2iaPuu_iBsEKji2B8UBgW-vIp-JSHzTut1dR1UpkFnZe53EkS6P5kTNm5cBA-r9282J",
|
||||
"y": "AeVtvHkJslwuPji-M71Zp3DzOostp_keWMB0f1zljl0P1CVhcpC5x4T_D3nEO_zRduL1R0Fv5gE6zaLm8X4cZD5m"
|
||||
},
|
||||
{
|
||||
"crv": "secp256k1",
|
||||
"kid": "n9b4gyd5NgHv7pJ7S27AKBrhBPHa3H5ptcixmYerSUg",
|
||||
"kty": "EC",
|
||||
"x": "t-mBmz-Rvh0n3W_bRL_TSOc3Vv0ZB0oGaPZBEqu4sTQ",
|
||||
"y": "PLm8asqUtHw5gVZN09vA5giJkDIPDOZ4zaG7NR77qSg"
|
||||
},
|
||||
{
|
||||
"crv": "Ed25519",
|
||||
"kid": "iXkiyhEh6E7U-aX0fg7w-esHWqPvvxWd6gH1JG2u7N0",
|
||||
"kty": "OKP",
|
||||
"x": "nVo63sClAQ8qwBAZW0tttHFhXdrLiKqJnFeJ-j3nA3U"
|
||||
},
|
||||
{
|
||||
"crv": "Ed448",
|
||||
"kid": "Qlta2qVlhHhg3jriJp0HsIBQqGVB1ZH2pEnyPHzl1Ms",
|
||||
"kty": "OKP",
|
||||
"x": "KgaYHB-xIpPPvmH2PdbnWT-67_CfJhuD3U90sv-i5CZmGdwtWOErsowzNYSvuFWk8vztPOERjn4A"
|
||||
}
|
||||
]
|
||||
}
|
8
crates/jose/tests/keys/k256.priv.pem
Normal file
8
crates/jose/tests/keys/k256.priv.pem
Normal file
@ -0,0 +1,8 @@
|
||||
-----BEGIN EC PARAMETERS-----
|
||||
BgUrgQQACg==
|
||||
-----END EC PARAMETERS-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHQCAQEEIPwbk/wHJMKyIvACaVE5Km8UzGlYi1yuc9KCHj1n/ZJIoAcGBSuBBAAK
|
||||
oUQDQgAEt+mBmz+Rvh0n3W/bRL/TSOc3Vv0ZB0oGaPZBEqu4sTQ8ubxqypS0fDmB
|
||||
Vk3T28DmCImQMg8M5njNobs1HvupKA==
|
||||
-----END EC PRIVATE KEY-----
|
4
crates/jose/tests/keys/k256.pub.pem
Normal file
4
crates/jose/tests/keys/k256.pub.pem
Normal file
@ -0,0 +1,4 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEt+mBmz+Rvh0n3W/bRL/TSOc3Vv0ZB0oG
|
||||
aPZBEqu4sTQ8ubxqypS0fDmBVk3T28DmCImQMg8M5njNobs1HvupKA==
|
||||
-----END PUBLIC KEY-----
|
1
crates/jose/tests/keys/oct.bin
Normal file
1
crates/jose/tests/keys/oct.bin
Normal file
@ -0,0 +1 @@
|
||||
23eaa437c2ace04e35a3a77b8132100f3438aa68946a30553cbf8fe80e2a5f3ca70082053f4bd4457653bbb15877d847c232948ea7512ccd679f71c268f1dd08
|
8
crates/jose/tests/keys/p256.priv.pem
Normal file
8
crates/jose/tests/keys/p256.priv.pem
Normal file
@ -0,0 +1,8 @@
|
||||
-----BEGIN EC PARAMETERS-----
|
||||
BggqhkjOPQMBBw==
|
||||
-----END EC PARAMETERS-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIFffy3Oo3NIcqhbqkh1WjM13JljI9+CG96Rub5bNxG2joAoGCCqGSM49
|
||||
AwEHoUQDQgAE4rnALl/X1zeOJtDmxz+YiUR1+9QGBfRE90qy/rqe0N2oaXdN6WDT
|
||||
a6yBcr2NBPBw1BEsui+jTu99/BppncNzmg==
|
||||
-----END EC PRIVATE KEY-----
|
4
crates/jose/tests/keys/p256.pub.pem
Normal file
4
crates/jose/tests/keys/p256.pub.pem
Normal file
@ -0,0 +1,4 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4rnALl/X1zeOJtDmxz+YiUR1+9QG
|
||||
BfRE90qy/rqe0N2oaXdN6WDTa6yBcr2NBPBw1BEsui+jTu99/BppncNzmg==
|
||||
-----END PUBLIC KEY-----
|
9
crates/jose/tests/keys/p384.priv.pem
Normal file
9
crates/jose/tests/keys/p384.priv.pem
Normal file
@ -0,0 +1,9 @@
|
||||
-----BEGIN EC PARAMETERS-----
|
||||
BgUrgQQAIg==
|
||||
-----END EC PARAMETERS-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MIGkAgEBBDCouZuJuvvAc+9SiWwALojasP9rvygIDdK82I6HgL9a9ma1Um0gEN5D
|
||||
cgaHPyrmUXSgBwYFK4EEACKhZANiAAR1Pv/f5aneSB7ACIDKhqspDb+svh2R62pp
|
||||
S2Lycc4hP0rR0a0wYO3lb25OFziYAs0sd0LkbjZFhNlx2ecQx+3NzHcM45p5MSut
|
||||
x0ylVN27SB2MBAOsPC0ca7vcTcB4cak=
|
||||
-----END EC PRIVATE KEY-----
|
5
crates/jose/tests/keys/p384.pub.pem
Normal file
5
crates/jose/tests/keys/p384.pub.pem
Normal file
@ -0,0 +1,5 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEdT7/3+Wp3kgewAiAyoarKQ2/rL4dketq
|
||||
aUti8nHOIT9K0dGtMGDt5W9uThc4mALNLHdC5G42RYTZcdnnEMftzcx3DOOaeTEr
|
||||
rcdMpVTdu0gdjAQDrDwtHGu73E3AeHGp
|
||||
-----END PUBLIC KEY-----
|
10
crates/jose/tests/keys/p521.priv.pem
Normal file
10
crates/jose/tests/keys/p521.priv.pem
Normal file
@ -0,0 +1,10 @@
|
||||
-----BEGIN EC PARAMETERS-----
|
||||
BgUrgQQAIw==
|
||||
-----END EC PARAMETERS-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MIHcAgEBBEIB7pEkW3rt95hcMEixYS/fZYvsyy6I0mA7cDtYTKJ1oc3X4I/mciMU
|
||||
3m6t/wy8Px8FaprF3CsAQgDq499zx/ZTUnygBwYFK4EEACOhgYkDgYYABAEb/gVt
|
||||
YMU1snsv8IpeknNomj7rv4gbBCo4tgfFAYFvryKfiUh807rdXUdVKZBZ2XudxJEu
|
||||
j+ZEzZuXAQPq/dvNiQHlbbx5CbJcLj44vjO9Wadw8zqLLaf5HljAdH9c5Y5dD9Ql
|
||||
YXKQuceE/w95xDv80Xbi9UdBb+YBOs2i5vF+HGQ+Zg==
|
||||
-----END EC PRIVATE KEY-----
|
6
crates/jose/tests/keys/p521.pub.pem
Normal file
6
crates/jose/tests/keys/p521.pub.pem
Normal file
@ -0,0 +1,6 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBG/4FbWDFNbJ7L/CKXpJzaJo+67+I
|
||||
GwQqOLYHxQGBb68in4lIfNO63V1HVSmQWdl7ncSRLo/mRM2blwED6v3bzYkB5W28
|
||||
eQmyXC4+OL4zvVmncPM6iy2n+R5YwHR/XOWOXQ/UJWFykLnHhP8PecQ7/NF24vVH
|
||||
QW/mATrNoubxfhxkPmY=
|
||||
-----END PUBLIC KEY-----
|
27
crates/jose/tests/keys/rsa.priv.pem
Normal file
27
crates/jose/tests/keys/rsa.priv.pem
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAqNYHjNfiXl2SPu7NYSkd5RiF45bo4c/WW4K1NwH+iBwOOb97
|
||||
0RvwlcyhctsvrtrUAJ046Z+3LgW27MR73NdcBP6z756XWIQ6CV6XlowG9NlgnEmO
|
||||
olh3XujZuNig+/05anzhTJr6Xl7uxh8o61VQgBjOgDma7cnEJNz2s89nu9f+WOMN
|
||||
age63O02ecA17TsZjU2jYcOCnV5UhsIyVRUcB3S2Jtk8FtRGBIydbkFHCX61atyh
|
||||
8GjzXYpneVPxTm1fRr+qUvGgNJqvK2HhuOzP6wRpcyKS6cl47I9Mu4L36pavtB/W
|
||||
OTXNhtduSYbUvMnkifAGuYTJHpT2e5QzSm2p7QIDAQABAoIBAD71qVUdQD3+WMX9
|
||||
GTttMMe40FzhbrCeZ4oygM8lgDxTcf6Y6Fw/NgU2jNIe6HYlBj/AYXPFanLQELnU
|
||||
P3hIGvmF3aWyly+cB2RtEMxEJWfzE/LT/0uM/961qFMk/Utaz6llLkL52/mLHzFk
|
||||
d0bixqUuGvt3/81Cm1E/0QOWiVFjjEjEjqjAroeMHYSuxiSjkJVq/MlxekkxDse5
|
||||
2MVqSMUXXCSt1opwVz2eQfPMIHQ6XEdGL99rvn+4eEeRh5ozzwtjV7Fj6TTHvBug
|
||||
mw0GqYJP9ZmFYqus6aODYJK7wVLDEYRZCP/1pXH89dg7PXQRsYNUfk+5SD2OsCGS
|
||||
lCi2FUECgYEA1djfJgLXpMaNuUaXd7V4bnkN38XC1Of8dubiGO5bFAym1qnExUQm
|
||||
JSq6Uubs3bRLSaa1i1EQqvIB/bBLm6we4m/jQbuNl8+m6dODaZQ2XVWmMWK84zDs
|
||||
RyJL2U+qDbSwt346AI3b3p+rtWlQLftx3qwj0+neAU4Jopfdz6R4HN0CgYEAyh3Q
|
||||
Lx9gsrVF3crgejhJiEf4DLiGAglGAJpekeSllPhXWGF/0hw3YurhTm9swLumHhj0
|
||||
MvDNjVHz/fm+W5uLvkO0CW2hfoOtG3cj3AbnVu34v323EK27wTsp3ADMmykKk7Vc
|
||||
7rRtKUNuTawL+rL7ImvRhgiA/1La0NKkIk/yKFECgYA+XMh2pU2N1/lslpGFb353
|
||||
eQX6xAlVXXHamupEqv4Ukrv0zf18KauWGyfLFVzuKq3qBJvczP83WuwJt2fVNhSr
|
||||
pEV53eAk7YGXnv8rwvU3ssqMjGbZOmMtO2szLHBDHvaKDF2N29DK03WXqeagAjvq
|
||||
2/h2zDTZ3JAiwncFHsmIYQKBgQCtgZJqIYGdaEgl8jdqNE/Lkhy6rt07ekKB2gM9
|
||||
Mw+CJv60fF6CPH6/h+sKrlSpz2z3gWyBHn8Wxjey22YytgDswKoZPefY96jvXwOF
|
||||
qteFpJxZps40nzAC3Ys9teHvgPf3fUXeFT1wotZaD0vpVySHlA82BK7YgIQfp/QH
|
||||
EKJNQQKBgGrBrO7ubNjF5MJtAx3MgvdvdMJpbn0hTlKwYBvqPImoSxZ2JfXwms02
|
||||
SbQa0jUHX0j86/4bUkg2r9PNqtSoWo9XQpfyZ/OMLlts014CoVmyL5whzNBqoFhi
|
||||
dhyh1tzCoRoIrTucZZL9J7r/M565CbZEHcXcPMdtnkjR1CD4fzbV
|
||||
-----END RSA PRIVATE KEY-----
|
9
crates/jose/tests/keys/rsa.pub.pem
Normal file
9
crates/jose/tests/keys/rsa.pub.pem
Normal file
@ -0,0 +1,9 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqNYHjNfiXl2SPu7NYSkd
|
||||
5RiF45bo4c/WW4K1NwH+iBwOOb970RvwlcyhctsvrtrUAJ046Z+3LgW27MR73Ndc
|
||||
BP6z756XWIQ6CV6XlowG9NlgnEmOolh3XujZuNig+/05anzhTJr6Xl7uxh8o61VQ
|
||||
gBjOgDma7cnEJNz2s89nu9f+WOMNage63O02ecA17TsZjU2jYcOCnV5UhsIyVRUc
|
||||
B3S2Jtk8FtRGBIydbkFHCX61atyh8GjzXYpneVPxTm1fRr+qUvGgNJqvK2HhuOzP
|
||||
6wRpcyKS6cl47I9Mu4L36pavtB/WOTXNhtduSYbUvMnkifAGuYTJHpT2e5QzSm2p
|
||||
7QIDAQAB
|
||||
-----END PUBLIC KEY-----
|
Reference in New Issue
Block a user