Skip to content

Commit

Permalink
wip: update to new ceramic_event builder api
Browse files Browse the repository at this point in the history
  • Loading branch information
nathanielc committed May 16, 2024
1 parent ffa1482 commit 631829f
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 62 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ publish = false

[dependencies]
anyhow = "1"
ceramic-event = { git = "https://github.com/ceramicnetwork/rust-ceramic", branch = "main" }
#ceramic-event = { git = "https://github.com/ceramicnetwork/rust-ceramic", branch = "main" }
ceramic-event = { path = "../rust-ceramic/event" }
json-patch = { version = "2.0.0", features = ["diff"] }
once_cell = "1.19.0"
rand = "0.8.5"
Expand Down
8 changes: 4 additions & 4 deletions src/api.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::query::FilterQuery;
use crate::{jws::Jws, query::FilterQuery};
use ceramic_event::{
Base64String, Base64UrlString, Jws, MultiBase32String, MultiBase36String, StreamId,
StreamIdType,
unvalidated::signed, Base64String, Base64UrlString, MultiBase32String, MultiBase36String,
StreamId, StreamIdType,
};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -39,7 +39,7 @@ pub struct BlockData<T: Serialize> {
pub data: Option<T>,
/// Signature for block
#[serde(skip_serializing_if = "Option::is_none")]
pub jws: Option<Jws>,
pub jws: Option<signed::Envelope>,
/// IPFS Linked Block
#[serde(skip_serializing_if = "Option::is_none")]
pub linked_block: Option<Base64String>,
Expand Down
131 changes: 131 additions & 0 deletions src/jws.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use std::{collections::BTreeMap, str::FromStr};

use ceramic_event::{
ssi, unvalidated::signed::Signer, Base64String, Base64UrlString, Cid, MultiBase32String,
};
use serde::{Deserialize, Serialize};

/// The fields associated with the signature used to sign a JWS
#[derive(Debug, Serialize, Deserialize)]
pub struct JwsSignature {
/// Protected header
pub protected: Option<Base64String>,
/// Signature
pub signature: Base64UrlString,
}

/// Builder used to create JWS
pub struct JwsBuilder<S> {
signer: S,
additional: BTreeMap<String, serde_json::Value>,
}

impl<S: Signer> JwsBuilder<S> {
pub fn new(signer: S) -> Self {
Self {
signer,
additional: BTreeMap::new(),
}
}

pub fn with_additional(mut self, key: String, value: serde_json::Value) -> Self {
self.additional.insert(key, value);
self
}

pub fn replace_additional(mut self, additional: BTreeMap<String, serde_json::Value>) -> Self {
self.additional = additional;
self
}

pub fn build_for_cid(self, cid: &Cid) -> anyhow::Result<Jws> {
let cid_str = Base64UrlString::from_cid(cid);
let link = MultiBase32String::try_from(cid)?;
Jws::new(&self.signer, cid_str, Some(link), self.additional)
}

pub fn build_for_data<T: Serialize>(self, input: &T) -> anyhow::Result<Jws> {
let input = serde_json::to_vec(input)?;
let input = Base64UrlString::from(input);
Jws::new(&self.signer, input, None, self.additional)
}
}

/// A JWS object
#[derive(Debug, Serialize, Deserialize)]
pub struct Jws {
/// Link to CID that contains encoded data
#[serde(skip_serializing_if = "Option::is_none")]
pub link: Option<MultiBase32String>,
/// Encoded data
pub payload: Base64UrlString,
/// The signatures of the JWS
pub signatures: Vec<JwsSignature>,
}

impl Jws {
/// Create a builder for Jws objects
pub fn builder<S: Signer>(signer: S) -> JwsBuilder<S> {
JwsBuilder::new(signer)
}

/// Creates a new JWS from a payload that has already been serialized to Base64UrlString
pub fn new(
signer: &impl Signer,
input: Base64UrlString,
link: Option<MultiBase32String>,
additional_parameters: BTreeMap<String, serde_json::Value>,
) -> anyhow::Result<Self> {
let alg = signer.algorithm();
let header = ssi::jws::Header {
algorithm: alg,
type_: Some("JWT".to_string()),
key_id: Some(signer.id().id.clone()),
additional_parameters,
..Default::default()
};
// creates compact signature of protected.signature
let header_str = Base64String::from(serde_json::to_vec(&header)?);
let signing_input = format!("{}.{}", header_str.as_ref(), input.as_ref());
let signed = signer.sign(signing_input.as_bytes())?;
Ok(Self {
link,
payload: input,
signatures: vec![JwsSignature {
protected: Some(header_str),
signature: signed.into(),
}],
})
}

/// Get the payload of this jws
pub fn payload(&self) -> &Base64UrlString {
&self.payload
}

/// Get the additional parameters of the jws signature
pub fn additional(&self) -> anyhow::Result<BTreeMap<String, serde_json::Value>> {
let first = self
.signatures
.first()
.ok_or_else(|| anyhow::anyhow!("No signatures"))?;
let protected = first
.protected
.as_ref()
.ok_or_else(|| anyhow::anyhow!("No protected header"))?;
let protected = serde_json::from_slice::<ssi::jws::Header>(&protected.to_vec()?)?;
Ok(protected.additional_parameters)
}

/// Get the capability field for this jws
pub fn capability(&self) -> anyhow::Result<Cid> {
let additional = self.additional()?;
let cap = additional
.get("cap")
.ok_or_else(|| anyhow::anyhow!("No cap"))?
.as_str()
.ok_or_else(|| anyhow::anyhow!("cap is not a string"))?;
let cid = Cid::from_str(cap)?;
Ok(cid)
}
}
Loading

0 comments on commit 631829f

Please sign in to comment.