From c654f5cba6afada037407c37115df0aaf1f3a589 Mon Sep 17 00:00:00 2001 From: Alex Rudy Date: Sun, 26 Nov 2023 06:11:30 +0000 Subject: [PATCH] Object safety for JWS key traits Ensures that TokenSigner and TokenVerifier are object-safe traits, and so can be used with --- Cargo.toml | 2 +- src/algorithms/mod.rs | 30 ++++++++++++++++++++++++++++-- src/jose/mod.rs | 6 +++--- src/token/mod.rs | 4 ++-- src/token/state.rs | 10 +++++----- 5 files changed, 39 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a7a48dd..056eeb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] } diff --git a/src/algorithms/mod.rs b/src/algorithms/mod.rs index 9ff5368..b5a5a47 100644 --- a/src/algorithms/mod.rs +++ b/src/algorithms/mod.rs @@ -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 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. @@ -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. @@ -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( diff --git a/src/jose/mod.rs b/src/jose/mod.rs index 29c00b3..8a2a9be 100644 --- a/src/jose/mod.rs +++ b/src/jose/mod.rs @@ -187,7 +187,7 @@ impl Header { 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), @@ -260,10 +260,10 @@ impl Header { 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() ); } diff --git a/src/token/mod.rs b/src/token/mod.rs index 274e5fe..51ed1df 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -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(), )); } diff --git a/src/token/state.rs b/src/token/state.rs index 86abeba..2045145 100644 --- a/src/token/state.rs +++ b/src/token/state.rs @@ -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, @@ -92,7 +92,7 @@ impl MaybeSigned for Unsigned { #[serde(bound(serialize = "H: Serialize, Alg: Clone, Alg::Signature: Serialize",))] pub struct Signed where - Alg: JoseAlgorithm + SerializeJWK, + Alg: DynJoseAlgorithm + SerializeJWK, { pub(super) header: jose::Header>, pub(super) signature: Alg::Signature, @@ -143,7 +143,7 @@ where #[serde(bound(serialize = "H: Serialize, Alg: Clone, Alg::Signature: Serialize",))] pub struct Verified where - Alg: JoseAlgorithm + SerializeJWK, + Alg: DynJoseAlgorithm + SerializeJWK, { pub(super) header: jose::Header>, pub(super) signature: Alg::Signature, @@ -151,7 +151,7 @@ where impl MaybeSigned for Verified where - Alg: JoseAlgorithm + SerializeJWK, + Alg: DynJoseAlgorithm + SerializeJWK, { type HeaderState = jose::SignedHeader; type Header = H; @@ -175,7 +175,7 @@ where impl HasSignature for Verified where - Alg: JoseAlgorithm + SerializeJWK, + Alg: DynJoseAlgorithm + SerializeJWK, { type Signature = Alg::Signature;