Skip to content

Commit

Permalink
Merge pull request #12 from alexrudy:object-safety
Browse files Browse the repository at this point in the history
Object safety for JWS key traits
  • Loading branch information
alexrudy authored Nov 26, 2023
2 parents 0bc7830 + c654f5c commit c4b4122
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 13 deletions.
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

0 comments on commit c4b4122

Please sign in to comment.