Skip to content

Commit

Permalink
test: add UCAN 0.10.0 conformance tests
Browse files Browse the repository at this point in the history
Note that not all of them pass yet, and that the build tests are
impossible to support, as currently defined, due to JSON
canonicalization issues.

Fixtures from: ucan-wg/conformance-tests#13
  • Loading branch information
QuinnWilton committed Sep 28, 2023
1 parent cfb7886 commit 841be84
Show file tree
Hide file tree
Showing 4 changed files with 1,838 additions and 4 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ p384 = "0.13.0"
p521 = "0.13.0"
proptest = { version = "1.1", optional = true }
rsa = { version = "0.9.2", features = ["sha2"] }
semver = "1.0.19"
serde = { version = "1.0.188", features = ["derive"] }
serde_json = "1.0.107"
sha2 = "0.10.8"
Expand All @@ -58,6 +59,7 @@ url = "2.4.1"

[dev-dependencies]
criterion = "0.4"
multihash = "0.18.0"
proptest = "1.1"

[features]
Expand Down
46 changes: 42 additions & 4 deletions src/ucan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use cid::{
multihash::{self, MultihashDigest},
Cid,
};
use serde::{Deserialize, Serialize};
use semver::Version;
use serde::{Deserialize, Deserializer, Serialize};

/// The current UCAN version
pub const UCAN_VERSION: &str = "0.10.0";
Expand All @@ -31,6 +32,7 @@ pub struct UcanPayload<F = DefaultFact, C = DefaultCapabilityParser> {
pub(crate) ucv: String,
pub(crate) iss: String,
pub(crate) aud: String,
#[serde(deserialize_with = "deserialize_required_nullable")]
pub(crate) exp: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) nbf: Option<u64>,
Expand Down Expand Up @@ -64,6 +66,21 @@ where
now_time: Option<u64>,
did_verifier_map: &DidVerifierMap,
) -> Result<(), Error> {
if self.typ() != "JWT" {
return Err(Error::VerifyingError {
msg: format!("expected header typ field to be 'JWT', got {}", self.typ()),
});
}

if Version::parse(self.version()).is_err() {
return Err(Error::VerifyingError {
msg: format!(
"expected header ucv field to be a semver, got {}",
self.version()
),
});
}

if self.is_expired(now_time) {
return Err(Error::VerifyingError {
msg: "token is expired".to_string(),
Expand All @@ -76,6 +93,18 @@ where
});
}

// TODO: parse and validate iss and aud DIDs during deserialization
self.payload
.aud
.strip_prefix("did:")
.and_then(|did| did.split_once(':'))
.ok_or(Error::VerifyingError {
msg: format!(
"expected did:<method>:<identifier>, got {}",
self.payload.aud
),
})?;

let (method, identifier) = self
.payload
.iss
Expand Down Expand Up @@ -158,7 +187,7 @@ where
/// Note that if a UCAN specifies an NBF but the other does not, the
/// other has an unbounded start time and this function will return
/// false.
pub fn lifetime_begins_before<S2>(&self, other: &Ucan<S2>) -> bool {
pub fn lifetime_begins_before(&self, other: &Ucan<C, F>) -> bool {
match (self.payload.nbf, other.payload.nbf) {
(Some(nbf), Some(other_nbf)) => nbf <= other_nbf,
(Some(_), None) => false,
Expand All @@ -167,7 +196,7 @@ where
}

/// Returns true if this UCAN expires no earlier than the other
pub fn lifetime_ends_after<S2>(&self, other: &Ucan<S2>) -> bool {
pub fn lifetime_ends_after(&self, other: &Ucan<C, F>) -> bool {
match (self.payload.exp, other.payload.exp) {
(Some(exp), Some(other_exp)) => exp >= other_exp,
(Some(_), None) => false,
Expand All @@ -176,7 +205,7 @@ where
}

/// Returns true if this UCAN's lifetime fully encompasses the other
pub fn lifetime_encompasses<S2>(&self, other: &Ucan<S2>) -> bool {
pub fn lifetime_encompasses(&self, other: &Ucan<C, F>) -> bool {
self.lifetime_begins_before(other) && self.lifetime_ends_after(other)
}

Expand Down Expand Up @@ -322,3 +351,12 @@ impl FromStr for Ucan {
})
}
}

fn deserialize_required_nullable<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: Deserialize<'de>,
D: Deserializer<'de>,
{
Deserialize::deserialize(deserializer)
.map_err(|_| serde::de::Error::custom("required field is missing or has invalid type"))
}
Loading

0 comments on commit 841be84

Please sign in to comment.