Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Object safety for JWS key traits #12

Merged
merged 1 commit into from
Nov 26, 2023
Merged
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: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
sha1 = "0.10"
sha2 = "0.10"
signature = { version = "2.2", features = ["digest"] }
signature = { version = "2.2", features = ["digest", "std"] }
thiserror = "1"
url = { version = "2.5", features = ["serde"] }
zeroize = { version = "1.7", features = ["serde", "derive"] }
Expand Down
30 changes: 28 additions & 2 deletions src/algorithms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,32 @@ pub trait JoseAlgorithm {
type Signature: SignatureEncoding;
}

/// A trait to associate an alogritm identifier with an algorithm.
///
/// This is a dynamic version of [`JoseAlgorithm`], which allows for
/// dynamic dispatch of the algorithm, and object-safety for the trait.
///
/// This trait does not need to be implemented manually, as it is implemented
/// for any type which implements [`JoseAlgorithm`].
pub trait DynJoseAlgorithm {
/// The type of the signature, which must support encoding.
type Signature: SignatureEncoding;

/// The identifier for this algorithm when used in a JWT registered header.
fn identifier(&self) -> AlgorithmIdentifier;
}

impl<T> DynJoseAlgorithm for T
where
T: JoseAlgorithm,
{
type Signature = T::Signature;

fn identifier(&self) -> AlgorithmIdentifier {
T::IDENTIFIER
}
}

/// A trait to associate an algorithm with a digest for signing.
pub trait JoseDigestAlgorithm: JoseAlgorithm {
/// The digest algorithm used by this signature.
Expand All @@ -162,7 +188,7 @@ pub trait JoseDigestAlgorithm: JoseAlgorithm {
/// A trait to represent an algorithm which can sign a JWT.
///
/// This trait should apply to signing keys.
pub trait TokenSigner: JoseAlgorithm {
pub trait TokenSigner: DynJoseAlgorithm {
/// Sign the contents of the JWT, when provided with the base64url-encoded header
/// and payload. This is the JWS Signature value, and will be base64url-encoded
/// and appended to the compact representation of the JWT.
Expand Down Expand Up @@ -207,7 +233,7 @@ where
///
/// This trait should apply to the equivalent of public keys, which have enough information
/// to verify a JWT signature, but not necessarily to sing it.
pub trait TokenVerifier: JoseAlgorithm {
pub trait TokenVerifier: DynJoseAlgorithm {
/// Verify the signature of the JWT, when provided with the base64url-encoded header
/// and payload.
fn verify_token(
Expand Down
6 changes: 3 additions & 3 deletions src/jose/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ impl<H> Header<H, UnsignedHeader> {
A: crate::algorithms::TokenSigner + crate::key::SerializeJWK + Clone,
{
let state = SignedHeader {
algorithm: A::IDENTIFIER,
algorithm: key.identifier(),
key: DerivedKeyValue::derive(self.state.key, key),
thumbprint: DerivedKeyValue::derive(self.state.thumbprint, key),
thumbprint_sha256: DerivedKeyValue::derive(self.state.thumbprint_sha256, key),
Expand Down Expand Up @@ -260,10 +260,10 @@ impl<H> Header<H, RenderedHeader> {
where
A: crate::algorithms::TokenVerifier + crate::key::SerializeJWK,
{
if *self.algorithm() != A::IDENTIFIER {
if *self.algorithm() != key.identifier() {
panic!(
"algorithm mismatch: expected header to have {:?}, got {:?}",
A::IDENTIFIER,
key.identifier(),
self.algorithm()
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/token/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,9 +467,9 @@ where
P: Serialize,
H: Serialize,
{
if A::IDENTIFIER != *self.state.header.algorithm() {
if algorithm.identifier() != *self.state.header.algorithm() {
return Err(TokenVerifyingError::Algorithm(
A::IDENTIFIER,
algorithm.identifier(),
*self.state.header.algorithm(),
));
}
Expand Down
10 changes: 5 additions & 5 deletions src/token/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use serde::Serialize;
use signature::SignatureEncoding;

use crate::{
algorithms::{JoseAlgorithm, SignatureBytes, TokenSigner},
algorithms::{DynJoseAlgorithm, SignatureBytes, TokenSigner},
base64data::Base64Signature,
jose,
key::SerializeJWK,
Expand Down Expand Up @@ -92,7 +92,7 @@ impl<H> MaybeSigned for Unsigned<H> {
#[serde(bound(serialize = "H: Serialize, Alg: Clone, Alg::Signature: Serialize",))]
pub struct Signed<H, Alg>
where
Alg: JoseAlgorithm + SerializeJWK,
Alg: DynJoseAlgorithm + SerializeJWK,
{
pub(super) header: jose::Header<H, jose::SignedHeader<Alg>>,
pub(super) signature: Alg::Signature,
Expand Down Expand Up @@ -143,15 +143,15 @@ where
#[serde(bound(serialize = "H: Serialize, Alg: Clone, Alg::Signature: Serialize",))]
pub struct Verified<H, Alg>
where
Alg: JoseAlgorithm + SerializeJWK,
Alg: DynJoseAlgorithm + SerializeJWK,
{
pub(super) header: jose::Header<H, jose::SignedHeader<Alg>>,
pub(super) signature: Alg::Signature,
}

impl<H, Alg> MaybeSigned for Verified<H, Alg>
where
Alg: JoseAlgorithm + SerializeJWK,
Alg: DynJoseAlgorithm + SerializeJWK,
{
type HeaderState = jose::SignedHeader<Alg>;
type Header = H;
Expand All @@ -175,7 +175,7 @@ where

impl<H, Alg> HasSignature for Verified<H, Alg>
where
Alg: JoseAlgorithm + SerializeJWK,
Alg: DynJoseAlgorithm + SerializeJWK,
{
type Signature = Alg::Signature;

Expand Down
Loading