Skip to content

Commit

Permalink
Complete jose rework
Browse files Browse the repository at this point in the history
  • Loading branch information
KendallWeihe committed Sep 9, 2024
1 parent 550f088 commit 2219045
Show file tree
Hide file tree
Showing 10 changed files with 320 additions and 175 deletions.
24 changes: 13 additions & 11 deletions crates/web5/src/credentials/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,36 @@ use super::{
jwt_payload_vc::JwtPayloadVerifiableCredential,
verifiable_credential_1_1::VerifiableCredential, VerificationError,
};
use crate::{errors::Result, jose::Jwt};
use std::time::SystemTime;
use crate::{errors::Result, jose::Jwt, json::FromJsonValue};

pub fn decode(vc_jwt: &str, verify_signature: bool) -> Result<VerifiableCredential> {
let jwt = Jwt::from_compact_jws(vc_jwt, verify_signature)?;

let jti = jwt
.claims
.get::<String>("jti")?
.jti
.ok_or(VerificationError::MissingClaim("jti".to_string()))?;
let iss = jwt
.claims
.get::<String>("iss")?
.iss
.ok_or(VerificationError::MissingClaim("issuer".to_string()))?;
let sub = jwt
.claims
.get::<String>("sub")?
.sub
.ok_or(VerificationError::MissingClaim("subject".to_string()))?;
let nbf = jwt
.claims
.get::<SystemTime>("nbf")?
.nbf
.ok_or(VerificationError::MissingClaim("not_before".to_string()))?;
let exp = jwt.claims.get::<SystemTime>("exp")?;
let exp = jwt.claims.exp;

let vc_payload = jwt
.claims
.get::<JwtPayloadVerifiableCredential>("vc")?
.ok_or(VerificationError::MissingClaim("vc".to_string()))?;
let vc_payload = JwtPayloadVerifiableCredential::from_json_value(
jwt.claims
.additional_properties
.ok_or(VerificationError::MissingClaim("vc".to_string()))?
.get("vc")
.ok_or(VerificationError::MissingClaim("vc".to_string()))?,
)?;

if let Some(id) = vc_payload.id {
if id != jti {
Expand Down
27 changes: 17 additions & 10 deletions crates/web5/src/credentials/jwt_payload_vc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use super::{
credential_schema::CredentialSchema, credential_subject::CredentialSubject, issuer::Issuer,
};
use crate::credentials::verifiable_credential_1_1::CredentialStatus;
use crate::errors::Result;
use crate::json::{FromJsonValue, JsonValue, ToJsonValue};
use crate::errors::{Result, Web5Error};
use crate::json::{json_value_type_name, FromJsonValue, JsonValue, ToJsonValue};
use crate::{
datetime::{deserialize_optional_rfc3339, serialize_optional_rfc3339},
json::JsonObject,
rfc3339::{deserialize_optional_system_time, serialize_optional_system_time},
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
Expand All @@ -24,14 +24,18 @@ pub struct JwtPayloadVerifiableCredential {
pub issuer: Option<Issuer>,
#[serde(
rename = "issuanceDate",
serialize_with = "serialize_optional_system_time",
deserialize_with = "deserialize_optional_system_time"
serialize_with = "serialize_optional_rfc3339",
deserialize_with = "deserialize_optional_rfc3339",
skip_serializing_if = "Option::is_none",
default
)]
pub issuance_date: Option<SystemTime>,
#[serde(
rename = "expirationDate",
serialize_with = "serialize_optional_system_time",
deserialize_with = "deserialize_optional_system_time"
serialize_with = "serialize_optional_rfc3339",
deserialize_with = "deserialize_optional_rfc3339",
skip_serializing_if = "Option::is_none",
default
)]
pub expiration_date: Option<SystemTime>,
#[serde(rename = "credentialStatus", skip_serializing_if = "Option::is_none")]
Expand All @@ -45,13 +49,16 @@ pub struct JwtPayloadVerifiableCredential {
}

impl FromJsonValue for JwtPayloadVerifiableCredential {
fn from_json_value(value: &JsonValue) -> Result<Option<Self>> {
fn from_json_value(value: &JsonValue) -> Result<Self> {
if let JsonValue::Object(ref obj) = *value {
let json_value = serde_json::to_value(obj)?;
let value = serde_json::from_value::<Self>(json_value)?;
Ok(Some(value))
Ok(value)
} else {
Ok(None)
Err(Web5Error::Json(format!(
"expected object, but found {}",
json_value_type_name(value)
)))
}
}
}
Expand Down
28 changes: 15 additions & 13 deletions crates/web5/src/credentials/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ use super::{
use crate::{
dids::bearer_did::BearerDid,
errors::{Result, Web5Error},
jose::Jwt,
json::JsonObject,
jose::{Jwt, JwtClaims},
json::{JsonValue, ToJsonValue},
};
use std::time::SystemTime;
use std::{collections::HashMap, time::SystemTime};

pub fn sign_with_did(
vc: &VerifiableCredential,
Expand All @@ -34,16 +34,18 @@ pub fn sign_with_did(
evidence: vc.evidence.clone(),
};

let mut claims = JsonObject::new();
claims.insert("vc", &vc_claim)?;
claims.insert("iss", &vc.issuer.to_string())?;
claims.insert("jti", &vc.id)?;
claims.insert("sub", &vc.credential_subject.id)?;
claims.insert("nbf", &vc.issuance_date)?;
claims.insert("iat", &SystemTime::now())?;
if let Some(exp) = &vc.expiration_date {
claims.insert("exp", exp)?;
}
let mut additional_properties: HashMap<String, JsonValue> = HashMap::new();
additional_properties.insert("vc".to_string(), vc_claim.to_json_value()?);

let claims = JwtClaims {
iss: Some(vc.issuer.to_string()),
jti: Some(vc.id.clone()),
sub: Some(vc.credential_subject.id.clone()),
nbf: Some(vc.issuance_date),
iat: Some(SystemTime::now()),
exp: vc.expiration_date,
additional_properties: Some(additional_properties),
};

let jwt = Jwt::from_claims(&claims, bearer_did, verification_method_id)?;
Ok(jwt.compact_jws)
Expand Down
28 changes: 15 additions & 13 deletions crates/web5/src/credentials/verifiable_credential_1_1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ use super::decode::decode;
use super::CredentialSubject;
use super::Issuer;

use crate::datetime::{
deserialize_optional_rfc3339, deserialize_rfc3339, serialize_optional_rfc3339,
serialize_rfc3339,
};
use crate::dids::bearer_did::BearerDid;
use crate::errors::Result;
use crate::json::JsonObject;
use crate::json::{FromJson, ToJson};
use crate::rfc3339::{
deserialize_optional_system_time, deserialize_system_time, serialize_optional_system_time,
serialize_system_time,
};

use serde::{Deserialize, Serialize};
use std::time::SystemTime;
Expand All @@ -32,14 +32,16 @@ pub struct VerifiableCredential {
pub credential_subject: CredentialSubject,
#[serde(
rename = "issuanceDate",
serialize_with = "serialize_system_time",
deserialize_with = "deserialize_system_time"
serialize_with = "serialize_rfc3339",
deserialize_with = "deserialize_rfc3339"
)]
pub issuance_date: SystemTime,
#[serde(
rename = "expirationDate",
serialize_with = "serialize_optional_system_time",
deserialize_with = "deserialize_optional_system_time"
serialize_with = "serialize_optional_rfc3339",
deserialize_with = "deserialize_optional_rfc3339",
skip_serializing_if = "Option::is_none",
default
)]
pub expiration_date: Option<SystemTime>,
#[serde(rename = "credentialStatus", skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -218,10 +220,10 @@ mod tests {
let result = VerifiableCredential::from_vc_jwt(vc_jwt_with_invalid_did_uri, true);

match result {
Err(Web5Error::Parameter(err_msg)) => {
assert_eq!(err_msg, "identifier regex match failure invalid did uri")
Err(Web5Error::Resolution(err)) => {
assert_eq!(err, ResolutionMetadataError::InvalidDid)
}
_ => panic!("Expected Web5Error::Parameter, but got: {:?}", result),
_ => panic!("Expected Web5Error::Resolution, but got: {:?}", result),
};
}

Expand Down Expand Up @@ -262,13 +264,13 @@ mod tests {
fn test_fails_cryptographic_verification() {
TEST_SUITE.include(test_name!());

let vc_jwt_with_invalid_signature = r#"eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDpqd2s6ZXlKaGJHY2lPaUpGWkRJMU5URTVJaXdpYTNSNUlqb2lUMHRRSWl3aVkzSjJJam9pUldReU5UVXhPU0lzSW5naU9pSkhWelpGVERsSVRUbHRkSGx5Y0dsWWRGRlVNR3B4Wms1MmFXTm5RVGxCVkRnME1IWTFZMDh5YjFSckluMCMwIn0.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJpZCI6InVybjp1dWlkOjZmYTQ2MDVjLWFlZGItNGQ2NC05NzdiLTFmY2NmYTU1ZTM1ZiIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmp3azpleUpoYkdjaU9pSkZaREkxTlRFNUlpd2lhM1I1SWpvaVQwdFFJaXdpWTNKMklqb2lSV1F5TlRVeE9TSXNJbmdpT2lKSFZ6WkZURGxJVFRsdGRIbHljR2xZZEZGVU1HcHhaazUyYVdOblFUbEJWRGcwTUhZMVkwOHliMVJySW4wIzAiLCJpc3N1YW5jZURhdGUiOiIyMDI0LTA4LTI4VDEyOjQyOjI3Ljc3Mjg4OSswMDowMCIsImV4cGlyYXRpb25EYXRlIjpudWxsLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDpkaHQ6cWdtbXB5anc1aHducWZnem43d21ybTMzYWR5OGdiOHo5aWRlaWI2bTlnajR5czZ3bnk4eSJ9fSwiaXNzIjoiZGlkOmp3azpleUpoYkdjaU9pSkZaREkxTlRFNUlpd2lhM1I1SWpvaVQwdFFJaXdpWTNKMklqb2lSV1F5TlRVeE9TSXNJbmdpT2lKSFZ6WkZURGxJVFRsdGRIbHljR2xZZEZGVU1HcHhaazUyYVdOblFUbEJWRGcwTUhZMVkwOHliMVJySW4wIzAiLCJqdGkiOiJ1cm46dXVpZDo2ZmE0NjA1Yy1hZWRiLTRkNjQtOTc3Yi0xZmNjZmE1NWUzNWYiLCJzdWIiOiJkaWQ6ZGh0OnFnbW1weWp3NWh3bnFmZ3puN3dtcm0zM2FkeThnYjh6OWlkZWliNm05Z2o0eXM2d255OHkiLCJuYmYiOjE3MjQ4NDg5NDcsImlhdCI6MTcyNDg0ODk0N30.-JwIGYZ9HlJASYxdRBWY5KlwP0iJUxWUOU6BsOR74VeC-zKgZb9WWZR08OVD-wv0X8KD5--0K5Dr9r5fL3B0Aw-invalid-signature"#;
let vc_jwt_with_invalid_signature = r#"eyJ0eXAiOiJKV1QiLCJhbGciOiJFZDI1NTE5Iiwia2lkIjoiZGlkOmp3azpleUpoYkdjaU9pSkZaREkxTlRFNUlpd2lhM1I1SWpvaVQwdFFJaXdpWTNKMklqb2lSV1F5TlRVeE9TSXNJbmdpT2lKMmJGOUNOVTB6UzFwclNXdDNTMDg1VGpKRlVFTTFjVE5IVGxoamQwWktObFl0VlU5RkxTMUlVa3MwSW4wIzAifQ.eyJpc3MiOiJkaWQ6andrOmV5SmhiR2NpT2lKRlpESTFOVEU1SWl3aWEzUjVJam9pVDB0UUlpd2lZM0oySWpvaVJXUXlOVFV4T1NJc0luZ2lPaUoyYkY5Q05VMHpTMXByU1d0M1MwODVUakpGVUVNMWNUTkhUbGhqZDBaS05sWXRWVTlGTFMxSVVrczBJbjAiLCJqdGkiOiJ1cm46dXVpZDoyMWUxNWRjYi0xM2MzLTQwYTYtYWJiNS01NTA3Nzg5Zjk4YmEiLCJzdWIiOiJkaWQ6ZGh0OnFnbW1weWp3NWh3bnFmZ3puN3dtcm0zM2FkeThnYjh6OWlkZWliNm05Z2o0eXM2d255OHkiLCJuYmYiOjE3MjU4OTU2NTYsImlhdCI6MTcyNTg5NTY1NiwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwiY3JlZGVudGlhbFN1YmplY3QiOnsiaWQiOiJkaWQ6ZGh0OnFnbW1weWp3NWh3bnFmZ3puN3dtcm0zM2FkeThnYjh6OWlkZWliNm05Z2o0eXM2d255OHkifSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJpc3N1ZXIiOiJkaWQ6andrOmV5SmhiR2NpT2lKRlpESTFOVEU1SWl3aWEzUjVJam9pVDB0UUlpd2lZM0oySWpvaVJXUXlOVFV4T1NJc0luZ2lPaUoyYkY5Q05VMHpTMXByU1d0M1MwODVUakpGVUVNMWNUTkhUbGhqZDBaS05sWXRWVTlGTFMxSVVrczBJbjAiLCJpZCI6InVybjp1dWlkOjIxZTE1ZGNiLTEzYzMtNDBhNi1hYmI1LTU1MDc3ODlmOThiYSIsImlzc3VhbmNlRGF0ZSI6IjIwMjQtMDktMDlUMTU6Mjc6MzZaIn19.6AR3aNzlMDgRpniSMhOGfXN3wUS0IkIoWa_KpZprOWwVbSyVjcI_Ndo3SGCutUSiBboYH9sFomdGb7_0AeVDCg"#;

let result = VerifiableCredential::from_vc_jwt(vc_jwt_with_invalid_signature, true);

match result {
Err(Web5Error::Crypto(err_msg)) => {
assert!(err_msg.contains("vc-jwt failed cryptographic verification"))
assert_eq!("cryptographic verification failure", err_msg)
}
_ => panic!("Expected Web5Error::Crypto, but got: {:?}", result),
};
Expand Down
85 changes: 50 additions & 35 deletions crates/web5/src/credentials/verifiable_presentation_1_1.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use crate::credentials::verifiable_credential_1_1::VerifiableCredential;
use crate::credentials::VerificationError;
use crate::datetime::{
deserialize_optional_rfc3339, deserialize_rfc3339, serialize_optional_rfc3339,
serialize_rfc3339,
};
use crate::dids::bearer_did::BearerDid;
use crate::dids::did::Did;
use crate::errors::{Result, Web5Error};
use crate::jose::Jwt;
use crate::json::{FromJsonValue, JsonObject, JsonValue, ToJsonValue};
use crate::rfc3339::{
deserialize_optional_system_time, deserialize_system_time, serialize_optional_system_time,
serialize_system_time,
};
use crate::jose::{Jwt, JwtClaims};
use crate::json::{json_value_type_name, FromJsonValue, JsonValue, ToJsonValue};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::SystemTime;
Expand All @@ -27,14 +27,16 @@ pub struct VerifiablePresentation {
pub holder: String,
#[serde(
rename = "issuanceDate",
serialize_with = "serialize_system_time",
deserialize_with = "deserialize_system_time"
serialize_with = "serialize_rfc3339",
deserialize_with = "deserialize_rfc3339"
)]
pub issuance_date: SystemTime,
#[serde(
rename = "expirationDate",
serialize_with = "serialize_optional_system_time",
deserialize_with = "deserialize_optional_system_time"
serialize_with = "serialize_optional_rfc3339",
deserialize_with = "deserialize_optional_rfc3339",
skip_serializing_if = "Option::is_none",
default
)]
pub expiration_date: Option<SystemTime>,
#[serde(rename = "verifiableCredential")]
Expand Down Expand Up @@ -62,28 +64,35 @@ pub struct JwtPayloadVerifiablePresentation {
pub holder: Option<String>,
#[serde(
rename = "issuanceDate",
serialize_with = "serialize_optional_system_time",
deserialize_with = "deserialize_optional_system_time"
serialize_with = "serialize_optional_rfc3339",
deserialize_with = "deserialize_optional_rfc3339",
skip_serializing_if = "Option::is_none",
default
)]
pub issuance_date: Option<SystemTime>,
#[serde(
rename = "expirationDate",
serialize_with = "serialize_optional_system_time",
deserialize_with = "deserialize_optional_system_time"
serialize_with = "serialize_optional_rfc3339",
deserialize_with = "deserialize_optional_rfc3339",
skip_serializing_if = "Option::is_none",
default
)]
pub expiration_date: Option<SystemTime>,
#[serde(rename = "verifiableCredential", skip_serializing_if = "Vec::is_empty")]
pub verifiable_credential: Vec<String>,
}

impl FromJsonValue for JwtPayloadVerifiablePresentation {
fn from_json_value(value: &JsonValue) -> Result<Option<Self>> {
fn from_json_value(value: &JsonValue) -> Result<Self> {
if let JsonValue::Object(ref obj) = *value {
let json_value = serde_json::to_value(obj)?;
let value = serde_json::from_value::<Self>(json_value)?;
Ok(Some(value))
Ok(value)
} else {
Ok(None)
Err(Web5Error::Json(format!(
"expected object, but found {}",
json_value_type_name(value)
)))
}
}
}
Expand Down Expand Up @@ -175,15 +184,18 @@ pub fn sign_presentation_with_did(
expiration_date: vp.expiration_date,
};

let mut claims = JsonObject::new();
claims.insert("vp", &vp_claims)?;
claims.insert("iss", &vp.holder)?;
claims.insert("jti", &vp.id)?;
claims.insert("nbf", &vp.issuance_date)?;
claims.insert("iat", &SystemTime::now())?;
if let Some(exp) = &vp.expiration_date {
claims.insert("exp", exp)?;
}
let mut additional_properties: HashMap<String, JsonValue> = HashMap::new();
additional_properties.insert("vp".to_string(), vp_claims.to_json_value()?);

let claims = JwtClaims {
iss: Some(vp.holder.clone()),
jti: Some(vp.id.clone()),
sub: None,
nbf: Some(vp.issuance_date),
iat: Some(SystemTime::now()),
exp: vp.expiration_date,
additional_properties: Some(additional_properties),
};

let jwt = Jwt::from_claims(&claims, bearer_did, verification_method_id)?;
Ok(jwt.compact_jws)
Expand All @@ -210,22 +222,25 @@ pub fn decode_vp_jwt(vp_jwt: &str, verify_signature: bool) -> Result<VerifiableP

let jti = jwt
.claims
.get::<String>("jti")?
.jti
.ok_or(VerificationError::MissingClaim("jti".to_string()))?;
let iss = jwt
.claims
.get::<String>("iss")?
.iss
.ok_or(VerificationError::MissingClaim("issuer".to_string()))?;
let nbf = jwt
.claims
.get::<SystemTime>("nbf")?
.nbf
.ok_or(VerificationError::MissingClaim("not_before".to_string()))?;
let exp = jwt.claims.get::<SystemTime>("exp")?;

let vp_payload = jwt
.claims
.get::<JwtPayloadVerifiablePresentation>("vp")?
.ok_or(VerificationError::MissingClaim("vp".to_string()))?;
let exp = jwt.claims.exp;

let vp_payload = JwtPayloadVerifiablePresentation::from_json_value(
jwt.claims
.additional_properties
.ok_or(VerificationError::MissingClaim("vp".to_string()))?
.get("vp")
.ok_or(VerificationError::MissingClaim("vp".to_string()))?,
)?;

if let Some(id) = vp_payload.id {
if id != jti {
Expand Down
Loading

0 comments on commit 2219045

Please sign in to comment.