You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
Sign all the things
This commit is contained in:
41
Cargo.lock
generated
41
Cargo.lock
generated
@ -152,16 +152,6 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-signature"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c57ade8a273b42906cb87145358c778667c1ca515bfe24f61ed8a4bfe756c8e3"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"signature",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-stream"
|
name = "async-stream"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@ -619,9 +609,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64ct"
|
name = "base64ct"
|
||||||
version = "1.5.1"
|
version = "1.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851"
|
checksum = "ea2b2456fd614d856680dcd9fcc660a51a820fa09daef2e49772b56a193c8474"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bincode"
|
name = "bincode"
|
||||||
@ -2200,6 +2190,18 @@ dependencies = [
|
|||||||
"treediff",
|
"treediff",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "k256"
|
||||||
|
version = "0.11.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6db2573d3fd3e4cc741affc9b5ce1a8ce36cf29f09f80f36da4309d0ae6d7854"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"ecdsa",
|
||||||
|
"elliptic-curve",
|
||||||
|
"sha2 0.10.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "language-tags"
|
name = "language-tags"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@ -2555,7 +2557,6 @@ name = "mas-jose"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-signature",
|
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64ct",
|
"base64ct",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -2566,9 +2567,10 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
"hmac",
|
"hmac",
|
||||||
"http",
|
"http",
|
||||||
"mas-http",
|
"k256",
|
||||||
"mas-iana",
|
"mas-iana",
|
||||||
"p256",
|
"p256",
|
||||||
|
"p384",
|
||||||
"pkcs1",
|
"pkcs1",
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
"rand",
|
"rand",
|
||||||
@ -3092,6 +3094,17 @@ dependencies = [
|
|||||||
"sha2 0.10.2",
|
"sha2 0.10.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "p384"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa"
|
||||||
|
dependencies = [
|
||||||
|
"ecdsa",
|
||||||
|
"elliptic-curve",
|
||||||
|
"sha2 0.10.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
|
@ -6,10 +6,9 @@ edition = "2021"
|
|||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-signature = "0.2.0"
|
|
||||||
anyhow = "1.0.62"
|
anyhow = "1.0.62"
|
||||||
async-trait = "0.1.57"
|
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"] }
|
chrono = { version = "0.4.22", features = ["serde"] }
|
||||||
crypto-mac = { version = "0.11.1", features = ["std"] }
|
crypto-mac = { version = "0.11.1", features = ["std"] }
|
||||||
digest = "0.10.3"
|
digest = "0.10.3"
|
||||||
@ -18,11 +17,13 @@ elliptic-curve = { version = "0.12.3", features = ["ecdh", "pem"] }
|
|||||||
futures-util = "0.3.23"
|
futures-util = "0.3.23"
|
||||||
hmac = "0.12.1"
|
hmac = "0.12.1"
|
||||||
http = "0.2.8"
|
http = "0.2.8"
|
||||||
|
k256 = { version = "0.11.2", features = ["ecdsa", "pem", "pkcs8"] }
|
||||||
p256 = { version = "0.11.1", 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"] }
|
pkcs1 = { version = "0.4.0", features = ["pem", "pkcs8"] }
|
||||||
pkcs8 = { version = "0.9.0", features = ["pem", "std"] }
|
pkcs8 = { version = "0.9.0", features = ["pem", "std"] }
|
||||||
rand = "0.8.5"
|
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"
|
schemars = "0.8.10"
|
||||||
sec1 = "0.3.0"
|
sec1 = "0.3.0"
|
||||||
serde = { version = "1.0.144", features = ["derive"] }
|
serde = { version = "1.0.144", features = ["derive"] }
|
||||||
@ -37,6 +38,3 @@ tracing = "0.1.36"
|
|||||||
url = { version = "2.2.2", features = ["serde"] }
|
url = { version = "2.2.2", features = ["serde"] }
|
||||||
|
|
||||||
mas-iana = { path = "../iana" }
|
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 url::Url;
|
||||||
|
|
||||||
|
use crate::constraints::Constrainable;
|
||||||
|
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||||
@ -109,20 +111,6 @@ impl JsonWebKey {
|
|||||||
self
|
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]
|
#[must_use]
|
||||||
pub const fn alg(&self) -> Option<JsonWebSignatureAlg> {
|
pub const fn alg(&self) -> Option<JsonWebSignatureAlg> {
|
||||||
self.alg
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct JsonWebKeySet {
|
pub struct JsonWebKeySet {
|
||||||
keys: Vec<JsonWebKey>,
|
keys: Vec<JsonWebKey>,
|
||||||
@ -251,10 +291,11 @@ impl From<rsa::RsaPublicKey> for JsonWebKeyParameters {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::constraints::ConstraintSet;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn load_google_keys() {
|
fn load_google_keys() {
|
||||||
let jwks = r#"{
|
let jwks = serde_json::json!({
|
||||||
"keys": [
|
"keys": [
|
||||||
{
|
{
|
||||||
"alg": "RS256",
|
"alg": "RS256",
|
||||||
@ -273,19 +314,33 @@ mod tests {
|
|||||||
"alg": "RS256"
|
"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
|
// Both keys are RSA public keys
|
||||||
for jwk in jwks.keys {
|
for jwk in &jwks.keys {
|
||||||
rsa::RsaPublicKey::try_from(jwk.parameters).unwrap();
|
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)]
|
#[allow(clippy::too_many_lines)]
|
||||||
#[test]
|
#[test]
|
||||||
fn load_keycloak_keys() {
|
fn load_keycloak_keys() {
|
||||||
let jwks = r#"{
|
let jwks = serde_json::json!({
|
||||||
"keys": [
|
"keys": [
|
||||||
{
|
{
|
||||||
"kid": "SuGUPE9Sr-1Gha2NLse33r5NQu3XoS_I3Qds3bcmfQE",
|
"kid": "SuGUPE9Sr-1Gha2NLse33r5NQu3XoS_I3Qds3bcmfQE",
|
||||||
@ -393,9 +448,9 @@ mod tests {
|
|||||||
"y": "Vu3rTFNn_9zWTki95UGT1Bd9PN84KDXmttCrJ1bsYHTWQCaEONk8iwA3U6mEDrg4xtZSTXXKCFdFP13ONWB9oZ4"
|
"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
|
// The first 6 keys are RSA, 7th is P-256
|
||||||
let mut keys = jwks.keys.into_iter();
|
let mut keys = jwks.keys.into_iter();
|
||||||
rsa::RsaPublicKey::try_from(keys.next().unwrap().parameters).unwrap();
|
rsa::RsaPublicKey::try_from(keys.next().unwrap().parameters).unwrap();
|
||||||
|
@ -115,14 +115,17 @@ where
|
|||||||
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
||||||
let raw = RawJwt::try_from(value)?;
|
let raw = RawJwt::try_from(value)?;
|
||||||
|
|
||||||
|
let header_reader =
|
||||||
|
base64ct::Decoder::<'_, Base64UrlUnpadded>::new(raw.header().as_bytes())
|
||||||
|
.map_err(JwtDecodeError::decode_header)?;
|
||||||
let header =
|
let header =
|
||||||
Base64UrlUnpadded::decode_vec(raw.header()).map_err(JwtDecodeError::decode_header)?;
|
serde_json::from_reader(header_reader).map_err(JwtDecodeError::deserialize_header)?;
|
||||||
let header = serde_json::from_slice(&header).map_err(JwtDecodeError::deserialize_header)?;
|
|
||||||
|
|
||||||
|
let payload_reader =
|
||||||
|
base64ct::Decoder::<'_, Base64UrlUnpadded>::new(raw.payload().as_bytes())
|
||||||
|
.map_err(JwtDecodeError::decode_payload)?;
|
||||||
let payload =
|
let payload =
|
||||||
Base64UrlUnpadded::decode_vec(raw.payload()).map_err(JwtDecodeError::decode_payload)?;
|
serde_json::from_reader(payload_reader).map_err(JwtDecodeError::deserialize_payload)?;
|
||||||
let payload =
|
|
||||||
serde_json::from_slice(&payload).map_err(JwtDecodeError::deserialize_payload)?;
|
|
||||||
|
|
||||||
let signature = Base64UrlUnpadded::decode_vec(raw.signature())
|
let signature = Base64UrlUnpadded::decode_vec(raw.signature())
|
||||||
.map_err(JwtDecodeError::decode_signature)?;
|
.map_err(JwtDecodeError::decode_signature)?;
|
||||||
@ -162,6 +165,14 @@ impl JwtVerificationError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Jwt<'a, T> {
|
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>
|
pub fn verify<K, S>(&self, key: &K) -> Result<(), JwtVerificationError>
|
||||||
where
|
where
|
||||||
K: Verifier<S>,
|
K: Verifier<S>,
|
||||||
|
@ -21,7 +21,10 @@ use sha2::{Sha256, Sha384, Sha512};
|
|||||||
use signature::{Signature, Verifier};
|
use signature::{Signature, Verifier};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{JsonWebKey, JsonWebKeySet, JsonWebSignatureHeader, VerifyingKeystore};
|
use crate::{
|
||||||
|
constraints::Constrainable, JsonWebKey, JsonWebKeySet, JsonWebSignatureHeader,
|
||||||
|
VerifyingKeystore,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -18,9 +18,13 @@
|
|||||||
#![allow(clippy::missing_errors_doc, clippy::module_name_repetitions)]
|
#![allow(clippy::missing_errors_doc, clippy::module_name_repetitions)]
|
||||||
|
|
||||||
pub mod claims;
|
pub mod claims;
|
||||||
|
pub mod constraints;
|
||||||
|
pub mod hmac;
|
||||||
pub(crate) mod jwk;
|
pub(crate) mod jwk;
|
||||||
pub(crate) mod jwt;
|
pub(crate) mod jwt;
|
||||||
mod keystore;
|
mod keystore;
|
||||||
|
pub(crate) mod rsa;
|
||||||
|
pub mod verifier;
|
||||||
|
|
||||||
pub use futures_util::future::Either;
|
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