Skip to content

Commit

Permalink
wip: search credentials method
Browse files Browse the repository at this point in the history
Signed-off-by: Ryan Tate <[email protected]>
  • Loading branch information
Ryanmtate committed Sep 25, 2024
1 parent 03c8087 commit 8e0e649
Show file tree
Hide file tree
Showing 6 changed files with 296 additions and 35 deletions.
18 changes: 9 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ impl From<String> for CredentialType {
}
}

impl From<&CredentialType> for String {
fn from(cred_type: &CredentialType) -> Self {
cred_type.0.to_owned()
}
}

uniffi::custom_newtype!(KeyAlias, String);
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct KeyAlias(pub String);
Expand Down
2 changes: 1 addition & 1 deletion src/credential/jwt_vc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ impl JwtVc {
self.jws.as_bytes().to_vec()
}

fn from_compact_jws_bytes(
pub(crate) fn from_compact_jws_bytes(
id: Uuid,
raw: Vec<u8>,
key_alias: Option<KeyAlias>,
Expand Down
121 changes: 119 additions & 2 deletions src/credential/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ use crate::{CredentialType, KeyAlias, Uuid};
use json_vc::{JsonVc, JsonVcEncodingError, JsonVcInitError};
use jwt_vc::{JwtVc, JwtVcInitError};
use mdoc::{Mdoc, MdocEncodingError, MdocInitError};
use oid4vp::core::credential_format::ClaimFormatDesignation;
use oid4vp::{
core::{
credential_format::ClaimFormatDesignation, presentation_definition::PresentationDefinition,
},
deps::jsonpath_lib::{self, JsonPathError},
};
use sd_jwt_vc::{SdJwtVc, SdJwtVcError};
use serde::{Deserialize, Serialize};
use ssi::{claims::vc::syntax::NonEmptyObject, prelude::AnyJsonCredential};

Expand Down Expand Up @@ -44,6 +50,77 @@ impl Credential {
pub fn r#type(&self) -> &CredentialType {
&self.r#type
}

/// Check if the credential satisfies a presentation definition.
pub fn satisfies_presentation_definition(
&self,
definition: &PresentationDefinition,
) -> Result<bool, CredentialPresentationError> {
// If the credential does not match the definition requested format, then
// return None.
if !definition.format().contains_key(&self.format().into()) {
return Ok(false);
}

// If the definition provided a `type` constraint,
// for credential(s), and the credential matches the type,
// return the credential.
if definition
.credential_types_hint()
.contains(&self.r#type().into())
{
return Ok(true);
}

let json = match self.format {
CredentialFormat::SdJwtVcJson => {
SdJwtVc::new_from_credential(self).map_err(|e| {
CredentialPresentationError::Decoding(CredentialDecodingError::from(e))
})?.decode_reveal_json().map_err(|e| {
CredentialPresentationError::Decoding(CredentialDecodingError::from(e))
})?
}
_ => {
unimplemented!("Credential format not supported");
}
};

// Use the JSON Value type as the selector for the constraint field JSON path.
let mut selector = jsonpath_lib::selector(&json);

let failed_validation = definition
.input_descriptors()
.iter()
.flat_map(|descriptor| {
descriptor
.constraints()
.fields()
.iter()
.filter_map(|field| match field.validator() {
Some(Ok(validator)) => {
field.path().iter().map(|path| {
selector(path)
})
.map(|value| {

}).collect::<Result<Vec<bool>, CredentialPresentationError>>()


Some(false)


},
_ => None,
})
})
.any(|validation| !validation);

if failed_validation {
return Ok(false);
}

Ok(false)
}
}

impl TryFrom<Credential> for AnyJsonCredential<NonEmptyObject> {
Expand All @@ -66,7 +143,7 @@ impl TryFrom<Credential> for AnyJsonCredential<NonEmptyObject> {
/// A credential that has been parsed as a known variant.
#[derive(Debug, Clone, uniffi::Object)]
pub struct ParsedCredential {
inner: ParsedCredentialInner,
pub(crate) inner: ParsedCredentialInner,
}

/// A credential that has been parsed as a known variant.
Expand All @@ -75,6 +152,9 @@ enum ParsedCredentialInner {
MsoMdoc(Arc<Mdoc>),
JwtVcJson(Arc<JwtVc>),
JwtVcJsonLd(Arc<JwtVc>),
// NOTE: The inner value re-uses the `JwtVc` type.
// TODO: Confirm this is accurate to re-use this type.
SdJwtVcJson(Arc<JwtVc>),
LdpVc(Arc<JsonVc>),
// More to come, for example:
// SdJwt(...),
Expand Down Expand Up @@ -115,6 +195,20 @@ impl ParsedCredential {
})
}

#[uniffi::constructor]
/// Construct a new `sd_jwt_vc` credential.
pub fn new_sd_jwt_vc_json(
sd_jwt_vc: Arc<SdJwtVc>,
) -> Result<Arc<Self>, CredentialDecodingError> {
SdJwtVc::as_jwt_vc(sd_jwt_vc)
.map(|vc| {
Arc::new(Self {
inner: ParsedCredentialInner::SdJwtVcJson(vc),
})
})
.map_err(CredentialDecodingError::from)
}

#[uniffi::constructor]
/// Parse a credential from the generic form retrieved from storage.
pub fn parse_from_credential(
Expand All @@ -124,6 +218,7 @@ impl ParsedCredential {
CredentialFormat::MsoMdoc => Ok(Self::new_mso_mdoc(credential.try_into()?)),
CredentialFormat::JwtVcJson => Ok(Self::new_jwt_vc_json(credential.try_into()?)),
CredentialFormat::JwtVcJsonLd => Ok(Self::new_jwt_vc_json_ld(credential.try_into()?)),
CredentialFormat::SdJwtVcJson => Self::new_sd_jwt_vc_json(credential.try_into()?),
CredentialFormat::LdpVc => Ok(Self::new_ldp_vc(credential.try_into()?)),
_ => Err(CredentialDecodingError::UnsupportedCredentialFormat(
credential.format.to_string(),
Expand All @@ -142,6 +237,15 @@ impl ParsedCredential {
payload: vc.to_compact_jws_bytes(),
key_alias: vc.key_alias(),
}),
// TODO: Consider using the `SdJwtVc` type instead of
// re-using the `JwtVc` type.
ParsedCredentialInner::SdJwtVcJson(vc) => Ok(Credential {
id: vc.id(),
format: CredentialFormat::SdJwtVcJson,
r#type: vc.r#type(),
payload: vc.to_compact_jws_bytes(),
key_alias: vc.key_alias(),
}),
ParsedCredentialInner::JwtVcJsonLd(vc) => Ok(Credential {
id: vc.id(),
format: CredentialFormat::JwtVcJsonLd,
Expand Down Expand Up @@ -182,10 +286,20 @@ pub enum CredentialDecodingError {
JsonVc(#[from] JsonVcInitError),
#[error(transparent)]
JwtVc(#[from] JwtVcInitError),
#[error(transparent)]
SdJwtVc(#[from] SdJwtVcError),
#[error("this credential format is not yet supported: {0}")]
UnsupportedCredentialFormat(String),
}

#[derive(Debug, uniffi::Error, thiserror::Error)]
pub enum CredentialPresentationError {
#[error(transparent)]
Decoding(#[from] CredentialDecodingError),
#[error("JSON path selector error: {0}")]
JsonPath(String)
}

/// The format of the credential.
#[derive(uniffi::Enum, PartialEq, Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "snake_case")]
Expand All @@ -195,6 +309,7 @@ pub enum CredentialFormat {
#[serde(rename = "jwt_vc_json-ld")]
JwtVcJsonLd,
LdpVc,
SdJwtVcJson,
#[serde(untagged)]
Other(String), // For ease of expansion.
}
Expand All @@ -206,6 +321,7 @@ impl From<&CredentialFormat> for ClaimFormatDesignation {
CredentialFormat::JwtVcJson => ClaimFormatDesignation::JwtVcJson,
CredentialFormat::JwtVcJsonLd => ClaimFormatDesignation::Other("jwt_vc_json-ld".into()),
CredentialFormat::LdpVc => ClaimFormatDesignation::LdpVc,
CredentialFormat::SdJwtVcJson => ClaimFormatDesignation::Other("sd_jwt_vc_json".into()),
CredentialFormat::Other(s) => ClaimFormatDesignation::Other(s.to_owned()),
}
}
Expand All @@ -218,6 +334,7 @@ impl std::fmt::Display for CredentialFormat {
CredentialFormat::JwtVcJson => write!(f, "jwt_vc_json"),
CredentialFormat::JwtVcJsonLd => write!(f, "jwt_vc_json-ld"),
CredentialFormat::LdpVc => write!(f, "ldp_vc"),
CredentialFormat::SdJwtVc => write!(f, "sd_jwt_vc"),
CredentialFormat::Other(s) => write!(f, "{s}"),
}
}
Expand Down
Loading

0 comments on commit 8e0e649

Please sign in to comment.