Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issuer claim check in VC #1235

Merged
merged 4 commits into from
Sep 20, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 261 additions & 0 deletions identity_credential/src/credential/jwt_serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ where
credential_subject: InnerCredentialSubject::new(subject),
issuance_date: None,
expiration_date: None,
issuer: None,
credential_schema: Cow::Borrowed(credential_schema),
credential_status: credential_status.as_ref().map(Cow::Borrowed),
refresh_service: Cow::Borrowed(refresh_service),
Expand All @@ -118,6 +119,18 @@ where
/// Checks whether the fields that are set in the `vc` object are consistent with the corresponding values
/// set for the registered claims.
fn check_consistency(&self) -> Result<()> {
// Check consistency of issuer.
let issuer_from_claims: &Issuer = self.iss.as_ref();
if !self
.vc
.issuer
.as_ref()
.map(|value| value == issuer_from_claims)
.unwrap_or(true)
{
return Err(Error::InconsistentCredentialJwtClaims("inconsistent issuer"));
};

// Check consistency of issuanceDate
let issuance_date_from_claims = self.issuance_date.to_issuance_date()?;
if !self
Expand Down Expand Up @@ -197,6 +210,7 @@ where
properties,
proof,
issuance_date: _,
issuer: _,
expiration_date: _,
} = vc;

Expand Down Expand Up @@ -302,6 +316,9 @@ where
/// One or more URIs defining the type of the `Credential`.
#[serde(rename = "type")]
types: Cow<'credential, OneOrMany<String>>,
/// The issuer of the `Credential`.
#[serde(skip_serializing_if = "Option::is_none")]
issuer: Option<Issuer>,
/// One or more `Object`s representing the `Credential` subject(s).
#[serde(rename = "credentialSubject")]
credential_subject: InnerCredentialSubject<'credential>,
Expand Down Expand Up @@ -345,6 +362,7 @@ mod tests {
use identity_core::convert::ToJson;

use crate::credential::Credential;
use crate::Error;

use super::CredentialJwtClaims;

Expand Down Expand Up @@ -409,4 +427,247 @@ mod tests {

assert_eq!(credential, retrieved_credential);
}

#[test]
fn claims_duplication() {
let credential_json: &str = r#"
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "http://example.edu/credentials/3732",
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "https://example.edu/issuers/14",
"issuanceDate": "2010-01-01T19:23:24Z",
"expirationDate": "2025-09-13T15:56:23Z",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science in Mechanical Engineering"
}
}
}"#;

// `sub`, `exp`, `jti`, `iss`, `nbf` are duplicated in `vc`.
let claims_json: &str = r#"
{
"sub": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"jti": "http://example.edu/credentials/3732",
"iss": "https://example.edu/issuers/14",
"nbf": 1262373804,
"exp": 1757778983,
"vc": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "http://example.edu/credentials/3732",
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "https://example.edu/issuers/14",
"issuanceDate": "2010-01-01T19:23:24Z",
"expirationDate": "2025-09-13T15:56:23Z",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science in Mechanical Engineering"
}
}
}
}"#;

let credential: Credential = Credential::from_json(credential_json).unwrap();
let credential_from_claims: Credential = CredentialJwtClaims::<'_, Object>::from_json(&claims_json)
.unwrap()
.try_into_credential()
.unwrap();

assert_eq!(credential, credential_from_claims);
}

#[test]
fn inconsistent_issuer() {
// issuer is inconsistent (15 instead of 14).
let claims_json: &str = r#"
{
"sub": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"jti": "http://example.edu/credentials/3732",
"iss": "https://example.edu/issuers/14",
"nbf": 1262373804,
"vc": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "https://example.edu/issuers/15",
"credentialSubject": {
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science in Mechanical Engineering"
}
}
}
}"#;

let credential_from_claims_result: Result<Credential, _> =
CredentialJwtClaims::<'_, Object>::from_json(&claims_json)
.unwrap()
.try_into_credential();
assert!(matches!(
credential_from_claims_result.unwrap_err(),
Error::InconsistentCredentialJwtClaims("inconsistent issuer")
));
}

#[test]
fn inconsistent_id() {
let claims_json: &str = r#"
{
"sub": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"jti": "http://example.edu/credentials/3732",
"iss": "https://example.edu/issuers/14",
"nbf": 1262373804,
"vc": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"id": "http://example.edu/credentials/1111",
"credentialSubject": {
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science in Mechanical Engineering"
}
}
}
}"#;

let credential_from_claims_result: Result<Credential, _> =
CredentialJwtClaims::<'_, Object>::from_json(&claims_json)
.unwrap()
.try_into_credential();
assert!(matches!(
credential_from_claims_result.unwrap_err(),
Error::InconsistentCredentialJwtClaims("inconsistent credential id")
));
}

#[test]
fn inconsistent_subject() {
let claims_json: &str = r#"
{
"sub": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"jti": "http://example.edu/credentials/3732",
"iss": "https://example.edu/issuers/14",
"nbf": 1262373804,
"vc": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "http://example.edu/credentials/3732",
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "https://example.edu/issuers/14",
"issuanceDate": "2010-01-01T19:23:24Z",
"credentialSubject": {
"id": "did:example:1111111111111111111111111",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science in Mechanical Engineering"
}
}
}
}"#;

let credential_from_claims_result: Result<Credential, _> =
CredentialJwtClaims::<'_, Object>::from_json(&claims_json)
.unwrap()
.try_into_credential();
assert!(matches!(
credential_from_claims_result.unwrap_err(),
Error::InconsistentCredentialJwtClaims("inconsistent credentialSubject: identifiers do not match")
));
}

#[test]
fn inconsistent_issuance_date() {
// issuer is inconsistent (15 instead of 14).
let claims_json: &str = r#"
{
"sub": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"jti": "http://example.edu/credentials/3732",
"iss": "https://example.edu/issuers/14",
"nbf": 1262373804,
"vc": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "http://example.edu/credentials/3732",
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "https://example.edu/issuers/14",
"issuanceDate": "2020-01-01T19:23:24Z",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science in Mechanical Engineering"
}
}
}
}"#;

let credential_from_claims_result: Result<Credential, _> =
CredentialJwtClaims::<'_, Object>::from_json(&claims_json)
.unwrap()
.try_into_credential();
assert!(matches!(
credential_from_claims_result.unwrap_err(),
Error::InconsistentCredentialJwtClaims("inconsistent issuanceDate")
));
}

#[test]
fn inconsistent_expiration_date() {
// issuer is inconsistent (15 instead of 14).
let claims_json: &str = r#"
{
"sub": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"jti": "http://example.edu/credentials/3732",
"iss": "https://example.edu/issuers/14",
"nbf": 1262373804,
"exp": 1757778983,
"vc": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "http://example.edu/credentials/3732",
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "https://example.edu/issuers/14",
"issuanceDate": "2010-01-01T19:23:24Z",
"expirationDate": "2026-09-13T15:56:23Z",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science in Mechanical Engineering"
}
}
}
}"#;

let credential_from_claims_result: Result<Credential, _> =
CredentialJwtClaims::<'_, Object>::from_json(&claims_json)
.unwrap()
.try_into_credential();
assert!(matches!(
credential_from_claims_result.unwrap_err(),
Error::InconsistentCredentialJwtClaims("inconsistent credential expirationDate")
));
}
}
Loading