Skip to content

Commit

Permalink
feat: migrate to ssi 0.8
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan770 committed Jun 27, 2024
1 parent 277d52e commit 61c981d
Show file tree
Hide file tree
Showing 16 changed files with 1,792 additions and 2,316 deletions.
2,591 changes: 1,355 additions & 1,236 deletions Cargo.lock

Large diffs are not rendered by default.

17 changes: 9 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,27 @@ repository = "https://github.com/vaultie/teddybear"
categories = ["cryptography", "wasm"]

[workspace.dependencies]
ssi-dids = { git = "https://github.com/vaultie/ssi", branch = "ssi-jws-features" }
ssi-json-ld = { git = "https://github.com/vaultie/ssi", branch = "ssi-jws-features" }
ssi-jwk = { git = "https://github.com/vaultie/ssi", branch = "ssi-jws-features", default-features = false, features = ["ed25519"] }
ssi-jws = { git = "https://github.com/vaultie/ssi", branch = "ssi-jws-features", default-features = false, features = ["ed25519"] }
ssi-ldp = { git = "https://github.com/vaultie/ssi", branch = "ssi-jws-features", default-features = false, features = ["ed25519"] }
ssi-vc = { git = "https://github.com/vaultie/ssi", branch = "ssi-jws-features" }
ssi-claims = { git = "https://github.com/vaultie/ssi", branch = "ed25519-from-type", features = ["w3c", "ed25519"] }
ssi-dids-core = { git = "https://github.com/vaultie/ssi", branch = "ed25519-from-type" }
ssi-json-ld = { git = "https://github.com/vaultie/ssi", branch = "ed25519-from-type" }
ssi-jwk = { git = "https://github.com/vaultie/ssi", branch = "ed25519-from-type", default-features = false, features = ["ed25519"] }
ssi-jws = { git = "https://github.com/vaultie/ssi", branch = "ed25519-from-type", default-features = false, features = ["ed25519"] }
ssi-vc = { git = "https://github.com/vaultie/ssi", branch = "ed25519-from-type" }
ssi-verification-methods = { git = "https://github.com/vaultie/ssi", branch = "ed25519-from-type", default-features = false, features = ["ed25519"] }

base64 = "0.21.7"
ed25519-dalek = "2.1.0"
iref = "2.1.1"
iref = "3.1.4"
multibase = { version = "0.9.1", default-features = false }
serde = { version = "1.0.195", features = ["derive"] }
serde_json = "1.0.111"
static-iref = "3.0.0"
thiserror = "1.0.56"
tokio = { version = "1.35.1", features = ["macros"] }

teddybear-crypto = { path = "crates/teddybear-crypto" }
teddybear-did-key = { path = "crates/teddybear-did-key" }
teddybear-jwe = { path = "crates/teddybear-jwe" }
teddybear-status-list = { path = "crates/teddybear-status-list" }
teddybear-vc = { path = "crates/teddybear-vc" }

[profile.release]
Expand Down
6 changes: 4 additions & 2 deletions crates/teddybear-crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ repository.workspace = true
categories.workspace = true

[dependencies]
ed25519-dalek = { workspace = true }
ssi-dids = { workspace = true }
ssi-dids-core = { workspace = true }
ssi-jwk = { workspace = true }
ssi-jws = { workspace = true }
ssi-verification-methods = { workspace = true }

ed25519-dalek = { workspace = true }
teddybear-did-key = { workspace = true }
multibase = { workspace = true }
thiserror = { workspace = true }
149 changes: 99 additions & 50 deletions crates/teddybear-crypto/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
use std::marker::PhantomData;
use std::{borrow::Cow, marker::PhantomData, sync::Arc};

use ed25519_dalek::SigningKey;
use ssi_dids::{did_resolve::easy_resolve, DIDMethod, Document, Source, VerificationMethod};
use ssi_dids_core::{
document::DIDVerificationMethod, method_resolver::VerificationMethodDIDResolver,
resolution::Options, DIDResolver, Document, Unexpected, DID,
};
use ssi_jwk::{Algorithm, Base64urlUInt, OctetParams, Params};
use ssi_jws::{
decode_jws_parts, decode_verify, encode_sign_custom_header, split_jws, verify_bytes,
DecodedJWS, Header,
decode_verify, encode_sign_custom_header, verify_bytes, CompactJWSStr, DecodeError, Header,
InvalidCompactJWS, InvalidHeader,
};
use ssi_verification_methods::{Ed25519VerificationKey2020, MethodWithSecret, Signer};
use thiserror::Error;

pub use ssi_jwk::JWK;
pub use teddybear_did_key::DidKey;

#[derive(Error, Debug)]
pub enum JwsError {
#[error("expected the signer JWK to be embedded within the JWS")]
MissingSignerKey,

#[error("the provided JWS value is invalid")]
InvalidSignature,

#[error(transparent)]
DecodeError(#[from] DecodeError),

#[error(transparent)]
InvalidCompactJWS(#[from] InvalidCompactJWS),

#[error(transparent)]
InvalidHeader(#[from] InvalidHeader),
}

#[derive(Error, Debug)]
pub enum Error {
#[error("provided JWK is missing a private key value")]
Expand All @@ -24,13 +46,16 @@ pub enum Error {
Jwk(#[from] ssi_jwk::Error),

#[error(transparent)]
Jws(#[from] ssi_jws::Error),
Jws(#[from] JwsError),

#[error(transparent)]
MultibaseError(#[from] multibase::Error),

#[error(transparent)]
DidResolve(#[from] ssi_dids::Error),
InvalidDid(#[from] Unexpected),

#[error(transparent)]
DidResolve(#[from] ssi_dids_core::resolution::Error),
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -141,19 +166,29 @@ impl Ed25519<Public> {
}

pub async fn from_did(did: &str) -> Result<Self, Error> {
let document = easy_resolve(did, &DidKey).await?;
let did = DID::new(did).map_err(|e| e.1)?;

let document = VerificationMethodDIDResolver::<_, Ed25519VerificationKey2020>::new(DidKey)
.resolve_with(did, Options::default())
.await?
.document
.into_document();

let ed25519 = extract_key_info(
String::from("Ed25519"),
&document,
first_verification_method(document.verification_method.as_deref())
document
.verification_method
.first()
.expect("teddybear-did-key should provide at least one ed25519 key"),
)?;

let x25519 = extract_key_info(
String::from("X25519"),
&document,
first_verification_method(document.key_agreement.as_deref())
document
.verification_relationships
.key_agreement
.first()
.and_then(|val| val.as_value())
.expect("teddybear-did-key should provide at least one x25519 key"),
)?;

Expand Down Expand Up @@ -207,23 +242,33 @@ impl<T> Ed25519<T> {

async fn parts_from_jwk(mut jwk: JWK) -> Result<(Document, KeyInfo, KeyInfo), Error> {
let did = DidKey
.generate(&Source::Key(&jwk))
.generate(&jwk)
.expect("ed25519 key should produce a correct did document");

let document = easy_resolve(&did, &DidKey).await?;
let document = VerificationMethodDIDResolver::<_, Ed25519VerificationKey2020>::new(DidKey)
.resolve_with(did.as_did(), Options::default())
.await?
.document
.into_document();

jwk.key_id = Some(
first_verification_method(document.verification_method.as_deref())
document
.verification_method
.first()
.expect("at least one key is expected")
.get_id(&document.id),
.id
.to_string(),
);
jwk.algorithm = Some(Algorithm::EdDSA);

let x25519 = extract_key_info(
String::from("X25519"),
&document,
first_verification_method(document.key_agreement.as_deref())
.expect("at least one key is expected"),
document
.verification_relationships
.key_agreement
.first()
.and_then(|val| val.as_value())
.expect("teddybear-did-key should provide at least one x25519 key"),
)?;

Ok((document, KeyInfo { jwk }, x25519))
Expand All @@ -242,45 +287,56 @@ impl<T> PartialEq<JWK> for Ed25519<T> {
}
}

impl Signer<Ed25519VerificationKey2020> for Ed25519<Private> {
type MessageSigner = MethodWithSecret<Ed25519VerificationKey2020, JWK>;

async fn for_method(
&self,
method: Cow<'_, Ed25519VerificationKey2020>,
) -> Option<Self::MessageSigner> {
if method.id.as_str() != self.ed25519_did() {
return None;
}

Some(MethodWithSecret::new(
method.into_owned(),
Arc::new(self.ed25519.jwk.clone()),
))
}
}

#[inline]
pub fn verify_jws(jws: &str, key: &JWK) -> Result<Vec<u8>, Error> {
Ok(decode_verify(jws, key)?.1)
Ok(decode_verify(jws, key)
.map_err(|_| JwsError::InvalidSignature)?
.1)
}

#[inline]
pub fn verify_jws_with_embedded_jwk(jws: &str) -> Result<(JWK, Vec<u8>), Error> {
let (header_b64, payload_enc, signature_b64) = split_jws(jws)?;

let DecodedJWS {
header,
signing_input,
payload,
signature,
} = decode_jws_parts(header_b64, payload_enc.as_bytes(), signature_b64)?;
let jws = CompactJWSStr::from_string(jws)
.map_err(|e| JwsError::from(InvalidCompactJWS(e.0.to_string())))?
.decode()
.map_err(JwsError::from)?;

let key = header.jwk.ok_or(ssi_jws::Error::InvalidSignature)?;
let key = jws.header.jwk.ok_or(JwsError::MissingSignerKey)?;

verify_bytes(header.algorithm, &signing_input, &key, &signature)?;
verify_bytes(jws.header.algorithm, &jws.payload, &key, &jws.signature)
.map_err(|_| JwsError::InvalidSignature)?;

Ok((key, payload))
Ok((key, jws.payload.into_owned()))
}

#[inline]
fn extract_key_info(
curve: String,
document: &Document,
verification_method: &VerificationMethod,
verification_method: &DIDVerificationMethod,
) -> Result<KeyInfo, Error> {
let id = verification_method.get_id(&document.id);

let public_key_multibase = match verification_method {
VerificationMethod::Map(map) => map
.property_set
.as_ref()
.and_then(|val| val.get("publicKeyMultibase").and_then(|val| val.as_str()))
.expect("publicKeyMultibase should always be present"),
_ => unreachable!(),
};
let public_key_multibase = verification_method
.properties
.get("publicKeyMultibase")
.and_then(|val| val.as_str())
.expect("publicKeyMultibase should always be present");

let public_key = multibase::decode(public_key_multibase)?.1;

Expand All @@ -290,15 +346,8 @@ fn extract_key_info(
private_key: None,
}));

jwk.key_id = Some(id);
jwk.key_id = Some(verification_method.id.to_string());
jwk.algorithm = Some(Algorithm::EdDSA);

Ok(KeyInfo { jwk })
}

#[inline]
fn first_verification_method(
verification_methods: Option<&[VerificationMethod]>,
) -> Option<&VerificationMethod> {
verification_methods.and_then(|methods| methods.first())
}
8 changes: 4 additions & 4 deletions crates/teddybear-did-key/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ repository.workspace = true
categories.workspace = true

[dependencies]
async-trait = "0.1.74"
ssi-dids-core = { workspace = true }
ssi-jwk = { workspace = true }

ed25519-dalek = { workspace = true }
iref = { workspace = true }
multibase = { workspace = true }
serde_json = { workspace = true }
ssi-dids = { workspace = true }
ssi-jwk = { workspace = true }
static-iref = "2.0.0"
static-iref = { workspace = true }
Loading

0 comments on commit 61c981d

Please sign in to comment.