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

feat!: update to v0.10.0 spec #131

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
File renamed without changes.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ repos:
- id: fmt
name: fmt
description: Format rust files.
entry: cargo +nightly fmt
entry: cargo fmt
language: system
types: [rust]
args: ["--all", "--", "--check"]
Expand Down
2 changes: 1 addition & 1 deletion .rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
edition = "2021"
imports_granularity = "Crate"
#imports_granularity = "Crate"
6 changes: 3 additions & 3 deletions ucan-key-support/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ repository = "https://github.com/cdata/rs-ucan/"
homepage = "https://github.com/cdata/rs-ucan"
license = "Apache-2.0"
readme = "README.md"
version = "0.1.7"
version = "0.1.8"

[features]
default = []
Expand All @@ -25,10 +25,10 @@ async-trait = "0.1"
bs58 = "0.5"
ed25519-zebra = "3.1"
log = "0.4"
rsa = "0.9"
p256 = "0.13"
rsa = "0.9"
sha2 = { version = "0.10", features = ["oid"] }
ucan = { path = "../ucan", version = "0.4.0" }
ucan = { path = "../ucan", version = ">=0.4.0" }

[build-dependencies]
npm_rs = "1.0"
Expand Down
6 changes: 6 additions & 0 deletions ucan-key-support/src/ed25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ pub fn bytes_to_ed25519_key(bytes: Vec<u8>) -> Result<Box<dyn KeyMaterial>> {
Ok(Box::new(Ed25519KeyMaterial(public_key, None)))
}

pub fn bytes_to_ed25519_private_key(bytes: Vec<u8>) -> Result<Box<dyn KeyMaterial>> {
let private_key = Ed25519PrivateKey::try_from(bytes.as_slice())?;
let public_key = Ed25519PublicKey::from(&private_key);
Ok(Box::new(Ed25519KeyMaterial(public_key, Some(private_key))))
}

#[derive(Clone)]
pub struct Ed25519KeyMaterial(pub Ed25519PublicKey, pub Option<Ed25519PrivateKey>);

Expand Down
6 changes: 6 additions & 0 deletions ucan-key-support/src/p256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ pub fn bytes_to_p256_key(bytes: Vec<u8>) -> Result<Box<dyn KeyMaterial>> {
Ok(Box::new(P256KeyMaterial(public_key, None)))
}

pub fn bytes_to_p256_private_key(bytes: Vec<u8>) -> Result<Box<dyn KeyMaterial>> {
let private_key = P256PrivateKey::try_from(bytes.as_slice())?;
let public_key = P256PublicKey::from(&private_key);
Ok(Box::new(P256KeyMaterial(public_key, Some(private_key))))
}

/// Support for NIST P-256 keys, aka secp256r1, aka ES256
#[derive(Clone)]
pub struct P256KeyMaterial(pub P256PublicKey, pub Option<P256PrivateKey>);
Expand Down
2 changes: 1 addition & 1 deletion ucan-key-support/src/rsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use ucan::crypto::{JwtSignatureAlgorithm, KeyMaterial};
pub use ucan::crypto::did::RSA_MAGIC_BYTES;

pub fn bytes_to_rsa_key(bytes: Vec<u8>) -> Result<Box<dyn KeyMaterial>> {
println!("Trying to parse RSA key...");
// println!("Trying to parse RSA key...");
// NOTE: DID bytes are PKCS1, but we store RSA keys as PKCS8
let public_key = RsaPublicKey::from_pkcs1_der(&bytes)?;

Expand Down
15 changes: 8 additions & 7 deletions ucan/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ repository = "https://github.com/cdata/rs-ucan/"
homepage = "https://github.com/cdata/rs-ucan"
license = "Apache-2.0"
readme = "README.md"
version = "0.4.0"
version = "0.5.0"
edition = "2021"

[features]
Expand All @@ -23,20 +23,21 @@ default = []
anyhow = "1.0"
async-recursion = "1.0"
async-trait = "0.1"
base64 = "0.21"
base64 = "0.22"
bs58 = "0.5"
cid = "0.10"
cid = { version = "0.11", features = ["serde"] }
futures = "0.3"
instant = { version = "0.1", features = ["wasm-bindgen"] }
libipld-core = { version = "0.16", features = ["serde-codec", "serde"] }
libipld-json = "0.16"
log = "0.4"
multihash-codetable = { version = "0.1", features = ["sha2", "blake3", "blake2b"] }
rand = "0.8"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
strum = "0.24"
strum_macros = "0.25"
unsigned-varint = "0.7"
strum = "0.26"
strum_macros = "0.26"
unsigned-varint = "0.8"
url = "2.0"

[target.'cfg(target_arch = "wasm32")'.dependencies]
Expand All @@ -48,5 +49,5 @@ tokio = { version = "^1", features = ["macros", "test-util"] }

[dev-dependencies]
did-key = "0.2"
serde_ipld_dagcbor = "0.3"
serde_ipld_dagcbor = "0.6"
wasm-bindgen-test = "0.3"
81 changes: 68 additions & 13 deletions ucan/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use crate::{
};
use anyhow::{anyhow, Result};
use base64::Engine;
use cid::multihash::Code;
use log::warn;
use multihash_codetable::Code;
use rand::Rng;
use serde::{de::DeserializeOwned, Serialize};

Expand Down Expand Up @@ -122,6 +122,8 @@ where
facts: FactsMap,
proofs: Vec<String>,
add_nonce: bool,

add_proof_facts: bool,
}

impl<'a, K> Default for UcanBuilder<'a, K>
Expand Down Expand Up @@ -150,6 +152,8 @@ where
facts: BTreeMap::new(),
proofs: Vec::new(),
add_nonce: false,

add_proof_facts: false,
}
}
}
Expand Down Expand Up @@ -206,24 +210,75 @@ where
self
}

/// Add facts or proofs of knowledge to this UCAN.
pub fn with_facts<T: Serialize + DeserializeOwned>(mut self, facts: &[(String, T)]) -> Self {
let f: Vec<(String, serde_json::Value)> = facts
.iter()
.map(|k| {
(
k.0.to_owned(),
serde_json::to_value(&k.1).unwrap_or(serde_json::json!("null")),
)
})
.collect();
self.facts.extend(f);
self
}

/// Will ensure that the built UCAN includes a number used once.
pub fn with_nonce(mut self) -> Self {
self.add_nonce = true;
self
}

/// Will add a collection of proof tokens (if any) to the facts field "prf".
pub fn with_add_proof_facts(mut self, add_proof_facts: bool) -> Self {
self.add_proof_facts = add_proof_facts;
self
}

/// Includes a UCAN in the list of proofs for the UCAN to be built.
/// Note that the proof's audience must match this UCAN's issuer
/// or else the proof chain will be invalidated!
/// The proof is encoded into a [Cid], hashed via the [UcanBuilder::default_hasher()]
/// algorithm, unless one is provided.
pub fn witnessed_by(mut self, authority: &Ucan, hasher: Option<Code>) -> Self {
pub fn witnessed_by(mut self, authority: &Ucan, hasher: Option<Code>) -> Result<Self> {
match authority.to_cid(hasher.unwrap_or_else(|| UcanBuilder::<K>::default_hasher())) {
Ok(proof) => self.proofs.push(proof.to_string()),
Err(error) => warn!("Failed to add authority to proofs: {}", error),
Ok(proof) => {
self.insert_proof(&proof, authority)?;
Ok(self)
}
Err(error) => Err(anyhow!("Failed to add authority to proofs: {}", error)),
}
}

self
fn insert_proof(&mut self, proof: &cid::Cid, authority: &Ucan) -> Result<()> {
self.proofs.push(proof.to_string());
if self.add_proof_facts {
if !self.facts.contains_key("prf") {
self.facts.insert("prf".to_owned(), serde_json::json!({}));
}
if let Some(prf_map) = self.facts.get_mut("prf") {
if let Some(prf_map) = prf_map.as_object_mut() {
prf_map.insert(
proof.to_string(),
serde_json::Value::String(authority.encode()?),
);
}
}
}
Ok(())
}

// Includes a collection of UCANs in the list of proofs for the UCAN to be built.
// (see witnessed_by)
pub fn with_proofs(self, proofs: &Vec<Ucan>, hasher: Option<Code>) -> Result<Self> {
let mut s = self;
for authority in proofs {
s = s.witnessed_by(authority, hasher)?;
}

Ok(s)
}

/// Claim a capability by inheritance (from an authorizing proof) or
Expand Down Expand Up @@ -254,26 +309,26 @@ where
/// you're building.
/// The proof is encoded into a [Cid], hashed via the [UcanBuilder::default_hasher()]
/// algorithm, unless one is provided.
pub fn delegating_from(mut self, authority: &Ucan, hasher: Option<Code>) -> Self {
pub fn delegating_from(mut self, authority: &Ucan, hasher: Option<Code>) -> Result<Self> {
match authority.to_cid(hasher.unwrap_or_else(|| UcanBuilder::<K>::default_hasher())) {
Ok(proof) => {
self.proofs.push(proof.to_string());
let proof_index = self.proofs.len() - 1;
self.insert_proof(&proof, authority)?;
let proof_delegation = ProofDelegationSemantics {};
let capability =
proof_delegation.parse(&format!("prf:{proof_index}"), "ucan/DELEGATE", None);
let capability = proof_delegation.parse(&format!("ucan:{proof}"), "ucan/*", None);

match capability {
Some(capability) => {
self.capabilities.push(Capability::from(&capability));
}
None => warn!("Could not produce delegation capability"),
None => {
return Err(anyhow!("Could not produce delegation capability"));
}
}
}
Err(error) => warn!("Could not encode authoritative UCAN: {:?}", error),
Err(error) => return Err(anyhow!("Could not encode authoritative UCAN: {:?}", error)),
};

self
Ok(self)
}

/// Returns the default hasher ([Code::Blake3_256]) used for [Cid] encodings.
Expand Down
32 changes: 30 additions & 2 deletions ucan/src/capability/data.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use anyhow::anyhow;
use serde::{Deserialize, Serialize};
use serde::{
de::Deserializer,
ser::{SerializeMap, Serializer},
Deserialize, Serialize,
};
use serde_json::Value;
use std::{
collections::{btree_map::Iter as BTreeMapIter, BTreeMap},
Expand Down Expand Up @@ -63,7 +67,7 @@ type CapabilitiesIterator<'a> = FlatMap<
fn((&'a String, &'a AbilitiesImpl)) -> AbilitiesMap<'a>,
>;

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Eq, PartialEq)]
/// The [Capabilities] struct contains capability data as a map-of-maps, matching the
/// [spec](https://github.com/ucan-wg/spec#326-capabilities--attenuation).
/// See `iter()` to deconstruct this map into a sequence of [Capability] datas.
Expand All @@ -85,6 +89,30 @@ type CapabilitiesIterator<'a> = FlatMap<
/// ```
pub struct Capabilities(CapabilitiesImpl);

impl Serialize for Capabilities {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(self.len()))?;
for (k, v) in self.0.iter() {
map.serialize_entry(k, v)?;
}
map.end()
}
}

impl<'de> Deserialize<'de> for Capabilities {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let t: BTreeMap<String, BTreeMap<String, Vec<Value>>> =
Deserialize::deserialize(deserializer)?;
Ok(Capabilities(t))
}
}

impl Capabilities {
/// Using a [FlatMap] implementation, iterate over a [Capabilities] map-of-map
/// as a sequence of [Capability] datas.
Expand Down
Loading