Skip to content

Commit

Permalink
Merge pull request #99 from minkan-chat/34-use-an-url-type-for-urls-i…
Browse files Browse the repository at this point in the history
…nstead-of-a-string

fix: introduce new Uri type for parsing URIs during deserialization
  • Loading branch information
Stupremee authored Apr 14, 2024
2 parents 7e5b1b5 + 1aec4bd commit b28f8bf
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 20 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ rust-version = "1.65"

[features]
default = []
std = ["signature/std", "thiserror-no-std/std", "rsa/std", "rand_core/std"]
std = [
"signature/std",
"thiserror-no-std/std",
"rsa/std",
"rand_core/std",
"fluent-uri/std"
]

[dependencies]
thiserror-no-std = "2.0.2"
Expand Down Expand Up @@ -75,6 +81,7 @@ serde-value = { version = "0.7.0", default-features = false }

# a replacement for the `mime` crate from hyper which seems to be no longer maintained
mediatype = { version = "0.19.3", features = ["serde"] }
fluent-uri = { version = "0.1.4", default-features = false }

[dev-dependencies]
rand = "0.8.5"
Expand Down
12 changes: 8 additions & 4 deletions src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub use self::{
use crate::{
format::Format,
jwa::{JsonWebContentEncryptionAlgorithm, JsonWebEncryptionAlgorithm, JsonWebSigningAlgorithm},
uri::BorrowedUri,
JsonWebKey, UntypedAdditionalProperties,
};

Expand Down Expand Up @@ -185,11 +186,11 @@ where
/// [section 4.1.2 of RFC 7515]: <https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.2>
/// [section 5 of RFC 7517]: <https://datatracker.ietf.org/doc/html/rfc7517#section-5>
// FIXME: use url type instead
pub fn jwk_set_url(&self) -> Option<HeaderValue<&str>> {
pub fn jwk_set_url(&self) -> Option<HeaderValue<BorrowedUri<'_>>> {
self.parameters
.jwk_set_url
.as_ref()
.map(HeaderValue::as_deref)
.map(|x| x.as_ref().map(|x| x.borrow()))
}

/// Depending where this [`JoseHeader`] is being used, in JWE it contains
Expand Down Expand Up @@ -232,8 +233,11 @@ where
/// [RFC 3986]: <https://datatracker.ietf.org/doc/html/rfc3986>
/// [RFC 5280]: <https://datatracker.ietf.org/doc/html/rfc5280>
/// [section 4.1.5 of RFC 7515]: <https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.5>
pub fn x509_url(&self) -> Option<HeaderValue<&str>> {
self.parameters.x509_url.as_ref().map(HeaderValue::as_deref)
pub fn x509_url(&self) -> Option<HeaderValue<BorrowedUri<'_>>> {
self.parameters
.x509_url
.as_ref()
.map(|x| x.as_ref().map(|x| x.borrow()))
}

/// An [`Iterator`] over a X.509 certificate chain that certify the public
Expand Down
10 changes: 5 additions & 5 deletions src/header/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{
header::parameters::Parameters,
jwa::{JsonWebContentEncryptionAlgorithm, JsonWebEncryptionAlgorithm, JsonWebSigningAlgorithm},
jwk::serde_impl::Base64DerCertificate,
JoseHeader, JsonWebKey, UntypedAdditionalProperties,
JoseHeader, JsonWebKey, UntypedAdditionalProperties, Uri,
};

/// A builder for a [`JoseHeader`].
Expand All @@ -23,10 +23,10 @@ use crate::{
pub struct JoseHeaderBuilder<F, T> {
// data
critical_headers: Option<BTreeSet<String>>,
jwk_set_url: Option<HeaderValue<String>>,
jwk_set_url: Option<HeaderValue<Uri>>,
json_web_key: Option<HeaderValue<JsonWebKey<UntypedAdditionalProperties>>>,
key_identifier: Option<HeaderValue<String>>,
x509_url: Option<HeaderValue<String>>,
x509_url: Option<HeaderValue<Uri>>,
x509_certificate_chain: Option<HeaderValue<Vec<Vec<u8>>>>,
x509_certificate_sha1_thumbprint: Option<HeaderValue<[u8; 20]>>,
x509_certificate_sha256_thumbprint: Option<HeaderValue<[u8; 32]>>,
Expand Down Expand Up @@ -367,10 +367,10 @@ macro_rules! setter {

setter! {
x509_certificate_chain: Vec<Vec<u8>>,
jwk_set_url: String,
jwk_set_url: Uri,
json_web_key: JsonWebKey<UntypedAdditionalProperties>,
key_identifier: String,
x509_url: String,
x509_url: Uri,
x509_certificate_sha1_thumbprint: [u8; 20],
x509_certificate_sha256_thumbprint: [u8; 32],
typ: MediaTypeBuf,
Expand Down
6 changes: 3 additions & 3 deletions src/header/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ use mediatype::MediaTypeBuf;
use serde_json::Value;

use super::HeaderValue;
use crate::{jwk::serde_impl::Base64DerCertificate, JsonWebKey, UntypedAdditionalProperties};
use crate::{jwk::serde_impl::Base64DerCertificate, JsonWebKey, UntypedAdditionalProperties, Uri};

#[derive(Debug)]
#[non_exhaustive]
pub(crate) struct Parameters<T> {
/// `crit` header MUST always be protected
pub(crate) critical_headers: Option<BTreeSet<String>>,
/// `jku` parameter defined in section 4.1.2 of JWS and section 4.1.4 of JWE
pub(crate) jwk_set_url: Option<HeaderValue<String>>,
pub(crate) jwk_set_url: Option<HeaderValue<Uri>>,
/// `jwk` parameter defined in section 4.1.3 of JWS and section 4.1.5 of JWE
pub(crate) json_web_key: Option<HeaderValue<JsonWebKey<UntypedAdditionalProperties>>>,
// `kid` parameter defined in section 4.1.4 of JWS and section 4.1.6 of JWE
pub(crate) key_id: Option<HeaderValue<String>>,
/// `x5u` parameter defined in section 4.1.5 of JWS and section 4.1.7 of JWE
// FIXME: use url type instead
pub(crate) x509_url: Option<HeaderValue<String>>,
pub(crate) x509_url: Option<HeaderValue<Uri>>,
/// `x5c` parameter defined in section 4.1.6 of JWS and section 4.1.8 of JWE
pub(crate) x509_certificate_chain: Option<HeaderValue<Vec<Base64DerCertificate>>>,
/// `x5t` parameter defined in section 4.1.7 of JWS and section 4.1.9 of JWE
Expand Down
11 changes: 6 additions & 5 deletions src/jwk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ use crate::{
},
policy::{Checkable, Checked, CryptographicOperation, Policy},
sealed::Sealed,
UntypedAdditionalProperties,
uri::BorrowedUri,
UntypedAdditionalProperties, Uri,
};

pub mod ec;
Expand Down Expand Up @@ -219,11 +220,11 @@ pub struct JsonWebKey<A = ()> {
#[serde(skip_serializing_if = "Option::is_none")]
kid: Option<String>,
/// `x5u` parameter section 4.6
// FIXME: consider using an dedicated URL type for this and ensure the protocol
// FIXME: considerung to ensure the protocol
// uses TLS or some other form of integrity protection.
// There are other things to consider, see the relevant section in the RFC.
#[serde(rename = "x5u", skip_serializing_if = "Option::is_none")]
x509_url: Option<String>,
x509_url: Option<Uri>,
/// `x5c` parameter section 4.7
// If the `x5c` parameter is not present, this will be an empty Vec
// FIXME: find a good way and crate to parse the DER-encoded X.509 certificate(s)
Expand Down Expand Up @@ -340,8 +341,8 @@ impl<T> JsonWebKey<T> {
/// [Section 4.6 of RFC 7517] defines the `x5u` (X.509 URL) Parameter.
///
/// [Section 4.6 of RFC 7517]: <https://datatracker.ietf.org/doc/html/rfc7517#section-4.6>
pub fn x509_url(&self) -> Option<&str> {
self.x509_url.as_deref()
pub fn x509_url(&self) -> Option<BorrowedUri<'_>> {
self.x509_url.as_ref().map(|x| x.borrow())
}

/// [Section 4.7 of RFC 7517] defines the `x5c` (X.509 Certificate Chain)
Expand Down
5 changes: 3 additions & 2 deletions src/jwk/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use super::{serde_impl::Base64DerCertificate, JsonWebKey, JsonWebKeyType, KeyOpe
use crate::{
jwa::JsonWebAlgorithm,
policy::{Checkable, Checked, Policy},
Uri,
};

/// Reasons the construction of a `JsonWebKey` via the
Expand Down Expand Up @@ -34,7 +35,7 @@ pub struct JsonWebKeyBuilder<A> {
pub(super) key_operations: Option<HashSet<KeyOperation>>,
pub(super) algorithm: Option<JsonWebAlgorithm>,
pub(super) kid: Option<String>,
pub(super) x509_url: Option<String>,
pub(super) x509_url: Option<Uri>,
pub(super) x509_certificate_chain: Vec<Base64DerCertificate>,
pub(super) x509_certificate_sha1_thumbprint: Option<[u8; 20]>,
pub(super) x509_certificate_sha256_thumbprint: Option<[u8; 32]>,
Expand Down Expand Up @@ -148,7 +149,7 @@ impl<A> JsonWebKeyBuilder<A> {
key_operations: HashSet<KeyOperation>,
algorithm: JsonWebAlgorithm,
kid: String,
x509_url: String,
x509_url: Uri,
x509_certificate_sha1_thumbprint: [u8; 20],
x509_certificate_sha256_thumbprint: [u8; 32],
}
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ pub mod jwk;
pub mod jws;
mod jwt;
pub mod policy;
mod uri;

use alloc::string::String;

pub use base64_url::Base64UrlString;
pub use uri::Uri;

#[doc(inline)]
pub use self::{header::JoseHeader, jwk::JsonWebKey, jws::JsonWebSignature, jwt::JsonWebToken};
Expand Down
116 changes: 116 additions & 0 deletions src/uri.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use alloc::string::String;
use core::{fmt, ops::Deref};

use serde::{Deserialize, Serialize};

/// A serializable URI type implemented using [`serde`] and [`fluent_uri`].
///
/// This is a thing wrapper around a [`fluent_uri::Uri<String>`] that implements
/// [`Serialize`] and [`Deserialize`].
#[derive(Debug, Clone, Default)]
pub struct Uri(fluent_uri::Uri<String>);

impl Uri {
/// Borrows this URI.
pub fn borrow(&self) -> BorrowedUri<'_> {
BorrowedUri(self.0.borrow())
}

/// Turns this URI into the underlying [`fluent_uri::Uri<String>`].
pub fn into_inner(self) -> fluent_uri::Uri<String> {
self.0
}
}

impl PartialEq for Uri {
fn eq(&self, other: &Self) -> bool {
self.0.as_str().eq(other.0.as_str())
}
}
impl Eq for Uri {}

impl Deref for Uri {
type Target = fluent_uri::Uri<String>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl From<fluent_uri::Uri<String>> for Uri {
fn from(uri: fluent_uri::Uri<String>) -> Self {
Self(uri)
}
}

impl fmt::Display for Uri {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}

impl Serialize for Uri {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0.as_str().serialize(serializer)
}
}

impl<'de> Deserialize<'de> for Uri {
fn deserialize<D>(deserializer: D) -> Result<Uri, D::Error>
where
D: serde::Deserializer<'de>,
{
let uri = String::deserialize(deserializer)?;

Ok(Uri(
fluent_uri::Uri::parse_from(uri).map_err(|(_, e)| serde::de::Error::custom(e))?
))
}
}

/// A borrowed version of the [`Uri`].
///
/// This is a thing wrapper around a [`fluent_uri::Uri<&str>`] that implements
/// [`Serialize`].
#[derive(Debug)]
pub struct BorrowedUri<'s>(&'s fluent_uri::Uri<&'s str>);

impl<'s> BorrowedUri<'s> {
/// Turns this borrowed URI into an owned [`Uri`].
pub fn to_owned(&self) -> Uri {
Uri(self.0.to_owned())
}
}

impl<'s> Deref for BorrowedUri<'s> {
type Target = fluent_uri::Uri<&'s str>;

fn deref(&self) -> &Self::Target {
self.0
}
}

impl fmt::Display for BorrowedUri<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(*self.0).fmt(f)
}
}

impl Serialize for BorrowedUri<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0.as_str().serialize(serializer)
}
}

impl PartialEq for BorrowedUri<'_> {
fn eq(&self, other: &Self) -> bool {
self.0.as_str().eq(other.0.as_str())
}
}
impl Eq for BorrowedUri<'_> {}

0 comments on commit b28f8bf

Please sign in to comment.