Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/jwk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub mod policy;
mod private;
mod public;
pub(crate) mod serde_impl;
mod set;
mod signer;
pub(crate) mod thumbprint;
mod verifier;
Expand All @@ -42,6 +43,7 @@ pub use self::{
key_use::KeyUsage,
private::{EcPrivate, OkpPrivate, Private},
public::{EcPublic, OkpPublic, Public},
set::{CheckedJsonWebKeySet, JsonWebKeySet},
signer::{FromJwkError, JwkSigner},
symmetric::SymmetricJsonWebKey,
thumbprint::Thumbprint,
Expand Down
2 changes: 1 addition & 1 deletion src/jwk/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ impl<P: Policy> Policy for &P {
}

/// An error returned by the [`Policy`] trait
pub trait PolicyError {
pub trait PolicyError: core::error::Error {
/// A custom error message
fn custom<T>(msg: T) -> Self
where
Expand Down
246 changes: 246 additions & 0 deletions src/jwk/set.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
//! This module contains the JWK Set implementation.

use alloc::vec::Vec;

use serde::{Deserialize, Serialize};

use super::{
policy::{Checkable, Checked, Policy},
FromJwkError, JsonWebKey, JwkSigner, JwkVerifier,
};

/// A list of raw [`JsonWebKey`] objects, which is parsed according to [Section
/// 5 of RFC 7517](https://www.rfc-editor.org/rfc/rfc7517#section-5).
///
/// ## Additional parameters
///
/// The `A` type parameter can be used to specify additional parameters for all
/// the json web keys inside this set.
///
/// ## Use key set for operations
///
/// In order to use a key set for signing or verifying, you first have to
/// validate all keys inside the set. Similar to how to have to check a
/// [`JsonWebKey`] in order to get a [`Signer`] or [`Verifier`] instance.
///
/// [`Signer`]: crate::jws::Signer
/// [`Verifier`]: crate::jws::Verifier
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct JsonWebKeySet<A = ()> {
keys: Vec<JsonWebKey<A>>,
}

impl<A> JsonWebKeySet<A> {
/// Tries to find the JWK with the given key ID paramter set.
pub fn find_by_keyid(&self, key_id: &str) -> Option<&JsonWebKey<A>> {
self.keys
.iter()
.find(|key| key.key_id().is_some_and(|id| id == key_id))
}

/// Returns an iterator over all the JWKs in this set.
pub fn iter(&self) -> impl Iterator<Item = &JsonWebKey<A>> {
self.keys.iter()
}

Check warning on line 44 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L42-L44

Added lines #L42 - L44 were not covered by tests

/// Returns an iterator that allows modifying each JWK.
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut JsonWebKey<A>> {
self.keys.iter_mut()
}

Check warning on line 49 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L47-L49

Added lines #L47 - L49 were not covered by tests

/// Checks all keys inside this set using the given policy.
///
/// ## Errors
///
/// If any of the keys inside this set do not pass the policy check, this
/// function will return an error.
pub fn check<P: Policy + Clone>(self, policy: P) -> Result<CheckedJsonWebKeySet<P, A>, P::Error>
where
A: Checkable,
{
let mut validated = Vec::new();

for key in self.keys {
match key.check(policy.clone()) {
Ok(checked) => validated.push(checked),
Err((_, err)) => return Err(err),

Check warning on line 66 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L66

Added line #L66 was not covered by tests
}
}

Ok(CheckedJsonWebKeySet { keys: validated })
}
}

impl<'a, A> IntoIterator for &'a mut JsonWebKeySet<A> {
type IntoIter = core::slice::IterMut<'a, JsonWebKey<A>>;
type Item = &'a mut JsonWebKey<A>;

fn into_iter(self) -> Self::IntoIter {
self.keys.iter_mut()
}

Check warning on line 80 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L78-L80

Added lines #L78 - L80 were not covered by tests
}

impl<'a, A> IntoIterator for &'a JsonWebKeySet<A> {
type IntoIter = core::slice::Iter<'a, JsonWebKey<A>>;
type Item = &'a JsonWebKey<A>;

fn into_iter(self) -> Self::IntoIter {
self.keys.iter()
}

Check warning on line 89 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L87-L89

Added lines #L87 - L89 were not covered by tests
}

impl<A> IntoIterator for JsonWebKeySet<A> {
type IntoIter = alloc::vec::IntoIter<Self::Item>;
type Item = JsonWebKey<A>;

fn into_iter(self) -> Self::IntoIter {
self.keys.into_iter()
}

Check warning on line 98 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L96-L98

Added lines #L96 - L98 were not covered by tests
}

impl<A> From<Vec<JsonWebKey<A>>> for JsonWebKeySet<A> {
fn from(keys: Vec<JsonWebKey<A>>) -> Self {
Self { keys }
}

Check warning on line 104 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L102-L104

Added lines #L102 - L104 were not covered by tests
}

impl<A> FromIterator<JsonWebKey<A>> for JsonWebKeySet<A> {
fn from_iter<T: IntoIterator<Item = JsonWebKey<A>>>(iter: T) -> Self {
let keys = iter.into_iter().collect();
Self { keys }
}

Check warning on line 111 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L108-L111

Added lines #L108 - L111 were not covered by tests
}

impl<A, P: Policy> From<CheckedJsonWebKeySet<P, A>> for JsonWebKeySet<A> {
fn from(checked: CheckedJsonWebKeySet<P, A>) -> Self {
checked.into_jwk_set()
}

Check warning on line 117 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L115-L117

Added lines #L115 - L117 were not covered by tests
}

/// A list of validated [`JsonWebKey`] objects.
///
/// This is the version of a [`JsonWebKeySet`] whose keys have all been checked,
/// so it is safe now to use them for signing or verifying data.
#[derive(Debug, Default, Clone)]
pub struct CheckedJsonWebKeySet<P, A = ()> {
keys: Vec<Checked<JsonWebKey<A>, P>>,
}

impl<A, P: Policy> CheckedJsonWebKeySet<P, A> {
/// Converts this checked JWK set back into a normal [`JsonWebKeySet`].
pub fn into_jwk_set(self) -> JsonWebKeySet<A> {
let keys = self
.keys
.into_iter()
.map(|x| x.into_type())
.collect::<Vec<_>>();

JsonWebKeySet { keys }
}

Check warning on line 139 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L131-L139

Added lines #L131 - L139 were not covered by tests

/// Tries to convert all keys in this set to [`JwkSigner`]s, in order to
/// sign a JWS using them.
///
/// # Errors
///
/// Fails if one of the JWKs could not be converted to a signer.
pub fn into_signers(self) -> Result<Vec<JwkSigner>, FromJwkError> {
let signers: Vec<JwkSigner> = self
.keys
.into_iter()
.map(|x| x.try_into())
.collect::<Result<Vec<_>, _>>()?;

Check warning on line 152 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L147-L152

Added lines #L147 - L152 were not covered by tests

Ok(signers)
}

Check warning on line 155 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L154-L155

Added lines #L154 - L155 were not covered by tests

/// Tries to convert all keys in this set to [`JwkSigner`]s, in order to
/// sign a JWS using them. But instead of taking `self`, it will clone
/// all keys.
///
/// # Errors
///
/// Fails if one of the JWKs could not be converted to a signer.
pub fn signers(&self) -> Result<Vec<JwkSigner>, FromJwkError>
where
A: Clone,
P: Clone,
{
let signers: Vec<JwkSigner> = self
.keys
.iter()
.cloned()
.map(|x| x.try_into())
.collect::<Result<Vec<_>, _>>()?;

Ok(signers)
}

/// Tries to convert all keys in this set to [`JwkVerifier`]s, in order to
/// verify a JWS using them.
///
/// # Errors
///
/// Fails if one of the JWKs could not be converted to a verifier.
pub fn into_verifiers(self) -> Result<Vec<JwkVerifier>, FromJwkError> {
let verifiers: Vec<JwkVerifier> = self
.keys
.into_iter()
.map(|x| x.try_into())
.collect::<Result<Vec<_>, _>>()?;

Check warning on line 190 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L185-L190

Added lines #L185 - L190 were not covered by tests

Ok(verifiers)
}

Check warning on line 193 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L192-L193

Added lines #L192 - L193 were not covered by tests

/// Tries to convert all keys in this set to [`JwkVerifier`]s, in order to
/// verify a JWS using them. But instead of taking `self`, it will clone
/// all keys.
///
/// # Errors
///
/// Fails if one of the JWKs could not be converted to a verifier.
pub fn verifiers(&self) -> Result<Vec<JwkVerifier>, FromJwkError>
where
A: Clone,
P: Clone,
{
let verifiers: Vec<JwkVerifier> = self
.keys
.iter()
.cloned()
.map(|x| x.try_into())
.collect::<Result<Vec<_>, _>>()?;

Ok(verifiers)
}

/// Tries to find the JWK for the given key id, and then converts that JWK
/// into a [`JwkSigner`].
pub fn signer_for_key_id(&self, key_id: &str) -> Option<Result<JwkSigner, FromJwkError>>
where
A: Clone,
P: Clone,
{
let key = self
.keys
.iter()
.find(|key| key.key_id().is_some_and(|id| id == key_id))?;

Check warning on line 227 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L219-L227

Added lines #L219 - L227 were not covered by tests

Some(JwkSigner::try_from(Checked::clone(key)))
}

Check warning on line 230 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L229-L230

Added lines #L229 - L230 were not covered by tests

/// Tries to find the JWK for the given key id, and then converts that JWK
/// into a [`JwkVerifier`].
pub fn verifier_for_key_id(&self, key_id: &str) -> Option<Result<JwkVerifier, FromJwkError>>
where
A: Clone,
P: Clone,
{
let key = self
.keys
.iter()
.find(|key| key.key_id().is_some_and(|id| id == key_id))?;

Check warning on line 242 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L234-L242

Added lines #L234 - L242 were not covered by tests

Some(JwkVerifier::try_from(Checked::clone(key)))
}

Check warning on line 245 in src/jwk/set.rs

View check run for this annotation

Codecov / codecov/patch

src/jwk/set.rs#L244-L245

Added lines #L244 - L245 were not covered by tests
}
28 changes: 28 additions & 0 deletions src/jws.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,34 @@ impl<F: Format, T: IntoPayload> JsonWebSignature<F, T> {
}

impl<T: IntoPayload> JsonWebSignature<JsonGeneral, T> {
/// Signs this JWS using multiple signers.
///
/// Instead of taking a trait object as a signer, this method takes a
/// generic type which can avoid the requirement for manual coercion to
/// a trait object.
///
/// You can use this method to avoid some unnecessary mappings. For example,
/// if you have a `Vec<JwkSigner>`, you can use
/// `sign_many_type(signers.iter_mut())` instead of having to map
/// `JwkSigner` to `&mut dyn Signer<S>` first.
///
/// # Errors
///
/// Returns an error if the length of the given iterator of signers does
/// not match the number of headers in this JWS.
/// Otherwise, this method may return the same errors as the normal sign
/// operation.
#[inline]
pub fn sign_many_type<'s, S: AsRef<[u8]> + 's, SIGNER: Signer<S> + 's>(
self,
signers: impl IntoIterator<Item = &'s mut SIGNER>,
) -> Result<Signed<JsonGeneral>, SignError<T::Error>> {
self.sign_many(signers.into_iter().map(|s| {
let s: &mut dyn Signer<S> = s;
s
}))
}

/// Signs this JWS using multiple signers.
///
/// This is only supported when the JWS is in the [`JsonGeneral`] format.
Expand Down
28 changes: 27 additions & 1 deletion src/jws/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,12 +199,38 @@ impl<T> ManyUnverified<T> {
/// Verify this struct using the given verifies, returning a [`Verified`]
/// representation of the inner type.
///
/// You can use this method to avoid some unnecessary mapping. For example,
/// if you have a `Vec<JwkVerifier>`, you can use
/// `verify_many_type(signers.iter_mut())` instead of having to map
/// `JwkVerifier` to `&mut dyn Verifier<S>` first.
///
/// # Errors
///
/// Returns an error if the number of verifiers does not match the number of
/// signatures, or if anything went wrong during a signature
/// verification or if one of the signatures is just invalid.
pub fn verify_many_type<'a, V: Verifier + 'a>(
self,
verifiers: impl IntoIterator<Item = &'a mut V>,
) -> Result<Verified<T>, VerifyError> {
self.verify_many(verifiers.into_iter().map(|s| {
let s: &mut dyn Verifier = s;
s
}))
}

/// Verify this struct using the given verifies, returning a [`Verified`]
/// representation of the inner type.
///
/// Note that currently, the order of the verifiers iterator and signatures
/// in the JWS must match exactly.
///
/// # Errors
///
/// Returns an error if the number of verifiers does not match the number of
/// signatures, or if anything went wrong during a signature
/// verification or if one of the signatures is just invalid.
// TODO: consider using a more specific error type to give the usermore
// TODO: consider using a more specific error type to give the user more
// information about the error
pub fn verify_many<'a>(
self,
Expand Down
24 changes: 24 additions & 0 deletions tests/jwk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,27 @@ fn symmetric_thumbprint() {
let p = Base64UrlString::encode(p);
assert_eq!(&*p, "prDKy90VJzrDTpm8-W2Q_pv_kzrX_zyZ7ANjRAasDxc");
}

pub mod set {
use jose::{jwk::JsonWebKeySet, JsonWebKey};

use crate::read_key_file;

#[test]
fn construct_and_find() {
let set = read_key_file("set");
let set: JsonWebKeySet = serde_json::from_str(&set).unwrap();

let ec_key = read_key_file("p256");
let ec_key = serde_json::from_str::<JsonWebKey>(&ec_key).unwrap();

let hmac_key = read_key_file("hs256");
let hmac_key = serde_json::from_str::<JsonWebKey>(&hmac_key).unwrap();

let found_ec_key = set.find_by_keyid("ec_key").unwrap();
let found_hmac_key = set.find_by_keyid("hmac_key").unwrap();

assert_eq!(found_ec_key.key_type(), ec_key.key_type());
assert_eq!(found_hmac_key.key_type(), hmac_key.key_type());
}
}
Loading