Skip to content

Commit 5e2ba7e

Browse files
committed
feat: Add verifier functionality
1 parent 17bf79f commit 5e2ba7e

16 files changed

+1126
-0
lines changed

pom.xml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,38 @@
208208
<artifactId>jsonld-common-java</artifactId>
209209
<version>1.14.0</version>
210210
</dependency>
211+
<dependency>
212+
<groupId>decentralized-identity</groupId>
213+
<artifactId>uni-resolver-local</artifactId>
214+
<version>0.30.0</version>
215+
<optional>true</optional>
216+
<exclusions>
217+
<exclusion>
218+
<groupId>commons-logging</groupId>
219+
<artifactId>commons-logging</artifactId>
220+
</exclusion>
221+
</exclusions>
222+
</dependency>
211223
<dependency>
212224
<groupId>com.danubetech</groupId>
213225
<artifactId>data-integrity-java</artifactId>
214226
<version>1.17.0</version>
215227
</dependency>
228+
<dependency>
229+
<groupId>com.danubetech</groupId>
230+
<artifactId>danubetech-common-java</artifactId>
231+
<version>1.29.0</version>
232+
</dependency>
233+
<dependency>
234+
<groupId>com.danubetech</groupId>
235+
<artifactId>notarization-service-client</artifactId>
236+
<version>0.1.0</version>
237+
</dependency>
238+
<dependency>
239+
<groupId>com.danubetech</groupId>
240+
<artifactId>revocation-service-client</artifactId>
241+
<version>0.2.0</version>
242+
</dependency>
216243
<dependency>
217244
<groupId>com.nimbusds</groupId>
218245
<artifactId>nimbus-jose-jwt</artifactId>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.danubetech.verifiablecredentials.verifier;
2+
3+
import com.danubetech.verifiablecredentials.verifier.result.VerifyResult;
4+
5+
public class VerifyingException extends Exception {
6+
7+
private VerifyResult verifyResult;
8+
9+
public VerifyingException(String message) {
10+
super(message);
11+
}
12+
13+
public VerifyingException(String message, Throwable ex) {
14+
super(message, ex);
15+
}
16+
17+
public VerifyingException(VerifyResult verifyResult) {
18+
this(verifyResult.getErrorMessage() == null ? "(unknown error)" : verifyResult.getErrorMessage());
19+
this.verifyResult = verifyResult;
20+
}
21+
22+
/*
23+
* Getters and setters
24+
*/
25+
26+
public VerifyResult getVerifyResult() {
27+
return verifyResult;
28+
}
29+
30+
public void setVerifyResult(VerifyResult verifyResult) {
31+
this.verifyResult = verifyResult;
32+
}
33+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.danubetech.verifiablecredentials.verifier.checker;
2+
3+
import com.danubetech.verifiablecredentials.verifier.VerifyingException;
4+
import com.danubetech.verifiablecredentials.verifier.result.VerifyResult;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
8+
import java.util.LinkedHashMap;
9+
import java.util.Map;
10+
11+
public abstract class AbstractChecker<I, R> implements Checker<I, R> {
12+
13+
private static Logger log = LoggerFactory.getLogger(AbstractChecker.class);
14+
15+
private final String checkName;
16+
17+
protected AbstractChecker(String checkName) {
18+
this.checkName = checkName;
19+
}
20+
21+
protected abstract R checkInternal(I i, VerifyResult verifyResult) throws VerifyingException;
22+
23+
@Override
24+
public final R check(I i, VerifyResult verifyResult) {
25+
26+
try {
27+
28+
R r = this.checkInternal(i, verifyResult);
29+
if (log.isDebugEnabled()) log.debug("Result for " + this.getClass().getSimpleName() + ": " + (r == null ? null : r.getClass().getSimpleName()));
30+
if (r != null) this.getCheckMetadata(verifyResult);
31+
return r;
32+
} catch (VerifyingException ex) {
33+
34+
String message = ex.getMessage() != null ? ex.getMessage() : ex.getClass().getSimpleName();
35+
if (log.isWarnEnabled()) log.warn("Verifying exception for " + this.getClass().getSimpleName() + ": " + message, ex);
36+
this.getCheckMetadata(verifyResult);
37+
this.addProblem(verifyResult, message);
38+
return null;
39+
} catch (Exception ex) {
40+
41+
String message = ex.getMessage() != null ? ex.getMessage() : ex.getClass().getSimpleName();
42+
if (log.isWarnEnabled()) log.warn("Exception for " + this.getClass().getSimpleName() + ": " + message, ex);
43+
this.getCheckMetadata(verifyResult);
44+
this.addProblem(verifyResult, message);
45+
return null;
46+
}
47+
}
48+
49+
protected final Map<String, Object> getCheckMetadata(VerifyResult verifyResult) {
50+
return verifyResult.getCheckMetadata(this.getCheckName());
51+
}
52+
53+
protected final void addProblem(VerifyResult verifyResult, String message, Map<String, Object> problemMetadata) {
54+
if (problemMetadata == null) problemMetadata = new LinkedHashMap<>();
55+
problemMetadata.put("check", this.getCheckName());
56+
verifyResult.addProblem(message, problemMetadata);
57+
}
58+
59+
protected final void addProblem(VerifyResult verifyResult, String message) {
60+
this.addProblem(verifyResult, message, null);
61+
}
62+
63+
protected final void addWarning(VerifyResult verifyResult, String message, Map<String, Object> warningMetadata) {
64+
if (warningMetadata == null) warningMetadata = new LinkedHashMap<>();
65+
warningMetadata.put("check", this.getCheckName());
66+
verifyResult.addWarning(message, warningMetadata);
67+
}
68+
69+
protected final void addWarning(VerifyResult verifyResult, String message) {
70+
this.addWarning(verifyResult, message, null);
71+
}
72+
73+
@Override
74+
public String getCheckName() {
75+
return this.checkName;
76+
}
77+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.danubetech.verifiablecredentials.verifier.checker;
2+
3+
import com.danubetech.verifiablecredentials.verifier.result.VerifyResult;
4+
5+
public interface Checker<I, R> {
6+
7+
public R check(I i, VerifyResult verifyResult);
8+
public String getCheckName();
9+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.danubetech.verifiablecredentials.verifier.checker;
2+
3+
import com.danubetech.verifiablecredentials.VerifiableCredential;
4+
import com.danubetech.verifiablecredentials.verifier.VerifyingException;
5+
import com.danubetech.verifiablecredentials.verifier.result.VerifyResult;
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
9+
import java.util.Date;
10+
11+
public class ExpirationDateChecker extends AbstractChecker<VerifiableCredential, Void> implements Checker<VerifiableCredential, Void> {
12+
13+
private static Logger log = LoggerFactory.getLogger(ExpirationDateChecker.class);
14+
15+
public ExpirationDateChecker() {
16+
super("expiration-date");
17+
}
18+
19+
@Override
20+
public Void checkInternal(VerifiableCredential verifiableCredential, VerifyResult verifyResult) throws VerifyingException {
21+
22+
if (verifiableCredential.getExpirationDate() == null) return null;
23+
24+
// check issuance date
25+
26+
if (verifiableCredential.getExpirationDate().before(new Date())) throw new VerifyingException("Expiration date is in the past.");
27+
this.getCheckMetadata(verifyResult).put("verified", Boolean.TRUE);
28+
29+
// done
30+
31+
return null;
32+
}
33+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.danubetech.verifiablecredentials.verifier.checker;
2+
3+
import com.danubetech.verifiablecredentials.VerifiableCredential;
4+
import com.danubetech.verifiablecredentials.verifier.VerifyingException;
5+
import com.danubetech.verifiablecredentials.verifier.result.VerifyResult;
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
9+
import java.util.Date;
10+
11+
public class IssuanceDateChecker extends AbstractChecker<VerifiableCredential, Void> implements Checker<VerifiableCredential, Void> {
12+
13+
private static Logger log = LoggerFactory.getLogger(IssuanceDateChecker.class);
14+
15+
public IssuanceDateChecker() {
16+
super("issuance-date");
17+
}
18+
19+
@Override
20+
public Void checkInternal(VerifiableCredential verifiableCredential, VerifyResult verifyResult) throws VerifyingException {
21+
22+
if (verifiableCredential.getIssuanceDate() == null) return null;
23+
24+
// check issuance date
25+
26+
if (verifiableCredential.getIssuanceDate().after(new Date())) throw new VerifyingException("Issuance date is in the future.");
27+
this.getCheckMetadata(verifyResult).put("verified", Boolean.TRUE);
28+
29+
// done
30+
31+
return null;
32+
}
33+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.danubetech.verifiablecredentials.verifier.checker;
2+
3+
import com.danubetech.verifiablecredentials.verifier.VerifyingException;
4+
import com.danubetech.verifiablecredentials.verifier.result.VerifyResult;
5+
import foundation.identity.jsonld.JsonLDObject;
6+
import foundation.identity.jsonld.validation.Validation;
7+
8+
public class JsonLDChecker extends AbstractChecker<JsonLDObject, Void> implements Checker<JsonLDObject, Void> {
9+
10+
public JsonLDChecker() {
11+
12+
super("jsonld");
13+
}
14+
15+
@Override
16+
public Void checkInternal(JsonLDObject jsonLdObject, VerifyResult verifyResult) throws VerifyingException {
17+
18+
if (jsonLdObject == null) return null;
19+
20+
Validation.validate(jsonLdObject);
21+
22+
return null;
23+
}
24+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package com.danubetech.verifiablecredentials.verifier.checker;
2+
3+
import com.danubetech.dataintegrity.DataIntegrityProof;
4+
import com.danubetech.dataintegrity.canonicalizer.RDFC10SHA256Canonicalizer;
5+
import com.danubetech.notarizationservice.client.ApiException;
6+
import com.danubetech.notarizationservice.client.NotarizationServiceClient;
7+
import com.danubetech.notarizationservice.client.swagger.model.BitcoinVerifyResponse;
8+
import com.danubetech.verifiablecredentials.VerifiableCredential;
9+
import com.danubetech.verifiablecredentials.proof.BlockchainHashProof2020;
10+
import com.danubetech.verifiablecredentials.verifier.VerifyingException;
11+
import com.danubetech.verifiablecredentials.verifier.result.VerifyResult;
12+
import foundation.identity.jsonld.JsonLDException;
13+
import foundation.identity.jsonld.JsonLDObject;
14+
import org.slf4j.Logger;
15+
import org.slf4j.LoggerFactory;
16+
17+
import java.io.IOException;
18+
import java.util.Map;
19+
20+
public class NotarizationChecker extends AbstractChecker<VerifiableCredential, Void> implements Checker<VerifiableCredential, Void> {
21+
22+
private static Logger log = LoggerFactory.getLogger(NotarizationChecker.class);
23+
24+
private NotarizationServiceClient notarizationServiceClient;
25+
26+
public NotarizationChecker(NotarizationServiceClient notarizationServiceClient) {
27+
28+
super("notarization");
29+
30+
this.notarizationServiceClient = notarizationServiceClient;
31+
}
32+
33+
@Override
34+
public Void checkInternal(VerifiableCredential verifiableCredential, VerifyResult verifyResult) throws VerifyingException {
35+
36+
Map<String, Object> jsonObject = verifiableCredential.getJsonObject();
37+
38+
// obtain the normalizedDocument document
39+
40+
String normalizedDocument;
41+
42+
try {
43+
44+
JsonLDObject jsonLdDocumentWithoutProof = JsonLDObject.builder().base(verifiableCredential).build();
45+
jsonLdDocumentWithoutProof.setDocumentLoader(verifiableCredential.getDocumentLoader());
46+
DataIntegrityProof.removeFromJsonLdObject(jsonLdDocumentWithoutProof);
47+
normalizedDocument = RDFC10SHA256Canonicalizer.getInstance().canonicalize(jsonLdDocumentWithoutProof);
48+
} catch (JsonLDException | IOException ex) {
49+
50+
throw new VerifyingException("Normalization problem: " + ex.getMessage(), ex);
51+
}
52+
53+
// check the notarization
54+
55+
BlockchainHashProof2020 ldProof = BlockchainHashProof2020.getFromJsonLDObject(verifiableCredential);
56+
if (log.isDebugEnabled()) log.debug("Found proof: " + ldProof);
57+
if (ldProof == null) return null;
58+
59+
String proofValue = ldProof.getProofValue();
60+
String[] proofValueParts = proofValue.split(" ");
61+
String txId = proofValueParts[0];
62+
String signature = proofValueParts[1];
63+
64+
BitcoinVerifyResponse bitcoinVerifyResponse;
65+
66+
try {
67+
68+
bitcoinVerifyResponse = this.getNotarizationServiceClient().verifyUsingPOST("SHA-256", null, null, Boolean.FALSE, signature, txId, normalizedDocument);
69+
if (log.isDebugEnabled()) log.debug("Notarization service response: " + bitcoinVerifyResponse);
70+
} catch (ApiException ex) {
71+
72+
throw new VerifyingException("Cannot check notarization: " + ex.getCode() + " - " + ex.getResponseBody(), ex);
73+
}
74+
75+
if (Boolean.TRUE != bitcoinVerifyResponse.getVerified()) {
76+
77+
throw new VerifyingException("Credential notarization cannot be verified. txId: " + bitcoinVerifyResponse.getTxId() + ", notarizationBlockHeight: " + bitcoinVerifyResponse.getNotarizationBlockHeight() + ", currentBlockHeight: " + bitcoinVerifyResponse.getCurrentBlockHeight());
78+
}
79+
80+
// CHECK METADATA
81+
82+
this.getCheckMetadata(verifyResult).put("txId", bitcoinVerifyResponse.getTxId());
83+
this.getCheckMetadata(verifyResult).put("notarizationBlockHeight", bitcoinVerifyResponse.getNotarizationBlockHeight());
84+
this.getCheckMetadata(verifyResult).put("currentBlockHeight", bitcoinVerifyResponse.getCurrentBlockHeight());
85+
this.getCheckMetadata(verifyResult).put("verified", bitcoinVerifyResponse.getVerified());
86+
87+
// done
88+
89+
return null;
90+
}
91+
92+
/*
93+
* Getters and setters
94+
*/
95+
96+
public NotarizationServiceClient getNotarizationServiceClient() {
97+
return this.notarizationServiceClient;
98+
}
99+
100+
public void setNotarizationServiceClient(NotarizationServiceClient notarizationServiceClient) {
101+
this.notarizationServiceClient = notarizationServiceClient;
102+
}
103+
}

0 commit comments

Comments
 (0)