Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
nanderstabel committed Oct 22, 2024
1 parent 25f7a96 commit 38bff4d
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 260 deletions.
138 changes: 33 additions & 105 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,10 @@ tauri-winres = "=0.1"

agent_shared = { git = "https://[email protected]/impierce/ssi-agent.git", rev = "1823810" }
# did_manager = { git = "https://[email protected]/impierce/did-manager.git", tag = "v1.0.0-beta.3" }
did_manager = { git = "https://[email protected]/impierce/did-manager.git", rev = "c1cfda0" }
did_manager = { git = "https://[email protected]/impierce/did-manager.git", rev = "c8a141d" }
jsonwebtoken = "9.3"
log = "^0.4"
oid4vc = { git = "https://[email protected]/impierce/openid4vc.git", rev = "bc4d6d2" }
# oid4vc = { path = "../openid4vc" }
oid4vc = { git = "https://[email protected]/impierce/openid4vc.git", rev = "5933fd6" }
rand = "0.8"
serde_json = "1.0"
serial_test = "2.0"
Expand Down
10 changes: 5 additions & 5 deletions identity-wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ downcast-rs = "1.2"
dyn-clone = "1.0"
futures = "0.3"
icu = "1.4.0"
identity_credential = { git = "https://github.com/impierce/identity.rs", branch = "fix/compile-fixes", default-features = false, features = [
identity_credential = { git = "https://github.com/impierce/identity.rs", rev = "309c399", default-features = false, features = [
"credential",
"domain-linkage",
"presentation",
"validator",
"sd-jwt-vc"
] }
identity_core = { git = "https://github.com/impierce/identity.rs", branch = "fix/compile-fixes" }
identity_eddsa_verifier = { git = "https://github.com/impierce/identity.rs", branch = "fix/compile-fixes" }
identity_iota = { git = "https://github.com/impierce/identity.rs", branch = "fix/compile-fixes" }
identity_jose = { git = "https://github.com/impierce/identity.rs", branch = "fix/compile-fixes" }
identity_core = { git = "https://github.com/impierce/identity.rs", rev = "309c399" }
identity_eddsa_verifier = { git = "https://github.com/impierce/identity.rs", rev = "309c399" }
identity_iota = { git = "https://github.com/impierce/identity.rs", rev = "309c399" }
identity_jose = { git = "https://github.com/impierce/identity.rs", rev = "309c399" }
iota_stronghold = { version = "2.1" }
itertools = "0.10.5"
jsonwebtoken.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion identity-wallet/bindings/credentials/DisplayCredential.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { CredentialMetadata } from "./CredentialMetadata";

export interface DisplayCredential { id: string, issuer_name: string, data: any, metadata: CredentialMetadata, connection_id?: string, display_name: string, }
export interface DisplayCredential { id: string, format: string, issuer_name: string, data: any, metadata: CredentialMetadata, connection_id?: string, display_name: string, }
101 changes: 53 additions & 48 deletions identity-wallet/src/state/credentials/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,47 +62,42 @@ impl TryFrom<serde_json::Value> for VerifiableCredentialRecord {
fn try_from(verifiable_credential: serde_json::Value) -> Result<Self, AppError> {
let display_credential = {
// FIX THIS
if let Ok(sd_jwt_vc) = SdJwtVc::from_str(verifiable_credential.as_str().unwrap()) {
let (id, format, data, issuance_date) = if let Ok(sd_jwt_vc) = SdJwtVc::from_str(
verifiable_credential
.as_str()
.ok_or(AppError::Error("Credential is not a valid JWT".to_string()))?,
) {
info!("sd_jwt_vc: {:#?}", sd_jwt_vc);

let issuance_date = sd_jwt_vc.claims().iat.map(|iat| iat.to_rfc3339()).unwrap_or_default();
let credential_subject = sd_jwt_vc.clone().into_disclosed_object(&Sha256Hasher::new()).unwrap();

let hash = sha256::digest(
json!(
{
"type": ["VerifiableCredential"],
"credentialSubject": credential_subject
}
)
.to_string(),
);

DisplayCredential {
id: Uuid::from_slice(&hash.as_bytes()[..16]).unwrap().to_string(),
format: CredentialFormats::VcSdJwt(()),
data: json!({
"type": ["VerifiableCredential"],
"issuer": sd_jwt_vc.claims().iss,
"credentialSubject": credential_subject

}),
metadata: CredentialMetadata {
is_favorite: false,
date_added: DateUtils::new_date_string(),
date_issued: issuance_date.to_string(),
},
// The other fields will be filled in at a later stage.
..Default::default()
}
let credential_subject = sd_jwt_vc
.clone()
.into_disclosed_object(&Sha256Hasher::new())
.map_err(|_| AppError::Error("Failed to convert SD JWT VC to Disclosed Object".to_string()))?;

// TODO: We are using this hash as Credential ID so that we can prevent credential duplication in
// demo situations. Now we can actually delete Credentials in UniMe we don't need to use the hash of the
// credential as the ID anymore. We should simply generate a random UUID.
let hash = sha256::digest(json!(credential_subject).to_string());

let id = Uuid::from_slice(&hash.as_bytes()[..16]).unwrap().to_string();
let format = CredentialFormats::VcSdJwt(());
// TODO: Remove this workaround that is basically a way of disguising the SD JWT VC as a VC so that
// it can be displayed in the Frontend.
let data = json!({
"type": ["VerifiableCredential"],
"issuer": sd_jwt_vc.claims().iss,
"credentialSubject": credential_subject

});

(id, format, data, issuance_date)
} else {
let credential_display = get_unverified_jwt_claims(&verifiable_credential)?["vc"].clone();

info!(
"Credential Display: {:#?}",
serde_json::to_string_pretty(&credential_display).unwrap()
);

// TODO: We are using this hash as Credential ID so that we can prevent credential duplication in
// demo situations. Now we can actually delete Credentials in UniMe we don't need to use the hash of the
// credential as the ID anymore. We should simply generate a random UUID.
// Derive the hash from the credential display.
let hash = {
let type_value = credential_display["type"].clone();
Expand Down Expand Up @@ -131,19 +126,29 @@ impl TryFrom<serde_json::Value> for VerifiableCredentialRecord {
.as_str()
.map(ToString::to_string)
.unwrap_or_default();

DisplayCredential {
id: Uuid::from_slice(&hash.as_bytes()[..16]).unwrap().to_string(),
format: CredentialFormats::JwtVcJson(()),
data: credential_display,
metadata: CredentialMetadata {
is_favorite: false,
date_added: DateUtils::new_date_string(),
date_issued: issuance_date.to_string(),
},
// The other fields will be filled in at a later stage.
..Default::default()
}
let id = Uuid::from_slice(&hash.as_bytes()[..16]).unwrap().to_string();
let format = CredentialFormats::JwtVcJson(());
let data = credential_display;

(id, format, data, issuance_date)
};

info!(
"Credential Display: {:#?}",
serde_json::to_string_pretty(&data).unwrap()
);

DisplayCredential {
id,
format,
data,
metadata: CredentialMetadata {
is_favorite: false,
date_added: DateUtils::new_date_string(),
date_issued: issuance_date,
},
// The other fields will be filled in at a later stage.
..Default::default()
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,15 @@ use crate::{
user_prompt::CurrentUserPrompt,
AppState,
},
subject::SubjectWrapper,
};

use identity_credential::{
credential::Jwt,
presentation::Presentation,
sd_jwt_v2::{KeyBindingJwt, Sha256Hasher},
sd_jwt_v2::Sha256Hasher,
sd_jwt_vc::{SdJwtVc, SdJwtVcPresentationBuilder},
};
use identity_iota::{core::Timestamp, did::CoreDID};
use identity_iota::did::CoreDID;
use jsonwebtoken::Algorithm;
use log::info;
use oid4vc::oid4vp::{authorization_request::ClientMetadataParameters, oid4vp::PresentationInputType};
Expand Down Expand Up @@ -55,12 +54,6 @@ pub async fn handle_oid4vp_authorization_request(state: AppState, action: Action
.as_ref()
.ok_or(MissingManagerError("identity"))?
.provider_manager;
let subject = state_guard
.identity_manager
.as_ref()
.ok_or(MissingManagerError("identity"))?
.subject
.clone();

let oid4vp_authorization_request =
match serde_json::from_value(serde_json::json!(state.core_utils.active_connection_request)).unwrap() {
Expand All @@ -76,7 +69,7 @@ pub async fn handle_oid4vp_authorization_request(state: AppState, action: Action
.unwrap()
.iter()
.filter_map(|verifiable_credential_record| {
info!("verifiable_credential_record: {:#?}", verifiable_credential_record);
info!("Verifiable Credential Record: {:#?}", verifiable_credential_record);

let share_credential = credential_uuids
.contains(&verifiable_credential_record.display_credential.id.parse().unwrap())
Expand All @@ -95,31 +88,6 @@ pub async fn handle_oid4vp_authorization_request(state: AppState, action: Action
})
.collect();

let subject_syntax_type = state
.profile_settings
.preferred_did_methods
.first()
.unwrap()
.to_string();

let subject_wrapper = SubjectWrapper {
subject,
subject_syntax_type,
};

let sd_jwt_vcs: Vec<serde_json::Value> = verifiable_credentials
.iter()
.filter_map(|(format, vc)| {
if format == &CredentialFormats::VcSdJwt(()) {
Some(vc.clone())
} else {
None
}
})
.collect();

let now = Timestamp::now_utc();

let mut verifiable_presentation_input = vec![];
let mut presentation_submissions = vec![];

Expand All @@ -128,49 +96,53 @@ pub async fn handle_oid4vp_authorization_request(state: AppState, action: Action
serde_json::to_string_pretty(&oid4vp_authorization_request).unwrap()
);

let nonce = oid4vp_authorization_request.body.extension.nonce.clone();
let aud = oid4vp_authorization_request.body.client_id.clone();
let sd_jwt_vcs: Vec<serde_json::Value> = verifiable_credentials
.iter()
.filter_map(|(format, vc)| (format == &CredentialFormats::VcSdJwt(())).then(|| vc.clone()))
.collect();

info!("sd_jwt_vcs: {:#?}", sd_jwt_vcs);

let verifiable_credentials: Vec<serde_json::Value> = verifiable_credentials
.iter()
.filter_map(|(format, vc)| (format == &CredentialFormats::JwtVcJson(())).then(|| vc.clone()))
.collect();

let OID4VPClientMetadata { algorithm, .. } = get_oid4vp_client_name_and_logo_uri(&oid4vp_authorization_request);
info!("verifiable_credentials: {:#?}", verifiable_credentials);

for sd_jwt_vc in sd_jwt_vcs {
let sd_jwt_vc = sd_jwt_vc
.as_str()
.and_then(|sd_jwt_vc| SdJwtVc::parse(sd_jwt_vc).ok())
.ok_or(AppError::Error("Failed to parse SD-JWT VC".to_string()))?;

let alg = sd_jwt_vc
.header()
.get("alg")
.and_then(|alg| alg.as_str())
.map(ToString::to_string)
.unwrap_or(serde_json::json!(algorithm).as_str().unwrap().to_string());
let kb_jwt = KeyBindingJwt::builder()
.iat(now.to_unix())
.aud(aud.clone())
.nonce(nonce.clone())
.finish(&sd_jwt_vc, &Sha256Hasher::new(), &alg, &subject_wrapper)
.await
.map_err(|e| AppError::Error(format!("Failed to create KeyBindingJwt for SD-JWT VC: {:?}", e)))?;

let (sd_jwt_vc, _) = SdJwtVcPresentationBuilder::new(sd_jwt_vc, &Sha256Hasher::new())
.map_err(|e| {
AppError::Error(format!(
"Failed to create SD-JWT VC Presentation Builder for SD-JWT VC: {:?}",
e
))
})?
.attach_key_binding_jwt(kb_jwt)
// TODO: Implement Key Binding
// .attach_key_binding_jwt(kb_jwt)
.finish()
.map_err(|e| AppError::Error(format!("Failed to attach KeyBindingJwt to SD-JWT VC: {:?}", e)))?;

info!(
"SD-JWT VC: {}",
serde_json::to_string_pretty(&sd_jwt_vc.clone().into_disclosed_object(&Sha256Hasher::new()).unwrap())
.unwrap()
);

let presentation_submission = create_sd_jwt_presentation_submission(
&oid4vp_authorization_request.body.extension.presentation_definition,
&[serde_json::json!(sd_jwt_vc
.clone()
.into_disclosed_object(&Sha256Hasher::new())
// FIX THIS
.unwrap())],
.map_err(|e| AppError::Error(format!(
"Failed to create Disclosed Object for SD-JWT VC: {:?}",
e
)))?)],
)
.map_err(|e| {
AppError::Error(format!(
Expand All @@ -183,19 +155,6 @@ pub async fn handle_oid4vp_authorization_request(state: AppState, action: Action
presentation_submissions.push(presentation_submission);
}

let verifiable_credentials: Vec<serde_json::Value> = verifiable_credentials
.iter()
.filter_map(|(format, vc)| {
if format == &CredentialFormats::JwtVcJson(()) {
Some(vc.clone())
} else {
None
}
})
.collect();

info!("verifiable_credentials: {:#?}", verifiable_credentials);

if !verifiable_credentials.is_empty() {
let presentation_submission = create_presentation_submission(
&oid4vp_authorization_request.body.extension.presentation_definition,
Expand Down Expand Up @@ -240,16 +199,25 @@ pub async fn handle_oid4vp_authorization_request(state: AppState, action: Action
));
}

let presentation_submission = if presentation_submissions.len() > 1 {
merge_submissions(presentation_submissions)
} else {
presentation_submissions.pop().ok_or(AppError::Error(
"Failed to create a Presentation Submission".to_string(),
))?
};

info!("Verifiable Presentation Input: {:#?}", verifiable_presentation_input);

if verifiable_presentation_input.len() != 1 {
let presentation_submission = if presentation_submissions.len() > 1 {
merge_submissions(presentation_submissions.clone())
} else {
presentation_submissions.pop().ok_or(AppError::Error(
"Failed to create a Presentation Submission".to_string(),
))?
};

// If multiple presentations are provided, this means that the `vp_token` in the Authorization
// Response will be a sequence which cannot be serialized into a x-www-form-urlencoded string by `reqwest`.
// See: https://github.com/nox/serde_urlencoded/issues/75#issuecomment-648257888
return Err(AppError::Error(
"Sending multiple presentations is not supported".to_string(),
));
}

info!("get the provider_manager");

info!("generating response");
Expand All @@ -258,7 +226,7 @@ pub async fn handle_oid4vp_authorization_request(state: AppState, action: Action
&oid4vp_authorization_request,
oid4vp::AuthorizationResponseInput {
verifiable_presentation_input,
presentation_submission,
presentation_submission: presentation_submissions.first().clone().unwrap().clone(),
},
)
.await
Expand Down Expand Up @@ -365,8 +333,9 @@ pub fn get_oid4vp_client_name_and_logo_uri(
.get(&ClaimFormatDesignation::JwtVcJson)
.and_then(|claim_format_property| match claim_format_property {
ClaimFormatProperty::Alg(alg) => alg.first().cloned(),
ClaimFormatProperty::SdJwt { sd_jwt_alg_values, .. } => sd_jwt_alg_values.first().cloned(),
// TODO: implement `ProofType`.
ClaimFormatProperty::ProofType(_) | _ => None,
ClaimFormatProperty::ProofType(_) => None,
})
.unwrap_or(Algorithm::EdDSA);

Expand Down
Loading

0 comments on commit 38bff4d

Please sign in to comment.