From 28cdcdaa8ea1612e30a5ea92592fd28c1a9176fa Mon Sep 17 00:00:00 2001 From: KBurgmann Date: Thu, 25 Jul 2024 01:56:00 +0200 Subject: [PATCH 1/3] Add policy examples [requires a later version of waltid-identity that includes PR #645] --- .../CustomCredentialDataValidatorPolicy.java | 31 +++++++++ ...ustomCredentialWrapperValidatorPolicy.java | 69 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/main/java/waltid/CustomCredentialDataValidatorPolicy.java create mode 100644 src/main/java/waltid/CustomCredentialWrapperValidatorPolicy.java diff --git a/src/main/java/waltid/CustomCredentialDataValidatorPolicy.java b/src/main/java/waltid/CustomCredentialDataValidatorPolicy.java new file mode 100644 index 0000000..bd938cd --- /dev/null +++ b/src/main/java/waltid/CustomCredentialDataValidatorPolicy.java @@ -0,0 +1,31 @@ +package waltid; + +import id.walt.credentials.verification.JavaCredentialDataValidatorPolicy; +import java.util.Map; +import java.util.Objects; +import kotlinx.serialization.json.JsonObject; +import kotlinx.serialization.json.JsonPrimitive; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class CustomCredentialDataValidatorPolicy extends JavaCredentialDataValidatorPolicy { + public CustomCredentialDataValidatorPolicy() { + super("java-custom-data-validator", "This is a custom data validator for Java"); + } + + @NotNull + @Override + public Object javaVerify(@NotNull JsonObject data, @Nullable Object args, @NotNull Map context) { + if (!data.containsKey("my_verification")) { + throw new IllegalArgumentException("Required attribute not found!"); + } + + String myVerificationContent = ((JsonPrimitive) Objects.requireNonNull(data.get("my_verification"))).getContent(); + + if (!myVerificationContent.equalsIgnoreCase("hello world")) { + throw new IllegalArgumentException("Greet the world!"); + } + + return myVerificationContent; + } +} diff --git a/src/main/java/waltid/CustomCredentialWrapperValidatorPolicy.java b/src/main/java/waltid/CustomCredentialWrapperValidatorPolicy.java new file mode 100644 index 0000000..62c4333 --- /dev/null +++ b/src/main/java/waltid/CustomCredentialWrapperValidatorPolicy.java @@ -0,0 +1,69 @@ +package waltid; + +import id.walt.credentials.schemes.JwsSignatureScheme; +import id.walt.credentials.verification.HolderBindingException; +import id.walt.credentials.verification.JavaCredentialWrapperValidatorPolicy; +import id.walt.crypto.utils.JwsUtils; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import kotlinx.serialization.json.JsonArray; +import kotlinx.serialization.json.JsonElement; +import kotlinx.serialization.json.JsonObject; +import kotlinx.serialization.json.JsonPrimitive; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class CustomCredentialWrapperValidatorPolicy extends JavaCredentialWrapperValidatorPolicy { + public CustomCredentialWrapperValidatorPolicy() { + super("java-custom-credential-wrapper-validator", "This is a custom credential wrapper validator for Java"); + } + + @NotNull + @Override + public Object javaVerify(@NotNull JsonObject data, @Nullable Object args, @NotNull Map context) { + // Getting the 'presenterDid' from data + JsonElement issuerElement = data.get(JwsSignatureScheme.JwsOption.ISSUER); + if (!(issuerElement instanceof JsonPrimitive)) { + throw new IllegalArgumentException("Missing or invalid issuer in data"); + } + String presenterDid = ((JsonPrimitive) issuerElement).getContent(); + + // Getting the 'vp' from data + JsonElement vpElement = data.get("vp"); + if (!(vpElement instanceof JsonObject vp)) { + throw new IllegalArgumentException("No \"vp\" field in VP!"); + } + + // Getting the 'verifiableCredential' from 'vp' + JsonElement credentialsElement = vp.get("verifiableCredential"); + if (!(credentialsElement instanceof JsonArray credentials)) { + throw new IllegalArgumentException("No \"verifiableCredential\" field in \"vp\"!"); + } + + // Processing the credentials to get 'credentialSubjects' + List credentialSubjects = credentials.stream() + .map(it -> { + if (!(it instanceof JsonPrimitive primitive)) { + throw new IllegalArgumentException("Invalid credential in \"verifiableCredential\"!"); + } + String content = primitive.getContent(); + JwsUtils.JwsParts decodedJws = JwsUtils.INSTANCE.decodeJws(content, false, false); + JsonObject payload = decodedJws.getPayload(); + JsonElement subElement = payload.get("sub"); + if (!(subElement instanceof JsonPrimitive)) { + throw new IllegalArgumentException("Invalid 'sub' in payload!"); + } + return ((JsonPrimitive) subElement).getContent().split("#")[0]; + }) + .collect(Collectors.toList()); + + // Returning the result based on 'credentialSubjects' + boolean allMatch = credentialSubjects.stream().allMatch(sub -> sub.equals(presenterDid)); + if (allMatch) { + return presenterDid; + } else { + throw new HolderBindingException(presenterDid, credentialSubjects); + } + } +} From 383d7f31a444857316c4acf1ee6b6a23fd272baa Mon Sep 17 00:00:00 2001 From: KBurgmann Date: Thu, 25 Jul 2024 11:03:06 +0200 Subject: [PATCH 2/3] Update to version where Java policies are available --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 89b7e0d..39695d7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ val kotlin_version: String by project -val waltid_version: String = "0.5.0" +val waltid_version: String = "1.0.2407250811-SNAPSHOT" plugins { @@ -16,7 +16,7 @@ tasks.withType().configureEach { repositories { mavenLocal() mavenCentral() - maven { url = uri("https://maven.waltid.dev/releases") } + maven { url = uri("https://maven.waltid.dev/snapshots") } } From 9e522a83aee9bb5cf29a42cbee47ef3c78d29cfa Mon Sep 17 00:00:00 2001 From: KBurgmann Date: Wed, 7 Aug 2024 02:59:07 +0200 Subject: [PATCH 3/3] Update to newest base version --- build.gradle.kts | 2 +- .../CustomCredentialDataValidatorPolicy.java | 15 ++++++-- ...ustomCredentialWrapperValidatorPolicy.java | 12 +++++- src/main/java/waltid/CustomKeyExample.java | 30 ++++----------- src/main/java/waltid/KeysExamples.java | 38 ++++++++++++------- src/main/java/waltid/RunAll.java | 2 +- 6 files changed, 57 insertions(+), 42 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 39695d7..a29b7b7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ val kotlin_version: String by project -val waltid_version: String = "1.0.2407250811-SNAPSHOT" +val waltid_version: String = "1.0.2408070002-SNAPSHOT" plugins { diff --git a/src/main/java/waltid/CustomCredentialDataValidatorPolicy.java b/src/main/java/waltid/CustomCredentialDataValidatorPolicy.java index bd938cd..c8d07d8 100644 --- a/src/main/java/waltid/CustomCredentialDataValidatorPolicy.java +++ b/src/main/java/waltid/CustomCredentialDataValidatorPolicy.java @@ -9,9 +9,6 @@ import org.jetbrains.annotations.Nullable; public class CustomCredentialDataValidatorPolicy extends JavaCredentialDataValidatorPolicy { - public CustomCredentialDataValidatorPolicy() { - super("java-custom-data-validator", "This is a custom data validator for Java"); - } @NotNull @Override @@ -28,4 +25,16 @@ public Object javaVerify(@NotNull JsonObject data, @Nullable Object args, @NotNu return myVerificationContent; } + + @NotNull + @Override + public String getName() { + return "java-custom-data-validator"; + } + + @NotNull + @Override + public String getDescription() { + return "This is a custom data validator for Java"; + } } diff --git a/src/main/java/waltid/CustomCredentialWrapperValidatorPolicy.java b/src/main/java/waltid/CustomCredentialWrapperValidatorPolicy.java index 62c4333..c0476e9 100644 --- a/src/main/java/waltid/CustomCredentialWrapperValidatorPolicy.java +++ b/src/main/java/waltid/CustomCredentialWrapperValidatorPolicy.java @@ -15,8 +15,16 @@ import org.jetbrains.annotations.Nullable; public class CustomCredentialWrapperValidatorPolicy extends JavaCredentialWrapperValidatorPolicy { - public CustomCredentialWrapperValidatorPolicy() { - super("java-custom-credential-wrapper-validator", "This is a custom credential wrapper validator for Java"); + @NotNull + @Override + public String getName() { + return "java-custom-credential-wrapper-validator"; + } + + @NotNull + @Override + public String getDescription() { + return "This is a custom credential wrapper validator for Java"; } @NotNull diff --git a/src/main/java/waltid/CustomKeyExample.java b/src/main/java/waltid/CustomKeyExample.java index bd3d827..135b2e6 100644 --- a/src/main/java/waltid/CustomKeyExample.java +++ b/src/main/java/waltid/CustomKeyExample.java @@ -5,6 +5,10 @@ import id.walt.crypto.keys.KeyMeta; import id.walt.crypto.keys.KeyType; import id.walt.crypto.keys.jwk.JWKKey; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Base64; +import java.util.Map; import kotlin.random.Random; import kotlinx.serialization.json.Json; import kotlinx.serialization.json.JsonElement; @@ -13,11 +17,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Base64; -import java.util.Map; - public class CustomKeyExample extends JavaKey { private String _xyz; @@ -71,7 +70,8 @@ public Key javaGetPublicKey() { return JWKKey.Companion.generateBlocking(getKeyType(), null); } - public byte[] javaVerifyRaw(@NotNull byte[] signed, @Nullable byte[] detachedPlaintext) { + @Override + public byte @NotNull [] javaVerifyRaw(byte @NotNull [] signed, byte @Nullable [] detachedPlaintext) { System.out.println("Verifying signed with custom key (example): " + Arrays.toString(signed)); assert detachedPlaintext != null; @@ -83,18 +83,7 @@ public byte[] javaVerifyRaw(@NotNull byte[] signed, @Nullable byte[] detachedPla } - @NotNull - @Override - public JsonElement javaVerifyJws() { - return null; - } - - @NotNull @Override - public byte[] javaVerifyRaw() { - return new byte[0]; - } - @NotNull public JsonElement javaVerifyJws(@Language(value = "json") @NotNull String signedJws) { if (Random.Default.nextBoolean()) { @@ -106,7 +95,7 @@ public JsonElement javaVerifyJws(@Language(value = "json") @NotNull String signe @NotNull @Override - public String javaSignJws(@NotNull byte[] plaintext, @NotNull Map headers) { + public String javaSignJws(byte @NotNull [] plaintext, @NotNull Map map) { var base64 = Base64.getUrlEncoder(); String myHeaders = base64.encodeToString("{\"my-headers\": \"xyz\"}".getBytes(StandardCharsets.UTF_8)); @@ -116,9 +105,8 @@ public String javaSignJws(@NotNull byte[] plaintext, @NotNull Map throw new IllegalArgumentException("Unknown key type!"); }; } - - } diff --git a/src/main/java/waltid/KeysExamples.java b/src/main/java/waltid/KeysExamples.java index 677ccd7..a84d0c8 100644 --- a/src/main/java/waltid/KeysExamples.java +++ b/src/main/java/waltid/KeysExamples.java @@ -18,7 +18,7 @@ public class KeysExamples { // demonstrating the Java (async) CompletableFuture API. // Replace KeyType.Ed25519 with a different KeyType if desired. - public static void signBlocking() { + public static void signBlocking() throws Exception { System.out.println("Generating key synchronous..."); JWKKey k = (JWKKey) JWKKey.Companion.generateBlocking(KeyType.Ed25519, null); System.out.println("Sync generated key: " + k); @@ -45,23 +45,35 @@ public static void signAsync() { System.out.println("Async generated key: " + key); System.out.println("Signing with key asynchronous..."); - key.signRawAsync(plaintext).thenAccept(signed -> { - System.out.println("Signed asynchronous: " + Arrays.toString((byte[]) signed)); - - verifyAsync(key, (byte[]) signed, plaintext, "Test async verification"); - }); + try { + key.signRawAsync(plaintext).thenAccept(signed -> { + System.out.println("Signed asynchronous: " + Arrays.toString((byte[]) signed)); + + try { + verifyAsync(key, (byte[]) signed, plaintext, "Test async verification"); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } catch (Exception e) { + throw new RuntimeException(e); + } }); } - public static void verifyAsync(Key key, byte[] signed, byte[] plaintext, String message) { + public static void verifyAsync(Key key, byte[] signed, byte[] plaintext, String message) throws Exception { key.getPublicKeyAsync().thenAccept(publicKey -> { - publicKey.verifyRawAsync(signed, plaintext).thenAccept(result -> { - System.out.println("Verification result (" + message + "): " + result); - }).join(); + try { + publicKey.verifyRawAsync(signed, plaintext).thenAccept(result -> { + System.out.println("Verification result (" + message + "): " + result); + }).join(); + } catch (Exception e) { + throw new RuntimeException(e); + } }).join(); } - public static String exportKey() { + public static String exportKey() throws Exception { // Other KeyTypes: var key2 = JWKKey.Companion.generateBlocking(KeyType.secp256r1, null); System.out.println("Export key..."); @@ -76,7 +88,7 @@ public static void importKey(String jwk) throws ExecutionException, InterruptedE System.out.println("Import result: " + keyImport); } - public static void runKeyExample() throws ExecutionException, InterruptedException { + public static void runKeyExample() throws Exception { KeysExamples.signAsync(); KeysExamples.signBlocking(); @@ -85,7 +97,7 @@ public static void runKeyExample() throws ExecutionException, InterruptedExcepti } - public static void main(String[] args) throws ExecutionException, InterruptedException { + public static void main(String[] args) throws Exception { runKeyExample(); } } diff --git a/src/main/java/waltid/RunAll.java b/src/main/java/waltid/RunAll.java index 2329e66..6615c3e 100644 --- a/src/main/java/waltid/RunAll.java +++ b/src/main/java/waltid/RunAll.java @@ -8,7 +8,7 @@ import static waltid.VcExamples.runVcExample; public class RunAll { - public static void main(String[] args) throws ExecutionException, InterruptedException { + public static void main(String[] args) throws Exception { runKeyExample(); runDidExample(); runVcExample();