From 9111a3528b02931186ec62b5f5c450e8601cd55e Mon Sep 17 00:00:00 2001 From: Simon Bihel Date: Tue, 2 Jul 2024 08:18:09 +0100 Subject: [PATCH] Ed25519Signature2020 test suite passing --- .github/workflows/cd.yml | 11 +++-- .github/workflows/ci.yml | 96 +++++++++++++++++++------------------- src/credentials.rs | 93 ++++++++++++++++-------------------- src/error.rs | 55 ++++++++++------------ src/identifiers.rs | 16 ++----- src/presentations.rs | 47 ++++++++----------- tests/vc-di-bbs-test-suite | 2 +- 7 files changed, 143 insertions(+), 177 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index ebcbd87..66f2d34 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -1,10 +1,11 @@ -name: CD +name: cd +# Temporarily disabled until ssi is published on: - push: - branches: [ main ] - release: - types: [published, created, edited] + # push: + # branches: [ main ] + # release: + # types: [published, created, edited] workflow_dispatch: jobs: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7b7d60..0492714 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,51 +45,51 @@ jobs: - name: Clippy run: cargo clippy - # vc_api_test_suites: - # runs-on: ubuntu-latest - # strategy: - # matrix: - # suite: - # # - "vc-api-issuer-test-suite" # not updated to latest test suites config - # # - "vc-api-verifier-test-suite" # not updated to latest test suites config - # # - "vc-di-ecdsa-test-suite" # ssi lost support for ecdsa and they updated the ids - # # - "vc-di-eddsa-test-suite" # ssi doesn't have support for eddsa-rdfc-2022 just yet - # # - "vc-di-ed25519signature2020-test-suite" # ssi needs support for editing VC context - # # - "did-key-test-suite" # not updated to latest test suites config - # # - "vc-data-model-2.0-test-suite" # ssi doesn't yet have full support for VCDM 2 - # # - "vc-di-bbs-test-suite" # ssi doesn't yet have support for BBS - # steps: - # - uses: actions/checkout@v4 - # with: - # path: didkit-http - # submodules: true - # - name: Checkout SSI library - # uses: actions/checkout@v4 - # with: - # repository: spruceid/ssi - # path: ssi - # ref: ${{env.SSI_REF}} - # - name: Rust Cache - # uses: Swatinem/rust-cache@v2 - # with: - # workspaces: | - # didkit-http - # shared-key: "vc-api" - # - uses: actions/setup-node@v4 - # with: - # node-version: 18 - # - name: Start VC API - # run: | - # cargo build - # DIDKIT_HTTP_ISSUER_KEYS='[{"kty":"OKP","crv":"Ed25519","x":"HvjBEw94RHAh9KkiD385aYZNxGkxIkwBcrLBY5Z7Koo","d":"1onWu34oC29Y09qCRl0aD2FOp5y5obTqHZxQQRT3-bs"}]' cargo run & - # - name: Start HTTPS Proxy - # run: npx local-ssl-proxy --source 9000 --target 3000 --hostname 127.0.0.1 & - # - name: Setup Test Suite - # run: | - # cd tests/${{ matrix.suite }} - # npm i - # ln ../localConfig.cjs localConfig.cjs - # - name: Run Test Suite - # run: | - # cd tests/${{ matrix.suite }} - # npm test + vc_api_test_suites: + runs-on: ubuntu-latest + strategy: + matrix: + suite: + # - "vc-api-issuer-test-suite" # not updated to latest test suites config + # - "vc-api-verifier-test-suite" # not updated to latest test suites config + # - "vc-di-ecdsa-test-suite" # ssi lost support for ecdsa and they updated the ids + # - "vc-di-eddsa-test-suite" # ssi doesn't have support for eddsa-rdfc-2022 just yet + - "vc-di-ed25519signature2020-test-suite" + # - "did-key-test-suite" # not updated to latest test suites config + # - "vc-data-model-2.0-test-suite" # ssi doesn't yet have full support for VCDM 2 + # - "vc-di-bbs-test-suite" # ssi doesn't yet have support for BBS + steps: + - uses: actions/checkout@v4 + with: + path: didkit-http + submodules: true + - name: Checkout SSI library + uses: actions/checkout@v4 + with: + repository: spruceid/ssi + path: ssi + ref: ${{env.SSI_REF}} + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + didkit-http + shared-key: "vc-api" + - uses: actions/setup-node@v4 + with: + node-version: 18 + - name: Start VC API + run: | + cargo build + DIDKIT_HTTP_ISSUER_KEYS='[{"kty":"OKP","crv":"Ed25519","x":"HvjBEw94RHAh9KkiD385aYZNxGkxIkwBcrLBY5Z7Koo","d":"1onWu34oC29Y09qCRl0aD2FOp5y5obTqHZxQQRT3-bs"}]' cargo run & + - name: Start HTTPS Proxy + run: npx local-ssl-proxy --source 9000 --target 3000 --hostname 127.0.0.1 & + - name: Setup Test Suite + run: | + cd tests/${{ matrix.suite }} + npm i + ln ../localConfig.cjs localConfig.cjs + - name: Run Test Suite + run: | + cd tests/${{ matrix.suite }} + npm test diff --git a/src/credentials.rs b/src/credentials.rs index 98aa6e9..2aa48d3 100644 --- a/src/credentials.rs +++ b/src/credentials.rs @@ -1,20 +1,20 @@ -use std::borrow::{Borrow, BorrowMut, Cow}; +use std::borrow::Cow; -use anyhow::Context; +use anyhow::{anyhow, Context}; use axum::{http::StatusCode, Extension, Json}; use serde::{Deserialize, Serialize}; use ssi::{ claims::{ - data_integrity::{CryptographicSuite, DataIntegrity}, + data_integrity::CryptographicSuite, vc::{ v1::{Credential as _, ToJwtClaims}, - v2::{self, Credential as _}, + v2::Credential as _, AnyJsonCredential, AnySpecializedJsonCredential, }, JsonCredentialOrJws, VerifiableClaims, VerificationEnvironment, }, dids::{AnyDidMethod, DIDResolver, VerificationMethodDIDResolver, DID}, - json_ld::{self, json_ld::Iri}, + json_ld::json_ld::Iri, prelude::JWSPayload, verification_methods::{ AnyMethod, GenericVerificationMethod, MaybeJwkVerificationMethod, ReferenceOrOwned, @@ -56,52 +56,45 @@ pub async fn issue( let resolver = VerificationMethodDIDResolver::<_, AnyMethod>::new(AnyDidMethod::default()); let issuer = match req.credential { - AnySpecializedJsonCredential::V1(ref vc) => &vc.issuer, - AnySpecializedJsonCredential::V2(ref vc) => &vc.issuer, + AnySpecializedJsonCredential::V1(ref vc) => vc.issuer.clone(), + AnySpecializedJsonCredential::V2(ref vc) => vc.issuer.clone(), }; // Find an appropriate verification method. let method = match &req.options.ldp_options.input_options.verification_method { - Some(method) => { - resolver - .resolve_verification_method(Some(issuer.id().as_iri()), Some(method.borrowed())) - .await? - } + Some(method) => resolver + .resolve_verification_method(Some(issuer.id().as_iri()), Some(method.borrowed())) + .await + .context("Could not resolve VM")?, None => { - let did = DID::new(issuer.id()).map_err(|_| { - ( - StatusCode::INTERNAL_SERVER_ERROR, - "Could not get any verification method for issuer URI".to_string(), - ) - })?; + let did = DID::new(issuer.id()) + .map_err(|_| anyhow!("Could not get any verification method for issuer URI"))?; - let output = resolver.resolve(did).await.map_err(|_| { - ( - StatusCode::INTERNAL_SERVER_ERROR, - "Could not fetch issuer DID document".to_string(), - ) - })?; + let output = resolver + .resolve(did) + .await + .context("Could not fetch issuer DID document")?; let method = output .document .into_document() .into_any_verification_method() - .ok_or(( - StatusCode::INTERNAL_SERVER_ERROR, - "Could not get any verification method for issuer DID document", - ))?; + .context("Could not get any verification method for issuer DID document")?; req.options.ldp_options.input_options.verification_method = Some(ReferenceOrOwned::Reference(method.id.clone().into_iri())); - Cow::Owned(GenericVerificationMethod::from(method).try_into()?) + Cow::Owned( + GenericVerificationMethod::from(method) + .try_into() + .context("Could not convert VM")?, + ) } }; - let public_jwk = method.try_to_jwk().ok_or(( - StatusCode::INTERNAL_SERVER_ERROR, - "Could not get any verification method for issuer DID".to_string(), - ))?; + let public_jwk = method + .try_to_jwk() + .context("Could not get any verification method for issuer DID")?; if req.options.ldp_options.type_ == Some("DataIntegrityProof".to_string()) { if req.options.ldp_options.cryptosuite.is_none() { @@ -181,26 +174,19 @@ pub async fn issue( ) .await .context("Failed to sign VC")?; - //if let Some(p) = vc.proofs.first_mut() { - // if let Some(proof_context) = &p.context { - // match vc.claims { - // AnySpecializedJsonCredential::V1(ref mut vc) => { - // let mut context = vc.context.as_ref(); - // *context = &json_ld::syntax::Context::Many( - // context.into_iter().chain(proof_context).cloned().collect(), - // ); - // } - // AnySpecializedJsonCredential::V2(ref mut vc) => { - // //let context = vc.context.borrow_mut(); - // //context = json_ld::syntax::Context::Many( - // // context.into_iter().chain(proof_context).cloned().collect(), - // //); - // todo!(); - // } - // } - // p.context = None; - // } - //} + if let Some(p) = vc.proofs.first_mut() { + if let Some(proof_context) = &p.context { + match vc.claims { + AnySpecializedJsonCredential::V1(ref mut vc) => { + vc.context.extend(proof_context.clone()); + } + AnySpecializedJsonCredential::V2(ref mut vc) => { + vc.context.extend(proof_context.clone()); + } + } + p.context = None; + } + } JsonCredentialOrJws::Credential(vc) } @@ -347,7 +333,6 @@ mod test { let _ = issue(Extension(keys), CustomErrorJson(req)).await.unwrap(); } - #[ignore = "Depends on https://github.com/spruceid/ssi/pull/560"] #[test(tokio::test)] async fn issue_ed25519() { let keys = default_keys(); diff --git a/src/error.rs b/src/error.rs index 7595b16..eddb3d3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,11 +1,9 @@ +use anyhow::{anyhow, Context}; use axum::{ http::StatusCode, response::{IntoResponse, Response}, }; -use ssi::{ - dids::resolution, - verification_methods::{InvalidVerificationMethod, VerificationMethodResolutionError}, -}; +use ssi::dids::resolution; use tracing::{debug, error}; #[derive(Debug, Clone)] @@ -20,24 +18,6 @@ pub enum ErrorBody { // Json(serde_json::Value), } -impl From for Error { - fn from(value: VerificationMethodResolutionError) -> Self { - Error { - status: StatusCode::INTERNAL_SERVER_ERROR, - body: ErrorBody::Text(value.to_string()), - } - } -} - -impl From for Error { - fn from(value: InvalidVerificationMethod) -> Self { - Error { - status: StatusCode::INTERNAL_SERVER_ERROR, - body: ErrorBody::Text(value.to_string()), - } - } -} - impl From for Error { fn from(value: resolution::Error) -> Self { Self { @@ -49,7 +29,12 @@ impl From for Error { | resolution::Error::InvalidOptions | resolution::Error::NoRepresentation => StatusCode::BAD_REQUEST, resolution::Error::MethodNotSupported(_) => StatusCode::NOT_IMPLEMENTED, - resolution::Error::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, + e @ resolution::Error::Internal(_) => { + return Err::<(), _>(e) + .context("Internal resolution error") + .unwrap_err() + .into() + } }, body: ErrorBody::Text(format!("Resolution failed: {value}")), } @@ -58,7 +43,8 @@ impl From for Error { impl From for Error { fn from(value: resolution::DerefError) -> Self { - let status = match &value { + let body = ErrorBody::Text(format!("Dereferencing failed: {value}")); + let status = match value { resolution::DerefError::Resolution(e) => match e { resolution::Error::NotFound => StatusCode::NOT_FOUND, resolution::Error::RepresentationNotSupported(_) => StatusCode::NOT_ACCEPTABLE, @@ -67,7 +53,12 @@ impl From for Error { | resolution::Error::InvalidOptions | resolution::Error::NoRepresentation => StatusCode::BAD_REQUEST, resolution::Error::MethodNotSupported(_) => StatusCode::NOT_IMPLEMENTED, - resolution::Error::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, + e @ resolution::Error::Internal(_) => { + return Err::<(), _>(e) + .context("Internal resolution error") + .unwrap_err() + .into() + } }, resolution::DerefError::NotFound | resolution::DerefError::ResourceNotFound(_) => { StatusCode::NOT_FOUND @@ -77,13 +68,17 @@ impl From for Error { | resolution::DerefError::UnsupportedMultipleServiceEndpoints => { StatusCode::NOT_IMPLEMENTED } - _ => StatusCode::INTERNAL_SERVER_ERROR, + e @ resolution::DerefError::ServiceEndpointConstructionFailed(_) => { + return Err::<(), _>(e) + .context("service endpoint construction failed") + .unwrap_err() + .into() + } + resolution::DerefError::FragmentConflict => return anyhow!("Fragment conflict").into(), + resolution::DerefError::NullDereference => return anyhow!("Null Dereference").into(), }; - Self { - status, - body: ErrorBody::Text(format!("Dereferencing failed: {value}")), - } + Self { status, body } } } diff --git a/src/identifiers.rs b/src/identifiers.rs index 27d36a9..42785ce 100644 --- a/src/identifiers.rs +++ b/src/identifiers.rs @@ -90,12 +90,8 @@ pub async fn resolve( match content_type { ContentType::JsonLdDidResolution => { serde_json::to_vec(&ResolutionResult::Success { - content: String::from_utf8(output.document).map_err(|_| { - ( - StatusCode::INTERNAL_SERVER_ERROR, - "Non UTF-8 representation".to_string(), - ) - })?, + content: String::from_utf8(output.document) + .context("Non UTF-8 representation".to_string())?, metadata: output.metadata, document_metadata: output.document_metadata, }) @@ -145,12 +141,8 @@ pub async fn resolve( match content_type { ContentType::JsonLdDidResolution => { serde_json::to_vec(&ResolutionResult::Success { - content: String::from_utf8(bytes).map_err(|_| { - ( - StatusCode::INTERNAL_SERVER_ERROR, - "Non UTF-8 representation".to_string(), - ) - })?, + content: String::from_utf8(bytes) + .context("Non UTF-8 representation".to_string())?, metadata: output.metadata, document_metadata: output.content_metadata, }) diff --git a/src/presentations.rs b/src/presentations.rs index 0278255..dea2453 100644 --- a/src/presentations.rs +++ b/src/presentations.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use anyhow::Context; +use anyhow::{anyhow, Context}; use axum::{http::StatusCode, Extension, Json}; use serde::{Deserialize, Serialize}; use ssi::{ @@ -49,46 +49,39 @@ pub async fn issue( // Find an appropriate verification method. let method = match &req.options.ldp_options.input_options.verification_method { - Some(method) => { - resolver - .resolve_verification_method(Some(holder.as_iri()), Some(method.borrowed())) - .await? - } + Some(method) => resolver + .resolve_verification_method(Some(holder.as_iri()), Some(method.borrowed())) + .await + .context("Could not resolve VM")?, None => { - let did = DID::new(&holder).map_err(|_| { - ( - StatusCode::INTERNAL_SERVER_ERROR, - "Could not get any verification method for holder URI".to_string(), - ) - })?; + let did = DID::new(&holder) + .map_err(|_| anyhow!("Could not get any verification method for holder URI"))?; - let output = resolver.resolve(did).await.map_err(|_| { - ( - StatusCode::INTERNAL_SERVER_ERROR, - "Could not fetch holder DID document".to_string(), - ) - })?; + let output = resolver + .resolve(did) + .await + .context("Could not fetch holder DID document")?; let method = output .document .into_document() .into_any_verification_method() - .ok_or(( - StatusCode::INTERNAL_SERVER_ERROR, - "Could not get any verification method for holder DID document", - ))?; + .context("Could not get any verification method for holder DID document")?; req.options.ldp_options.input_options.verification_method = Some(ReferenceOrOwned::Reference(method.id.clone().into_iri())); - Cow::Owned(GenericVerificationMethod::from(method).try_into()?) + Cow::Owned( + GenericVerificationMethod::from(method) + .try_into() + .context("Could not convert VM")?, + ) } }; - let public_jwk = method.try_to_jwk().ok_or(( - StatusCode::INTERNAL_SERVER_ERROR, - "Could not get any verification method for holder DID".to_string(), - ))?; + let public_jwk = method + .try_to_jwk() + .context("Could not get any verification method for holder DID")?; let res = match req.options.proof_format { ProofFormat::Jwt => { diff --git a/tests/vc-di-bbs-test-suite b/tests/vc-di-bbs-test-suite index 9e96dce..a767782 160000 --- a/tests/vc-di-bbs-test-suite +++ b/tests/vc-di-bbs-test-suite @@ -1 +1 @@ -Subproject commit 9e96dce052c0c487144f74205f3f9e829fac6ce5 +Subproject commit a767782f0a36e42198c744ec10bc6ecc1b811d67