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(validation): credential signer must be equal issuer #185

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
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
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ repositories {
// Dash has a maven plugin, BUT is not resolvable through mavenCentral()
url = uri("https://repo.eclipse.org/content/repositories/dash-licenses/")
}
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}

ext {
Expand All @@ -79,7 +80,7 @@ dependencies {
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${openApiVersion}"
implementation group: 'com.smartsensesolutions', name: 'commons-dao', version: '0.0.5'
implementation 'org.liquibase:liquibase-core'
implementation 'org.eclipse.tractusx.ssi:cx-ssi-lib:0.0.17'
implementation 'org.eclipse.tractusx.ssi:cx-ssi-lib:0.0.18-SNAPSHOT'

//Added explicitly to mitigate CVE 2022-1471
implementation group: 'org.yaml', name: 'snakeyaml', version: '2.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ public Map<String, Object> credentialsValidation(Map<String, Object> data, boole

LinkedDataProofValidation proofValidation = LinkedDataProofValidation.newInstance(didResolver);

boolean valid = proofValidation.verifiy(verifiableCredential);
boolean valid = proofValidation.verify(verifiableCredential);

Map<String, Object> response = new TreeMap<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ private boolean validateAudience(String audience, SignedJWT signedJWT) {
private boolean validateCredential(VerifiableCredential credential) {
final DidResolver resolver = didDocumentResolverService.getCompositeDidResolver();
final LinkedDataProofValidation linkedDataProofValidation = LinkedDataProofValidation.newInstance(resolver);
final boolean isValid = linkedDataProofValidation.verifiy(credential);
final boolean isValid = linkedDataProofValidation.verify(credential);

if (isValid) {
log.debug("Credential validation result: (valid: {}, credential-id: {})", isValid, credential.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ void validateCredentialsWithInvalidVC() throws com.fasterxml.jackson.core.JsonPr
utils.when(() -> {
LinkedDataProofValidation.newInstance(Mockito.any(DidResolver.class));
}).thenReturn(mock);
Mockito.when(mock.verifiy(Mockito.any(VerifiableCredential.class))).thenReturn(false);
Mockito.when(mock.verify(Mockito.any(VerifiableCredential.class))).thenReturn(false);

Map<String, Object> stringObjectMap = credentialController.credentialsValidation(map, false).getBody();
Assertions.assertFalse(Boolean.parseBoolean(stringObjectMap.get(StringPool.VALID).toString()));
Expand All @@ -238,7 +238,7 @@ void validateCredentialsWithExpiryCheckTrue() throws com.fasterxml.jackson.core.
utils.when(() -> {
LinkedDataProofValidation.newInstance(Mockito.any(DidResolver.class));
}).thenReturn(mock);
Mockito.when(mock.verifiy(Mockito.any(VerifiableCredential.class))).thenReturn(true);
Mockito.when(mock.verify(Mockito.any(VerifiableCredential.class))).thenReturn(true);

Map<String, Object> stringObjectMap = credentialController.credentialsValidation(map, true).getBody();
Assertions.assertTrue(Boolean.parseBoolean(stringObjectMap.get(StringPool.VALID).toString()));
Expand All @@ -265,7 +265,7 @@ void validateCredentialsWithExpiryCheckFalse() throws com.fasterxml.jackson.core
utils.when(() -> {
LinkedDataProofValidation.newInstance(Mockito.any(DidResolver.class));
}).thenReturn(mock);
Mockito.when(mock.verifiy(Mockito.any(VerifiableCredential.class))).thenReturn(true);
Mockito.when(mock.verify(Mockito.any(VerifiableCredential.class))).thenReturn(true);

Map<String, Object> stringObjectMap = credentialController.credentialsValidation(map, false).getBody();
Assertions.assertTrue(Boolean.parseBoolean(stringObjectMap.get(StringPool.VALID).toString()));
Expand All @@ -291,7 +291,7 @@ void validateExpiredCredentialsWithExpiryCheckTrue() throws com.fasterxml.jackso
utils.when(() -> {
LinkedDataProofValidation.newInstance(Mockito.any(DidResolver.class));
}).thenReturn(mock);
Mockito.when(mock.verifiy(Mockito.any(VerifiableCredential.class))).thenReturn(true);
Mockito.when(mock.verify(Mockito.any(VerifiableCredential.class))).thenReturn(true);

Map<String, Object> stringObjectMap = credentialController.credentialsValidation(map, true).getBody();
Assertions.assertFalse(Boolean.parseBoolean(stringObjectMap.get(StringPool.VALID).toString()));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package org.eclipse.tractusx.managedidentitywallets.vc;

import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.SneakyThrows;
import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication;
import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings;
import org.eclipse.tractusx.managedidentitywallets.config.TestContextInitializer;
import org.eclipse.tractusx.managedidentitywallets.constant.StringPool;
import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet;
import org.eclipse.tractusx.managedidentitywallets.service.CommonService;
import org.eclipse.tractusx.managedidentitywallets.service.PresentationService;
import org.eclipse.tractusx.managedidentitywallets.service.WalletKeyService;
import org.eclipse.tractusx.managedidentitywallets.utils.TestUtils;
import org.eclipse.tractusx.ssi.lib.crypt.x21559.x21559PrivateKey;
import org.eclipse.tractusx.ssi.lib.model.proof.jws.JWSSignature2020;
import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential;
import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialBuilder;
import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject;
import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialType;
import org.eclipse.tractusx.ssi.lib.proof.LinkedDataProofGenerator;
import org.eclipse.tractusx.ssi.lib.proof.SignatureType;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.ContextConfiguration;

import java.net.URI;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = { ManagedIdentityWalletsApplication.class })
@ContextConfiguration(initializers = { TestContextInitializer.class })
public class VerifiableCredentialIssuerEqualProofSignerTest {

@Autowired
private MIWSettings miwSettings;

@Autowired
private TestRestTemplate restTemplate;

@Autowired
private WalletKeyService walletKeyService;

@Autowired
private CommonService commonService;

@Autowired
private PresentationService presentationService;

@SneakyThrows
@Test
public void test() {
var bpn1 = "BPNL000000000FOO";
var response1 = TestUtils.createWallet(bpn1, "did:web:localhost%3A8080:BPNL000000000FOO", restTemplate, miwSettings.authorityWalletBpn());
Assertions.assertTrue(response1.getStatusCode().is2xxSuccessful(), "Wallet 1 creation failed. " + response1.getBody());
var wallet1 = commonService.getWalletByIdentifier(bpn1);

var bpn2 = "BPNL000000000BAR";
var response2 = TestUtils.createWallet(bpn2, "did:web:localhost%3A8080:BPNL000000000BAR", restTemplate, miwSettings.authorityWalletBpn());
Assertions.assertTrue(response2.getStatusCode().is2xxSuccessful(), "Wallet 2 creation failed. " + response2.getBody());
var wallet2 = commonService.getWalletByIdentifier(bpn2);

// create vc where issuer and proof-signer are different
var verifiableCredentialWithSignerDifferentIssuer = issueVC(wallet1.getDid(), wallet2);
var verifiableCredentialWithSignerEqualIssuer = issueVC(wallet1.getDid(), wallet1);

var presentationWithSignerDifferentIssuer = presentationService.createPresentation(
Map.of(StringPool.VERIFIABLE_CREDENTIALS, List.of(verifiableCredentialWithSignerDifferentIssuer)),
true, "audience", miwSettings.authorityWalletBpn());
var presentationCredentialWithSignerEqualIssuer = presentationService.createPresentation(
Map.of(StringPool.VERIFIABLE_CREDENTIALS, List.of(verifiableCredentialWithSignerEqualIssuer)),
true, "audience", miwSettings.authorityWalletBpn());

var resultSignerEqualIssuer = presentationService.validatePresentation(presentationCredentialWithSignerEqualIssuer, true, false, "audience");
var resultSignerDifferentIssuer = presentationService.validatePresentation(presentationWithSignerDifferentIssuer, true, false, "audience");

Assertions.assertFalse((boolean) resultSignerDifferentIssuer.get(StringPool.VALID), "Presentation should not be valid. Issuer different than proof-signer. Verifiable Credential:\n" + verifiableCredentialWithSignerEqualIssuer.toPrettyJson());
Assertions.assertTrue((boolean) resultSignerEqualIssuer.get(StringPool.VALID), "Presentation should be valid. Issuer equal than proof-signer. Verifiable Credential:\n" + verifiableCredentialWithSignerEqualIssuer.toPrettyJson());
}

@SneakyThrows
private VerifiableCredential issueVC(String issuerDid, Wallet signerWallet) throws JsonProcessingException {
List<URI> contexts = new ArrayList();
contexts.add(URI.create("https://www.w3.org/2018/credentials/v1"));
// if the credential does not contain the JWS proof-context add it
URI jwsUri = URI.create("https://w3id.org/security/suites/jws-2020/v1");
if (!contexts.contains(jwsUri)) {
contexts.add(jwsUri);
}

URI id = URI.create(UUID.randomUUID().toString());
VerifiableCredentialBuilder builder =
new VerifiableCredentialBuilder()
.context(contexts)
.id(URI.create(issuerDid + "#" + id))
.type(List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL))
.issuer(URI.create(issuerDid))
.issuanceDate(Instant.now())
.expirationDate(Instant.now().plusSeconds(60 * 60 * 24 * 365))
.credentialSubject(new VerifiableCredentialSubject(Map.of("id", "foo")));

LinkedDataProofGenerator generator = LinkedDataProofGenerator.newInstance(SignatureType.JWS);
URI verificationMethod = signerWallet.getDidDocument().getVerificationMethods().get(0).getId();

byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdentifierAsBytes(signerWallet.getId());

JWSSignature2020 proof =
(JWSSignature2020) generator.createProof(builder.build(), verificationMethod, new x21559PrivateKey(privateKeyBytes));

//Adding Proof to VC
builder.proof(proof);

//Create Credential
return builder.build();
}
}
Loading