Skip to content

Commit ac157ff

Browse files
committed
Better support for Verifiable Presentations.
Signed-off-by: Markus Sabadello <[email protected]>
1 parent cb0adca commit ac157ff

File tree

8 files changed

+521
-12
lines changed

8 files changed

+521
-12
lines changed

src/main/java/com/danubetech/verifiablecredentials/VerifiablePresentation.java

Lines changed: 104 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package com.danubetech.verifiablecredentials;
22

33
import java.io.IOException;
4+
import java.net.URI;
5+
import java.net.URISyntaxException;
46
import java.util.ArrayList;
7+
import java.util.Collections;
58
import java.util.LinkedHashMap;
69
import java.util.List;
710

@@ -11,6 +14,8 @@
1114
import com.github.jsonldjava.core.JsonLdConsts;
1215
import com.github.jsonldjava.utils.JsonUtils;
1316

17+
import info.weboftrust.ldsignatures.LdSignature;
18+
1419
public class VerifiablePresentation {
1520

1621
public static final String JSONLD_CONTEXT_CREDENTIALS = "https://www.w3.org/2018/credentials/v1";
@@ -33,17 +38,17 @@ private VerifiablePresentation(LinkedHashMap<String, Object> jsonLdObject, boole
3338

3439
public VerifiablePresentation() {
3540

36-
ArrayList<Object> context = new ArrayList<Object> ();
37-
context.add(JSONLD_CONTEXT_CREDENTIALS);
41+
ArrayList<Object> contextList = new ArrayList<Object> ();
42+
contextList.add(JSONLD_CONTEXT_CREDENTIALS);
3843

39-
ArrayList<String> type = new ArrayList<String> ();
40-
type.add(JSONLD_TYPE_VERIFIABLE_PRESENTATION);
44+
ArrayList<String> typeList = new ArrayList<String> ();
45+
typeList.add(JSONLD_TYPE_VERIFIABLE_PRESENTATION);
4146

4247
ArrayList<String> verifiableCredential = new ArrayList<String> ();
4348

4449
this.jsonLdObject = new LinkedHashMap<String, Object> ();
45-
this.jsonLdObject.put(JsonLdConsts.CONTEXT, context);
46-
this.jsonLdObject.put(JSONLD_TERM_TYPE, type);
50+
this.jsonLdObject.put(JsonLdConsts.CONTEXT, contextList);
51+
this.jsonLdObject.put(JSONLD_TERM_TYPE, typeList);
4752
this.jsonLdObject.put(JSONLD_TERM_VERIFIABLE_CREDENTIAL, verifiableCredential);
4853
}
4954

@@ -122,6 +127,11 @@ public static VerifiablePresentation fromJwtVerifiableCredential(JwtVerifiableCr
122127
return fromJwtVerifiableCredential(jwtVerifiableCredential, true);
123128
}
124129

130+
public LinkedHashMap<String, Object> getJsonLdObject() {
131+
132+
return this.jsonLdObject;
133+
}
134+
125135
@SuppressWarnings("unchecked")
126136
public VerifiableCredential getVerifiableCredential() {
127137

@@ -145,9 +155,43 @@ public VerifiableCredential getVerifiableCredential() {
145155
return jsonLdObject == null ? null : VerifiableCredential.fromJsonLdObject(jsonLdObject);
146156
}
147157

148-
public LinkedHashMap<String, Object> getJsonLdObject() {
158+
public LdSignature getLdSignature() {
149159

150-
return this.jsonLdObject;
160+
return LdSignature.getFromJsonLdObject(this.getJsonLdObject());
161+
}
162+
163+
@SuppressWarnings("unchecked")
164+
public List<String> getContext() {
165+
166+
return (List<String>) this.jsonLdObject.get(JsonLdConsts.CONTEXT);
167+
}
168+
169+
public void setContext(List<String> context) {
170+
171+
if (context == null)
172+
this.jsonLdObject.remove(JsonLdConsts.CONTEXT);
173+
else
174+
this.jsonLdObject.put(JsonLdConsts.CONTEXT, context);
175+
}
176+
177+
@SuppressWarnings("unchecked")
178+
public List<String> getType() {
179+
180+
Object object = this.jsonLdObject.get(JSONLD_TERM_TYPE);
181+
if (object == null) return null;
182+
183+
if (object instanceof List) return (List<String>) object;
184+
if (object instanceof String) return Collections.singletonList((String) object);
185+
186+
throw new IllegalStateException("Invalid object for '" + JSONLD_TERM_TYPE + "': " + object);
187+
}
188+
189+
public void setType(List<String> type) {
190+
191+
if (type == null)
192+
this.jsonLdObject.remove(JSONLD_TERM_TYPE);
193+
else
194+
this.jsonLdObject.put(JSONLD_TERM_TYPE, type);
151195
}
152196

153197
public String toPrettyJsonString() throws JsonGenerationException, IOException {
@@ -164,7 +208,59 @@ public String toJsonString() throws JsonGenerationException, IOException {
164208
* Validation
165209
*/
166210

211+
private static void validateTrue(boolean valid) throws IllegalStateException {
212+
213+
if (! valid) throw new IllegalStateException();
214+
}
215+
216+
private static void validateUrl(String uri) {
217+
218+
try {
219+
220+
if (! new URI(uri).isAbsolute()) throw new URISyntaxException("Not absolute.", uri);
221+
} catch (URISyntaxException ex) {
222+
223+
throw new RuntimeException(ex.getMessage());
224+
}
225+
}
226+
227+
private static void validateRun(Runnable runnable, String message) throws IllegalStateException {
228+
229+
try {
230+
231+
runnable.run();
232+
} catch (Exception ex) {
233+
234+
throw new IllegalStateException(message);
235+
}
236+
}
237+
167238
public void validate() throws IllegalStateException {
168239

240+
validateRun(() -> { validateTrue(this.getJsonLdObject() != null); }, "Bad or missing verifiable presentation.");
241+
validateRun(() -> { validateTrue(this.getVerifiableCredential() != null); }, "Bad or missing 'verifiableCredential'.");
242+
243+
validateRun(() -> { validateTrue(this.getContext().size() > 0); }, "Bad or missing '@context'.");
244+
validateRun(() -> { validateTrue(this.getType().size() > 0); }, "Bad or missing '@type'.");
245+
246+
validateRun(() -> { validateTrue(JSONLD_CONTEXT_CREDENTIALS.equals(this.getContext().get(0)) || JSONLD_CONTEXT_CREDENTIALS_NO_WWW.equals(this.getContext().get(0))); }, "First value of @context must be https://www.w3.org/2018/credentials/v1: " + this.getContext().get(0));
247+
validateRun(() -> { validateUrl(this.getContext().get(0)); }, "@context must be a valid URI: " + this.getContext().get(0));
248+
validateRun(() -> { validateTrue(this.getType().contains(JSONLD_TYPE_VERIFIABLE_PRESENTATION)); }, "@type must contain VerifiablePresentation: " + this.getType());
249+
}
250+
251+
/*
252+
* Object methods
253+
*/
254+
255+
@Override
256+
public String toString() {
257+
258+
try {
259+
260+
return this.toJsonString();
261+
} catch (IOException ex) {
262+
263+
return super.toString();
264+
}
169265
}
170266
}

src/test/java/com/danubetech/verifiablecredentials/TestUtil.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@
1111

1212
import org.apache.commons.codec.binary.Base64;
1313
import org.apache.commons.codec.binary.Hex;
14+
import org.bitcoinj.core.Base58;
1415
import org.bitcoinj.core.ECKey;
1516

1617
class TestUtil {
1718

1819
static final String testEd25519PrivateKeyString =
19-
"984b589e121040156838303f107e13150be4a80fc5088ccba0b0bdc9b1d89090de8777a28f8da1a74e7a13090ed974d879bf692d001cddee16e4cc9f84b60580";
20+
"43bt2CEvmvm538bQ6YAnpfWTq5xisAB5Kqz7uiob9sabHsZp2HtFEFXRPGa5Mvdhw5xPEABrLduxFu5vt3AViEgF";
2021

2122
static final String testEd25519PublicKeyString =
22-
"de8777a28f8da1a74e7a13090ed974d879bf692d001cddee16e4cc9f84b60580";
23+
"FyfKP2HvTKqDZQzvyL38yXH7bExmwofxHf2NR5BrcGf1";
2324

2425
static final String testSecp256k1PrivateKeyString =
2526
"2ff4e6b73bc4c4c185c68b2c378f6b233978a88d3c8ed03df536f707f084e24e";
@@ -79,8 +80,8 @@ class TestUtil {
7980

8081
try {
8182

82-
testEd25519PrivateKey = Hex.decodeHex(testEd25519PrivateKeyString.toCharArray());
83-
testEd25519PublicKey = Hex.decodeHex(testEd25519PublicKeyString.toCharArray());
83+
testEd25519PrivateKey = Base58.decode(testEd25519PrivateKeyString);
84+
testEd25519PublicKey = Base58.decode(testEd25519PublicKeyString);
8485
} catch (Exception ex) {
8586

8687
throw new RuntimeException(ex.getMessage(), ex);
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
package com.danubetech.verifiablecredentials;
2+
import static org.junit.jupiter.api.Assertions.assertEquals;
3+
import static org.junit.jupiter.api.Assertions.assertFalse;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import java.util.LinkedHashMap;
7+
import java.util.Map;
8+
9+
import org.apache.commons.codec.DecoderException;
10+
import org.bitcoinj.core.Base58;
11+
import org.junit.jupiter.api.Test;
12+
13+
import com.github.jsonldjava.utils.JsonUtils;
14+
15+
import info.weboftrust.ldsignatures.util.CanonicalizationUtil;
16+
import info.weboftrust.ldsignatures.verifier.Ed25519Signature2018LdVerifier;
17+
18+
class VerifyPresentationTest {
19+
20+
final static byte[] publicKeyPresentation1;
21+
final static byte[] publicKeyPresentation2;
22+
final static byte[] publicKeyCredential1;
23+
final static byte[] publicKeyCredential2;
24+
25+
final static VerifiablePresentation verifiablePresentationGood1;
26+
final static VerifiablePresentation verifiablePresentationGood2;
27+
final static VerifiablePresentation verifiablePresentationBad1;
28+
final static VerifiablePresentation verifiablePresentationBad2;
29+
final static VerifiableCredential verifiableCredentialGood1;
30+
final static VerifiableCredential verifiableCredentialGood2;
31+
final static VerifiableCredential verifiableCredentialBad1;
32+
final static VerifiableCredential verifiableCredentialBad2;
33+
34+
static {
35+
36+
try {
37+
38+
publicKeyPresentation1 = Base58.decode("DqS5F3GVe3rCxucgi4JBNagjv4dKoHc8TDLDw9kR58Pz");
39+
publicKeyPresentation2 = Base58.decode("5yKdnU7ToTjAoRNDzfuzVTfWBH38qyhE1b9xh4v8JaWF");
40+
publicKeyCredential1 = Base58.decode("5TVraf9itbKXrRvt2DSS95Gw4vqU3CHAdetoufdcKazA");
41+
publicKeyCredential2 = Base58.decode("5yKdnU7ToTjAoRNDzfuzVTfWBH38qyhE1b9xh4v8JaWF");
42+
43+
LinkedHashMap<String, Object> jsonLdObjectGood1 = (LinkedHashMap<String, Object>) JsonUtils.fromInputStream(VerifyPresentationTest.class.getResourceAsStream("verifiable-presentation1.ldp.good.jsonld"));
44+
LinkedHashMap<String, Object> jsonLdObjectGood2 = (LinkedHashMap<String, Object>) JsonUtils.fromInputStream(VerifyPresentationTest.class.getResourceAsStream("verifiable-presentation2.ldp.good.jsonld"));
45+
LinkedHashMap<String, Object> jsonLdObjectBad1 = (LinkedHashMap<String, Object>) JsonUtils.fromInputStream(VerifyPresentationTest.class.getResourceAsStream("verifiable-presentation1.ldp.bad.jsonld"));
46+
LinkedHashMap<String, Object> jsonLdObjectBad2 = (LinkedHashMap<String, Object>) JsonUtils.fromInputStream(VerifyPresentationTest.class.getResourceAsStream("verifiable-presentation2.ldp.bad.jsonld"));
47+
verifiablePresentationGood1 = VerifiablePresentation.fromJsonLdObject(jsonLdObjectGood1);
48+
verifiablePresentationGood2 = VerifiablePresentation.fromJsonLdObject(jsonLdObjectGood2);
49+
verifiablePresentationBad1 = VerifiablePresentation.fromJsonLdObject(jsonLdObjectBad1);
50+
verifiablePresentationBad2 = VerifiablePresentation.fromJsonLdObject(jsonLdObjectBad2);
51+
verifiableCredentialGood1 = verifiablePresentationGood1.getVerifiableCredential();
52+
verifiableCredentialGood2 = verifiablePresentationGood2.getVerifiableCredential();
53+
verifiableCredentialBad1 = verifiablePresentationBad1.getVerifiableCredential();
54+
verifiableCredentialBad2 = verifiablePresentationBad2.getVerifiableCredential();
55+
} catch (Exception ex) {
56+
57+
throw new RuntimeException(ex.getMessage(), ex);
58+
}
59+
}
60+
61+
/*
62+
* GOOD CREDENTIAL
63+
*/
64+
65+
@Test
66+
void testVerifyGoodCredential1() throws Exception {
67+
68+
Ed25519Signature2018LdVerifier verifier = new Ed25519Signature2018LdVerifier(publicKeyCredential1);
69+
boolean verify = verifier.verify(verifiableCredentialGood1.getJsonLdObject());
70+
71+
assertTrue(verify);
72+
assertEquals("Bachelor of Science and Arts", (String) ((Map<String, Object>) verifiableCredentialGood1.getJsonLdCredentialSubject().get("degree")).get("name"));
73+
}
74+
75+
@Test
76+
void testVerifyGoodCredential2() throws Exception {
77+
78+
Ed25519Signature2018LdVerifier verifier = new Ed25519Signature2018LdVerifier(publicKeyCredential2);
79+
boolean verify = verifier.verify(verifiableCredentialGood2.getJsonLdObject());
80+
81+
assertTrue(verify);
82+
assertEquals("Bachelor of Science and Arts", (String) ((Map<String, Object>) verifiableCredentialGood1.getJsonLdCredentialSubject().get("degree")).get("name"));
83+
}
84+
85+
/*
86+
* BAD CREDENTIAL
87+
*/
88+
89+
@Test
90+
void testVerifyBadCredential1() throws Exception {
91+
92+
Ed25519Signature2018LdVerifier verifier = new Ed25519Signature2018LdVerifier(publicKeyCredential1);
93+
boolean verify = verifier.verify(verifiableCredentialBad1.getJsonLdObject());
94+
95+
assertFalse(verify);
96+
assertEquals("Master of Science and Arts", (String) ((Map<String, Object>) verifiableCredentialBad1.getJsonLdCredentialSubject().get("degree")).get("name"));
97+
}
98+
99+
@Test
100+
void testVerifyBadCredential2() throws Exception {
101+
102+
Ed25519Signature2018LdVerifier verifier = new Ed25519Signature2018LdVerifier(publicKeyCredential2);
103+
boolean verify = verifier.verify(verifiableCredentialBad2.getJsonLdObject());
104+
105+
assertFalse(verify);
106+
assertEquals("Master of Science and Arts", (String) ((Map<String, Object>) verifiableCredentialBad2.getJsonLdCredentialSubject().get("degree")).get("name"));
107+
}
108+
109+
/*
110+
* GOOD PRESENTATION
111+
*/
112+
113+
@Test
114+
void testVerifyGoodPresentation1() throws Exception {
115+
116+
VerifiablePresentation verifiablePresentation = VerifiablePresentation.fromJsonString(verifiablePresentationGood1.toJsonString());
117+
118+
CanonicalizationUtil.fixImplicitGraph(verifiablePresentation.getVerifiableCredential().getLdSignature().getJsonLdSignatureObject());
119+
CanonicalizationUtil.fixImplicitGraph(verifiablePresentation.getVerifiableCredential().getJsonLdObject());
120+
121+
Ed25519Signature2018LdVerifier verifier = new Ed25519Signature2018LdVerifier(publicKeyPresentation1);
122+
boolean verify = verifier.verify(verifiablePresentation.getJsonLdObject());
123+
124+
assertTrue(verify);
125+
}
126+
127+
@Test
128+
void testVerifyGoodPresentation2() throws Exception {
129+
130+
VerifiablePresentation verifiablePresentation = VerifiablePresentation.fromJsonString(verifiablePresentationGood2.toJsonString());
131+
132+
CanonicalizationUtil.fixImplicitGraph(verifiablePresentation.getVerifiableCredential().getLdSignature().getJsonLdSignatureObject());
133+
CanonicalizationUtil.fixImplicitGraph(verifiablePresentation.getVerifiableCredential().getJsonLdObject());
134+
135+
Ed25519Signature2018LdVerifier verifier = new Ed25519Signature2018LdVerifier(publicKeyPresentation2);
136+
boolean verify = verifier.verify(verifiablePresentation.getJsonLdObject());
137+
138+
assertTrue(verify);
139+
}
140+
141+
/*
142+
* BAD PRESENTATION
143+
*/
144+
145+
@Test
146+
void testVerifyBadPresentation1() throws Exception {
147+
148+
VerifiablePresentation verifiablePresentation = VerifiablePresentation.fromJsonString(verifiablePresentationBad1.toJsonString());
149+
150+
CanonicalizationUtil.fixImplicitGraph(verifiablePresentation.getVerifiableCredential().getLdSignature().getJsonLdSignatureObject());
151+
CanonicalizationUtil.fixImplicitGraph(verifiablePresentation.getVerifiableCredential().getJsonLdObject());
152+
153+
Ed25519Signature2018LdVerifier verifier = new Ed25519Signature2018LdVerifier(publicKeyPresentation1);
154+
boolean verify = verifier.verify(verifiablePresentation.getJsonLdObject());
155+
156+
assertFalse(verify);
157+
}
158+
159+
@Test
160+
void testVerifyBadPresentation2() throws Exception {
161+
162+
VerifiablePresentation verifiablePresentation = VerifiablePresentation.fromJsonString(verifiablePresentationBad2.toJsonString());
163+
164+
CanonicalizationUtil.fixImplicitGraph(verifiablePresentation.getVerifiableCredential().getLdSignature().getJsonLdSignatureObject());
165+
CanonicalizationUtil.fixImplicitGraph(verifiablePresentation.getVerifiableCredential().getJsonLdObject());
166+
167+
Ed25519Signature2018LdVerifier verifier = new Ed25519Signature2018LdVerifier(publicKeyPresentation2);
168+
boolean verify = verifier.verify(verifiablePresentation.getJsonLdObject());
169+
170+
assertFalse(verify);
171+
}
172+
}

0 commit comments

Comments
 (0)