From de508f5cf63b4f370464b071ac0f802d55a379f4 Mon Sep 17 00:00:00 2001 From: Hjort Date: Fri, 23 Feb 2024 16:29:24 +0100 Subject: [PATCH 1/4] Add bindings for Acceptablerequest --- concordium-base | 2 +- .../sdk/crypto/CryptoJniNative.java | 12 + .../wallet/web3Id/AcceptableRequest.java | 73 +++++ .../crypto/wallet/web3Id/AttributeCheck.java | 5 + .../wallet/web3Id/RequestStatementTest.java | 24 ++ crypto-jni/Cargo.lock | 277 +++++++++++++++++- crypto-jni/src/lib.rs | 141 +++++++-- 7 files changed, 511 insertions(+), 23 deletions(-) create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AcceptableRequest.java create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AttributeCheck.java diff --git a/concordium-base b/concordium-base index 23129b486..97f744307 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit 23129b48624f8ca9fed57b167c88b31e7b7170d8 +Subproject commit 97f744307a8034d1f63cc420cf742e4fc5537647 diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/CryptoJniNative.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/CryptoJniNative.java index 0e338625c..586eee1c8 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/CryptoJniNative.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/CryptoJniNative.java @@ -7,6 +7,8 @@ import com.concordium.sdk.crypto.wallet.UnsignedCredentialInput; import com.concordium.sdk.crypto.wallet.credential.CredentialDeploymentDetails; import com.concordium.sdk.crypto.wallet.credential.CredentialDeploymentSerializationContext; +import com.concordium.sdk.crypto.wallet.web3Id.AcceptableRequest; +import com.concordium.sdk.crypto.wallet.web3Id.AttributeCheck; import com.concordium.sdk.exceptions.JNIError; import com.concordium.sdk.transactions.InitName; import com.concordium.sdk.transactions.ReceiveName; @@ -316,4 +318,14 @@ public static String getVerifiableCredentialBackupEncryptionKey(String seedAsHex * {@link Web3IdProof} as JSON. */ public static native String createWeb3IdProof(String input); + + /** + * Check that a request is acceptable + * @param input {@link Request} serialized as JSON. + * @return JSON representing {@link StringResult}. If successful the field 'result' is empty, + * but if not acceptable the reason will be contained in the error. + */ + public static native String isAcceptableRequest(String input); + + public static native String isAcceptableAtomicStatement(String input, String rangeTags, String setTags, AcceptableRequest.RawAttributeCheck check); } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AcceptableRequest.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AcceptableRequest.java new file mode 100644 index 000000000..c573f390e --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AcceptableRequest.java @@ -0,0 +1,73 @@ +package com.concordium.sdk.crypto.wallet.web3Id; + +import java.text.AttributedCharacterIterator.Attribute; +import java.util.List; +import java.util.concurrent.atomic.AtomicStampedReference; + +import com.concordium.sdk.crypto.CryptoJniNative; +import com.concordium.sdk.crypto.NativeResolver; +import com.concordium.sdk.crypto.wallet.StringResult; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.AtomicStatement; +import com.concordium.sdk.exceptions.CryptoJniException; +import com.concordium.sdk.responses.accountinfo.credential.AttributeType; +import com.concordium.sdk.serializing.JsonMapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; + +public class AcceptableRequest { + // Static block to load native library. + static { + NativeResolver.loadLib(); + } + + /** + * Check that a request is acceptable + * @param input the request that should be checked + * @return a Presentation serialized as JSON + */ + public static void acceptableRequest(QualifiedRequest request) { + StringResult result = null; + try { + String jsonStr = CryptoJniNative.isAcceptableRequest(JsonMapper.INSTANCE.writeValueAsString(request)); + result = JsonMapper.INSTANCE.readValue(jsonStr, StringResult.class); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + if (!result.isSuccess()) { + throw CryptoJniException.from(result.getErr()); + } + } + + /** + * Check that a request is acceptable + * @param input the request that should be checked + * @return a Presentation serialized as JSON + */ + public static void acceptableAtomicStatement(AtomicStatement statement, List rangeTags, List setTags, AttributeCheck attributeCheck) { + StringResult result = null; + try { + String jsonStr = CryptoJniNative.isAcceptableAtomicStatement(JsonMapper.INSTANCE.writeValueAsString(statement), rangeTags.toString(), setTags.toString(), new RawAttributeCheck(attributeCheck)); + result = JsonMapper.INSTANCE.readValue(jsonStr, StringResult.class); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + if (!result.isSuccess()) { + throw CryptoJniException.from(result.getErr()); + } + } + + public static class RawAttributeCheck { + AttributeCheck checker; + + public RawAttributeCheck(AttributeCheck checker) { + this.checker = checker; + } + + public String check_attribute(int tag, String value) throws JsonMappingException, JsonProcessingException { + // TODO fix tag + return checker.checkAttribute(AttributeType.values()[tag].name(), JsonMapper.INSTANCE.readValue(value, CredentialAttribute.class)); + } + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AttributeCheck.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AttributeCheck.java new file mode 100644 index 000000000..4e31d28e3 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AttributeCheck.java @@ -0,0 +1,5 @@ +package com.concordium.sdk.crypto.wallet.web3Id; + +public interface AttributeCheck { + public String checkAttribute(String tag, CredentialAttribute value); +} diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/RequestStatementTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/RequestStatementTest.java index 9826fb03f..307214f59 100644 --- a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/RequestStatementTest.java +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/RequestStatementTest.java @@ -4,6 +4,7 @@ import static org.junit.Assert.assertTrue; import java.nio.charset.Charset; +import java.util.Collections; import java.util.List; import org.junit.Test; @@ -12,8 +13,10 @@ import com.concordium.sdk.crypto.wallet.FileHelpers; import com.concordium.sdk.crypto.wallet.Network; import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; +import com.concordium.sdk.crypto.wallet.web3Id.CredentialAttribute.CredentialAttributeType; import com.concordium.sdk.crypto.wallet.web3Id.Statement.AtomicStatement; import com.concordium.sdk.crypto.wallet.web3Id.Statement.QualifiedRequestStatement; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.RangeStatement; import com.concordium.sdk.crypto.wallet.web3Id.Statement.RequestStatement; import com.concordium.sdk.crypto.wallet.web3Id.Statement.StatementType; import com.concordium.sdk.serializing.JsonMapper; @@ -51,6 +54,27 @@ public void testGetsUnsatisfiedStatement() throws Exception { assertEquals(requestStatement1.getStatement().get(0), unsatisfied.get(0)); } + @Test + public void testIsAcceptableRequest() throws Exception { + QualifiedRequest request = ProofTest.loadRequest("accountRequest.json"); + AcceptableRequest.acceptableRequest(request); + } + + @Test + public void testIsAcceptableAtomicStatement() { + AtomicStatement statement = RangeStatement.builder().attributeTag("dob") + .lower(CredentialAttribute.builder().type(CredentialAttributeType.STRING).value("20140112").build()) + .upper(CredentialAttribute.builder().type(CredentialAttributeType.STRING).value("20150112").build()) + .build(); + + AcceptableRequest.acceptableAtomicStatement(statement, Collections.singletonList("dob"), Collections.emptyList(), new AttributeCheck() { + + @Override + public String checkAttribute(String tag, CredentialAttribute value) { + return "Test"; + }}); + } + @Test public void testCanQualifyStatement() throws Exception { // Arrange diff --git a/crypto-jni/Cargo.lock b/crypto-jni/Cargo.lock index 87f9a9204..14c7324f0 100644 --- a/crypto-jni/Cargo.lock +++ b/crypto-jni/Cargo.lock @@ -197,6 +197,18 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + [[package]] name = "bitvec" version = "1.0.1" @@ -540,6 +552,27 @@ dependencies = [ "wallet_library", ] +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + [[package]] name = "curve25519-dalek" version = "4.1.1" @@ -703,6 +736,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "ed25519" version = "2.2.3" @@ -745,6 +799,12 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "equivalent" version = "1.0.1" @@ -864,6 +924,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" + [[package]] name = "hex" version = "0.4.3" @@ -940,6 +1006,17 @@ dependencies = [ "serde", ] +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi 0.3.6", + "libc", + "windows-sys", +] + [[package]] name = "itertools" version = "0.10.5" @@ -1019,6 +1096,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "leb128" version = "0.2.5" @@ -1031,6 +1114,17 @@ version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.2", + "libc", + "redox_syscall", +] + [[package]] name = "link-cplusplus" version = "1.0.8" @@ -1162,7 +1256,7 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] @@ -1201,6 +1295,48 @@ dependencies = [ "sha2", ] +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -1223,6 +1359,20 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettytable-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" +dependencies = [ + "csv", + "encode_unicode", + "is-terminal", + "lazy_static", + "term", + "unicode-width", +] + [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -1334,6 +1484,26 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.9.4" @@ -1415,6 +1585,18 @@ dependencies = [ "serde_json", ] +[[package]] +name = "rust_iso3166" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc46f436f726b768364d35d099f43a94f22fd34857ff4f679b1f5cbcb03b9f71" +dependencies = [ + "js-sys", + "phf", + "prettytable-rs", + "wasm-bindgen", +] + [[package]] name = "rustc_version" version = "0.4.0" @@ -1424,6 +1606,12 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.12" @@ -1553,6 +1741,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "spki" version = "0.7.3" @@ -1615,6 +1809,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -1750,14 +1955,16 @@ dependencies = [ [[package]] name = "wallet_library" -version = "0.2.0" +version = "0.3.0" dependencies = [ "anyhow", + "chrono", "concordium_base", "ed25519-dalek", "either", "hex", "key_derivation", + "rust_iso3166", "serde", "serde_json", "thiserror", @@ -1860,6 +2067,72 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" + [[package]] name = "wyz" version = "0.5.1" diff --git a/crypto-jni/src/lib.rs b/crypto-jni/src/lib.rs index 117695729..6a87b65d9 100644 --- a/crypto-jni/src/lib.rs +++ b/crypto-jni/src/lib.rs @@ -4,45 +4,35 @@ use concordium_base::{ base, common::*, contracts_common::{schema::VersionedModuleSchema, Amount}, - encrypted_transfers, - encrypted_transfers::types::{ + encrypted_transfers::{self, types::{ AggregatedDecryptedAmount, EncryptedAmount, EncryptedAmountTransferData, IndexedEncryptedAmount, SecToPubAmountTransferData, - }, - id, - id::{constants::ArCurve, curve_arithmetic::Curve, elgamal, types::GlobalContext}, - transactions::{AddBakerKeysMarker, BakerKeysPayload, ConfigureBakerKeysPayload}, + }}, + id::{self, constants::{self, ArCurve, AttributeKind}, types::AttributeTag, curve_arithmetic::Curve, elgamal, id_proof_types::AtomicStatement, types::GlobalContext}, + transactions::{AddBakerKeysMarker, BakerKeysPayload, ConfigureBakerKeysPayload}, web3id::{Request, Web3IdAttribute}, }; use core::slice; use ed25519_dalek::*; use jni::{ - objects::{JClass, JString}, + objects::{JClass, JString, JValue, JObject}, sys::{jboolean, jbyteArray, jint, jlong, jstring, JNI_FALSE}, JNIEnv, }; use rand::thread_rng; use serde_json::{from_str, to_string}; use std::{ - convert::{From, TryFrom, TryInto}, - i8, - str::Utf8Error, + collections::HashSet, convert::{From, TryFrom, TryInto}, i8, str::Utf8Error }; use wallet_library::{ credential::{ self, create_unsigned_credential_v1_aux, serialize_credential_deployment_payload, CredentialDeploymentDetails, CredentialDeploymentPayload, - }, - identity::{create_identity_object_request_v1_aux, create_identity_recovery_request_aux}, - proofs::Web3IdProofInput, - wallet::{ - get_account_public_key_aux, get_account_signing_key_aux, - get_attribute_commitment_randomness_aux, get_credential_id_aux, get_id_cred_sec_aux, - get_prf_key_aux, get_signature_blinding_randomness_aux, - get_verifiable_credential_backup_encryption_key_aux, - get_verifiable_credential_public_key_aux, get_verifiable_credential_signing_key_aux, - }, + }, identity::{create_identity_object_request_v1_aux, create_identity_recovery_request_aux}, proofs::Web3IdProofInput, statement::{AcceptableRequest, RequestCheckError}, wallet::{ + get_account_public_key_aux, get_account_signing_key_aux, get_attribute_commitment_randomness_aux, get_credential_id_aux, get_id_cred_sec_aux, get_prf_key_aux, get_signature_blinding_randomness_aux, get_verifiable_credential_backup_encryption_key_aux, get_verifiable_credential_public_key_aux, get_verifiable_credential_signing_key_aux + } }; +use wallet_library::statement::{AcceptableAtomicStatement, WalletConfigRules}; const SUCCESS: i32 = 0; const NATIVE_CONVERSION_ERROR: i32 = 1; @@ -1314,3 +1304,114 @@ pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_createWeb3 CryptoJniResult::Ok(presentation_string).to_jstring(&env) } + +impl From for CryptoJniResult { + fn from(e: wallet_library::statement::RequestCheckError) -> Self { + let error = JNIErrorResponse { + errorType: JNIErrorResponseType::NativeConversion, + errorMessage: e.to_string(), + }; + CryptoJniResult::Err(error) + } +} + +/// The JNI wrapper for checking that a request is acceptable. +/// * `input` - the JSON string of [`wallet_library::proofs::RequestStatement`] +#[no_mangle] +#[allow(non_snake_case)] +pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptableRequest( + env: JNIEnv, + _: JClass, + input: JString, +) -> jstring { + let input_string = match get_string(env, input) { + Ok(s) => s, + Err(err) => return StringResult::Err(err).to_jstring(&env), + }; + + let request: Request = match serde_json::from_str(&input_string) { + Ok(req) => req, + Err(err) => return StringResult::from(err).to_jstring(&env), + }; + + match request.acceptable_request(&wallet_library::statement::get_default_wallet_config()) { + Ok(r) => r, + Err(err) => return StringResult::from(err).to_jstring(&env), + }; + + CryptoJniResult::Ok(()).to_jstring(&env) +} + +/// The JNI wrapper for checking that a request is acceptable. +/// * `input` - the JSON string of [`wallet_library::proofs::RequestStatement`] +#[no_mangle] +#[allow(non_snake_case)] +pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptableAtomicStatement<'a>( + env: JNIEnv<'a>, + _: JClass, + request_input: JString, + range_tags_input: JString, + set_tags_input: JString, + attribute_check: JObject<'a>, +) -> jstring { + let input_string = match get_string(env, request_input) { + Ok(s) => s, + Err(err) => return StringResult::Err(err).to_jstring(&env), + }; + + let request: AtomicStatement = match serde_json::from_str(&input_string) { + Ok(req) => req, + Err(err) => return StringResult::from(err).to_jstring(&env), + }; + + let input_string = match get_string(env, set_tags_input) { + Ok(s) => s, + Err(err) => return StringResult::Err(err).to_jstring(&env), + }; + + let set_tags: HashSet = match serde_json::from_str(&input_string) { + Ok(req) => req, + Err(err) => return StringResult::from(err).to_jstring(&env), + }; + + let input_string = match get_string(env, range_tags_input) { + Ok(s) => s, + Err(err) => return StringResult::Err(err).to_jstring(&env), + }; + + let range_tags: HashSet = match serde_json::from_str(&input_string) { + Ok(req) => req, + Err(err) => return StringResult::from(err).to_jstring(&env), + }; + + let check = |tag: &AttributeTag, value: &Web3IdAttribute| { + let attribute: String = match to_string(value) { + Ok(a) => a, + Err(_) => return Err(RequestCheckError::InvalidValue("Unable to be stringified".to_owned())) + }; + let attribute: JString = match env.new_string(attribute) { + Ok(a) => a, + Err(_) => return Err(RequestCheckError::InvalidValue("Unable to be stringified".to_owned())) + }; + + let args = &[JValue::Int(tag.0.into()), attribute.into()]; + match env.call_method(attribute_check, "check_attribute", "(I)V", args) { + Ok(_) => Ok(()), + Err(e) => Err(RequestCheckError::InvalidValue(e.to_string())) + } + }; + + let rules = WalletConfigRules:: { + set_tags, + range_tags, + attribute_check: Box::new(check), + _marker: std::marker::PhantomData + }; + + match request.acceptable_atomic_statement(&Some(rules)) { + Ok(r) => r, + Err(err) => return StringResult::from(err).to_jstring(&env), + }; + + CryptoJniResult::Ok(()).to_jstring(&env) +} From 175c85b65a49dad423cc41859a4790e6593a2fa4 Mon Sep 17 00:00:00 2001 From: Hjort Date: Mon, 26 Feb 2024 11:30:06 +0100 Subject: [PATCH 2/4] Fixes to AcceptableRequest --- .../wallet/web3Id/AcceptableRequest.java | 23 ++-- .../wallet/web3Id/RequestStatementTest.java | 2 +- crypto-jni/src/lib.rs | 115 +++++++++++++----- 3 files changed, 96 insertions(+), 44 deletions(-) diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AcceptableRequest.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AcceptableRequest.java index c573f390e..bed835a86 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AcceptableRequest.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AcceptableRequest.java @@ -1,15 +1,11 @@ package com.concordium.sdk.crypto.wallet.web3Id; -import java.text.AttributedCharacterIterator.Attribute; import java.util.List; -import java.util.concurrent.atomic.AtomicStampedReference; - import com.concordium.sdk.crypto.CryptoJniNative; import com.concordium.sdk.crypto.NativeResolver; import com.concordium.sdk.crypto.wallet.StringResult; import com.concordium.sdk.crypto.wallet.web3Id.Statement.AtomicStatement; import com.concordium.sdk.exceptions.CryptoJniException; -import com.concordium.sdk.responses.accountinfo.credential.AttributeType; import com.concordium.sdk.serializing.JsonMapper; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; @@ -22,6 +18,7 @@ public class AcceptableRequest { /** * Check that a request is acceptable + * * @param input the request that should be checked * @return a Presentation serialized as JSON */ @@ -41,13 +38,20 @@ public static void acceptableRequest(QualifiedRequest request) { /** * Check that a request is acceptable + * * @param input the request that should be checked * @return a Presentation serialized as JSON */ - public static void acceptableAtomicStatement(AtomicStatement statement, List rangeTags, List setTags, AttributeCheck attributeCheck) { + public static void acceptableAtomicStatement(AtomicStatement statement, List rangeTags, + List setTags, AttributeCheck attributeCheck) { StringResult result = null; try { - String jsonStr = CryptoJniNative.isAcceptableAtomicStatement(JsonMapper.INSTANCE.writeValueAsString(statement), rangeTags.toString(), setTags.toString(), new RawAttributeCheck(attributeCheck)); + + String jsonStr = CryptoJniNative.isAcceptableAtomicStatement( + JsonMapper.INSTANCE.writeValueAsString(statement), + JsonMapper.INSTANCE.writeValueAsString(rangeTags), + JsonMapper.INSTANCE.writeValueAsString(setTags), + new RawAttributeCheck(attributeCheck)); result = JsonMapper.INSTANCE.readValue(jsonStr, StringResult.class); } catch (JsonProcessingException e) { throw new RuntimeException(e); @@ -64,10 +68,9 @@ public static class RawAttributeCheck { public RawAttributeCheck(AttributeCheck checker) { this.checker = checker; } - - public String check_attribute(int tag, String value) throws JsonMappingException, JsonProcessingException { - // TODO fix tag - return checker.checkAttribute(AttributeType.values()[tag].name(), JsonMapper.INSTANCE.readValue(value, CredentialAttribute.class)); + + public String check_attribute(String tag, String value) throws JsonMappingException, JsonProcessingException { + return checker.checkAttribute(tag, JsonMapper.INSTANCE.readValue(value, CredentialAttribute.class)); } } } diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/RequestStatementTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/RequestStatementTest.java index 307214f59..4c3e2ecb4 100644 --- a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/RequestStatementTest.java +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/RequestStatementTest.java @@ -22,6 +22,7 @@ import com.concordium.sdk.serializing.JsonMapper; import com.concordium.sdk.transactions.CredentialRegistrationId; import com.concordium.sdk.types.ContractAddress; +import com.fasterxml.jackson.core.JsonProcessingException; public class RequestStatementTest { @@ -68,7 +69,6 @@ public void testIsAcceptableAtomicStatement() { .build(); AcceptableRequest.acceptableAtomicStatement(statement, Collections.singletonList("dob"), Collections.emptyList(), new AttributeCheck() { - @Override public String checkAttribute(String tag, CredentialAttribute value) { return "Test"; diff --git a/crypto-jni/src/lib.rs b/crypto-jni/src/lib.rs index 6a87b65d9..961d5a5fb 100644 --- a/crypto-jni/src/lib.rs +++ b/crypto-jni/src/lib.rs @@ -4,35 +4,58 @@ use concordium_base::{ base, common::*, contracts_common::{schema::VersionedModuleSchema, Amount}, - encrypted_transfers::{self, types::{ - AggregatedDecryptedAmount, EncryptedAmount, EncryptedAmountTransferData, - IndexedEncryptedAmount, SecToPubAmountTransferData, - }}, - id::{self, constants::{self, ArCurve, AttributeKind}, types::AttributeTag, curve_arithmetic::Curve, elgamal, id_proof_types::AtomicStatement, types::GlobalContext}, - transactions::{AddBakerKeysMarker, BakerKeysPayload, ConfigureBakerKeysPayload}, web3id::{Request, Web3IdAttribute}, + encrypted_transfers::{ + self, + types::{ + AggregatedDecryptedAmount, EncryptedAmount, EncryptedAmountTransferData, + IndexedEncryptedAmount, SecToPubAmountTransferData, + }, + }, + id::{ + self, + constants::{self, ArCurve, AttributeKind}, + curve_arithmetic::Curve, + elgamal, + id_proof_types::AtomicStatement, + types::GlobalContext, + }, + transactions::{AddBakerKeysMarker, BakerKeysPayload, ConfigureBakerKeysPayload}, + web3id::{Request, Web3IdAttribute}, }; use core::slice; use ed25519_dalek::*; use jni::{ - objects::{JClass, JString, JValue, JObject}, + objects::{JClass, JObject, JString}, sys::{jboolean, jbyteArray, jint, jlong, jstring, JNI_FALSE}, JNIEnv, }; use rand::thread_rng; use serde_json::{from_str, to_string}; use std::{ - collections::HashSet, convert::{From, TryFrom, TryInto}, i8, str::Utf8Error + collections::HashSet, + convert::{From, TryFrom, TryInto}, + i8, + str::Utf8Error, }; use wallet_library::{ credential::{ self, create_unsigned_credential_v1_aux, serialize_credential_deployment_payload, CredentialDeploymentDetails, CredentialDeploymentPayload, - }, identity::{create_identity_object_request_v1_aux, create_identity_recovery_request_aux}, proofs::Web3IdProofInput, statement::{AcceptableRequest, RequestCheckError}, wallet::{ - get_account_public_key_aux, get_account_signing_key_aux, get_attribute_commitment_randomness_aux, get_credential_id_aux, get_id_cred_sec_aux, get_prf_key_aux, get_signature_blinding_randomness_aux, get_verifiable_credential_backup_encryption_key_aux, get_verifiable_credential_public_key_aux, get_verifiable_credential_signing_key_aux - } + }, + identity::{create_identity_object_request_v1_aux, create_identity_recovery_request_aux}, + proofs::Web3IdProofInput, + statement::{ + AcceptableAtomicStatement, AcceptableRequest, RequestCheckError, WalletConfigRules, + }, + wallet::{ + get_account_public_key_aux, get_account_signing_key_aux, + get_attribute_commitment_randomness_aux, get_credential_id_aux, get_id_cred_sec_aux, + get_prf_key_aux, get_signature_blinding_randomness_aux, + get_verifiable_credential_backup_encryption_key_aux, + get_verifiable_credential_public_key_aux, get_verifiable_credential_signing_key_aux, + }, }; -use wallet_library::statement::{AcceptableAtomicStatement, WalletConfigRules}; const SUCCESS: i32 = 0; const NATIVE_CONVERSION_ERROR: i32 = 1; @@ -1329,10 +1352,11 @@ pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptab Err(err) => return StringResult::Err(err).to_jstring(&env), }; - let request: Request = match serde_json::from_str(&input_string) { - Ok(req) => req, - Err(err) => return StringResult::from(err).to_jstring(&env), - }; + let request: Request = + match serde_json::from_str(&input_string) { + Ok(req) => req, + Err(err) => return StringResult::from(err).to_jstring(&env), + }; match request.acceptable_request(&wallet_library::statement::get_default_wallet_config()) { Ok(r) => r, @@ -1346,7 +1370,9 @@ pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptab /// * `input` - the JSON string of [`wallet_library::proofs::RequestStatement`] #[no_mangle] #[allow(non_snake_case)] -pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptableAtomicStatement<'a>( +pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptableAtomicStatement< + 'a, +>( env: JNIEnv<'a>, _: JClass, request_input: JString, @@ -1359,17 +1385,18 @@ pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptab Err(err) => return StringResult::Err(err).to_jstring(&env), }; - let request: AtomicStatement = match serde_json::from_str(&input_string) { - Ok(req) => req, - Err(err) => return StringResult::from(err).to_jstring(&env), - }; + let request: AtomicStatement = + match serde_json::from_str(&input_string) { + Ok(req) => req, + Err(err) => return StringResult::from(err).to_jstring(&env), + }; let input_string = match get_string(env, set_tags_input) { Ok(s) => s, Err(err) => return StringResult::Err(err).to_jstring(&env), }; - let set_tags: HashSet = match serde_json::from_str(&input_string) { + let set_tags: HashSet = match serde_json::from_str(&input_string) { Ok(req) => req, Err(err) => return StringResult::from(err).to_jstring(&env), }; @@ -1379,33 +1406,55 @@ pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptab Err(err) => return StringResult::Err(err).to_jstring(&env), }; - let range_tags: HashSet = match serde_json::from_str(&input_string) { + let range_tags: HashSet = match serde_json::from_str(&input_string) { Ok(req) => req, Err(err) => return StringResult::from(err).to_jstring(&env), }; - let check = |tag: &AttributeTag, value: &Web3IdAttribute| { - let attribute: String = match to_string(value) { + let check = |tag: &String, attribute: &Web3IdAttribute| { + let jtag: JString = match env.new_string(tag) { Ok(a) => a, - Err(_) => return Err(RequestCheckError::InvalidValue("Unable to be stringified".to_owned())) + Err(_) => { + return Err(RequestCheckError::InvalidValue( + "Unable to be stringified".to_owned(), + )) + } }; - let attribute: JString = match env.new_string(attribute) { + let attribute: String = match to_string(attribute) { Ok(a) => a, - Err(_) => return Err(RequestCheckError::InvalidValue("Unable to be stringified".to_owned())) + Err(_) => { + return Err(RequestCheckError::InvalidValue( + "Unable to be stringified".to_owned(), + )) + } }; - let args = &[JValue::Int(tag.0.into()), attribute.into()]; - match env.call_method(attribute_check, "check_attribute", "(I)V", args) { + let jattribute: JString = match env.new_string(attribute) { + Ok(a) => a, + Err(_) => { + return Err(RequestCheckError::InvalidValue( + "Unable to be stringified".to_owned(), + )) + } + }; + + let args = &[jtag.into(), jattribute.into()]; + match env.call_method( + attribute_check, + "check_attribute", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + args, + ) { Ok(_) => Ok(()), - Err(e) => Err(RequestCheckError::InvalidValue(e.to_string())) + Err(e) => Err(RequestCheckError::InvalidValue(e.to_string())), } }; - let rules = WalletConfigRules:: { + let rules = WalletConfigRules:: { set_tags, range_tags, attribute_check: Box::new(check), - _marker: std::marker::PhantomData + _marker: std::marker::PhantomData, }; match request.acceptable_atomic_statement(&Some(rules)) { From e8ef2950dc746ee7323152f7b0adcf7face281c7 Mon Sep 17 00:00:00 2001 From: Hjort Date: Wed, 13 Mar 2024 11:24:26 +0100 Subject: [PATCH 3/4] Change concordium-base submodule to main + update CHANGELOG --- CHANGELOG.md | 1 + concordium-base | 2 +- crypto-jni/Cargo.lock | 34 ++++++++-------------------------- crypto-jni/src/lib.rs | 4 ++-- 4 files changed, 12 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38b97c99c..5616e44ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Added method `findAtLowestHeight` for finding the earliest block satisfying some condition. - Added method `findAccountCreation` for finding the block in which an account was created. - Fixed an issue where `ConcordiumHdWallet.fromSeedPhrase` always produced an invalid seed as hex. +- Added `AcceptableRequest` class with `acceptableRequest` and `acceptableAtomicStatement` to check if a request satisfies wallet rules. ## 7.0.0 - Make the `energy` parameter for invoking an instance `Optional`. diff --git a/concordium-base b/concordium-base index 97f744307..e6461db70 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit 97f744307a8034d1f63cc420cf742e4fc5537647 +Subproject commit e6461db7032ea88157babc8ff09ea5bac4a39d68 diff --git a/crypto-jni/Cargo.lock b/crypto-jni/Cargo.lock index 14c7324f0..aa63090e4 100644 --- a/crypto-jni/Cargo.lock +++ b/crypto-jni/Cargo.lock @@ -344,18 +344,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", - "time 0.1.45", "wasm-bindgen", - "winapi", + "windows-targets", ] [[package]] @@ -380,7 +379,7 @@ dependencies = [ [[package]] name = "concordium-contracts-common" -version = "9.0.0" +version = "9.1.0" dependencies = [ "base64", "bs58", @@ -400,7 +399,7 @@ dependencies = [ [[package]] name = "concordium-contracts-common-derive" -version = "4.0.1" +version = "4.1.0" dependencies = [ "proc-macro2", "quote", @@ -409,7 +408,7 @@ dependencies = [ [[package]] name = "concordium_base" -version = "4.0.0" +version = "4.1.0" dependencies = [ "anyhow", "ark-bls12-381", @@ -868,7 +867,7 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -1696,7 +1695,7 @@ dependencies = [ "serde", "serde_json", "serde_with_macros", - "time 0.3.28", + "time", ] [[package]] @@ -1849,17 +1848,6 @@ dependencies = [ "syn 1.0.107", ] -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - [[package]] name = "time" version = "0.3.28" @@ -1970,12 +1958,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/crypto-jni/src/lib.rs b/crypto-jni/src/lib.rs index 961d5a5fb..2916b2aed 100644 --- a/crypto-jni/src/lib.rs +++ b/crypto-jni/src/lib.rs @@ -1358,7 +1358,7 @@ pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptab Err(err) => return StringResult::from(err).to_jstring(&env), }; - match request.acceptable_request(&wallet_library::statement::get_default_wallet_config()) { + match request.acceptable_request(&wallet_library::default_wallet_config::default_wallet_config()) { Ok(r) => r, Err(err) => return StringResult::from(err).to_jstring(&env), }; @@ -1457,7 +1457,7 @@ pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptab _marker: std::marker::PhantomData, }; - match request.acceptable_atomic_statement(&Some(rules)) { + match request.acceptable_atomic_statement(Some(rules).as_ref()) { Ok(r) => r, Err(err) => return StringResult::from(err).to_jstring(&env), }; From 5d7a91d8b0828a3537b54a3883e5dbce657800a3 Mon Sep 17 00:00:00 2001 From: Hjort Date: Thu, 14 Mar 2024 13:58:08 +0100 Subject: [PATCH 4/4] Address feedback --- .../sdk/crypto/CryptoJniNative.java | 23 ++++++-- .../sdk/crypto/wallet/ErrorResult.java | 16 ++++++ .../wallet/web3Id/AcceptableRequest.java | 53 ++++++++++++++----- .../crypto/wallet/web3Id/AttributeCheck.java | 9 +++- .../wallet/web3Id/RequestStatementTest.java | 34 ++++++++++-- crypto-jni/src/lib.rs | 46 +++++++++------- 6 files changed, 138 insertions(+), 43 deletions(-) create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/ErrorResult.java diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/CryptoJniNative.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/CryptoJniNative.java index 586eee1c8..01ad74a37 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/CryptoJniNative.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/CryptoJniNative.java @@ -321,11 +321,28 @@ public static String getVerifiableCredentialBackupEncryptionKey(String seedAsHex /** * Check that a request is acceptable + * * @param input {@link Request} serialized as JSON. - * @return JSON representing {@link StringResult}. If successful the field 'result' is empty, - * but if not acceptable the reason will be contained in the error. + * @return JSON representing {@link VoidResult}. If successful the field + * 'result' is empty, + * but if not acceptable the reason will be contained in the error. */ public static native String isAcceptableRequest(String input); - public static native String isAcceptableAtomicStatement(String input, String rangeTags, String setTags, AcceptableRequest.RawAttributeCheck check); + /** + * Check that an atomic is acceptable, with the given restrictions + * + * @param input {@link Request} serialized as JSON. + * @param rangeTags the list of tags, which may be used for range statements, + * serialized as JSON. + * @param setTags the list of tags, which may be used for membership + * statements, serialized as JSON. + * @param check provides the function to check whether the statement value + * is acceptable. + * @return JSON representing {@link VoidResult}. If successful the field + * 'result' is empty, + * but if not acceptable the reason will be contained in the error. + */ + public static native String isAcceptableAtomicStatement(String input, String rangeTags, String setTags, + AcceptableRequest.RawAttributeCheck check); } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/ErrorResult.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/ErrorResult.java new file mode 100644 index 000000000..dd68a3cc3 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/ErrorResult.java @@ -0,0 +1,16 @@ +package com.concordium.sdk.crypto.wallet; + +import com.concordium.sdk.exceptions.JNIError; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class ErrorResult extends StringResult { + + @JsonCreator + ErrorResult( + @JsonProperty("Ok") String ok, + @JsonProperty("Err") JNIError err + ) { + super(ok, err); + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AcceptableRequest.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AcceptableRequest.java index bed835a86..41703ee3e 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AcceptableRequest.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/AcceptableRequest.java @@ -3,12 +3,12 @@ import java.util.List; import com.concordium.sdk.crypto.CryptoJniNative; import com.concordium.sdk.crypto.NativeResolver; +import com.concordium.sdk.crypto.wallet.ErrorResult; import com.concordium.sdk.crypto.wallet.StringResult; import com.concordium.sdk.crypto.wallet.web3Id.Statement.AtomicStatement; import com.concordium.sdk.exceptions.CryptoJniException; import com.concordium.sdk.serializing.JsonMapper; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; public class AcceptableRequest { // Static block to load native library. @@ -17,33 +17,50 @@ public class AcceptableRequest { } /** - * Check that a request is acceptable + * Check that a request is acceptable: + * That range statements are only on the attributes "dob", "idiDocIssuedAt" or + * "idDocExpiredAt" + * That membership statement are only on the attributes "Country of residence", + * "Nationality", "IdDocType" or "IdDocIssuer" + * That attribute tags are not reused within a credentialStatement * * @param input the request that should be checked - * @return a Presentation serialized as JSON + * @throws NotAcceptableException if the request is not acceptable + * @Throws CryptoJniException if there an error occurs duting the check + * @returns if the request is acceptable, otherwise throws a + * NotAcceptableException */ - public static void acceptableRequest(QualifiedRequest request) { - StringResult result = null; + public static void acceptableRequest(QualifiedRequest request) throws NotAcceptableException { + ErrorResult result = null; try { String jsonStr = CryptoJniNative.isAcceptableRequest(JsonMapper.INSTANCE.writeValueAsString(request)); - result = JsonMapper.INSTANCE.readValue(jsonStr, StringResult.class); + result = JsonMapper.INSTANCE.readValue(jsonStr, ErrorResult.class); } catch (JsonProcessingException e) { throw new RuntimeException(e); } - if (!result.isSuccess()) { throw CryptoJniException.from(result.getErr()); } + if (result.getResult() != null) { + throw new NotAcceptableException(result.getResult()); + } } /** - * Check that a request is acceptable + * Check that a statement is acceptable * - * @param input the request that should be checked - * @return a Presentation serialized as JSON + * @param statement the statement that should be checked + * @param rangeTag the attribute tags that may be used for range + * statements + * @param setTags the attribute tags that may be used for set statements + * @param attributeCheck custom check on the value of the statement. + * @throws NotAcceptableException if the request is not acceptable + * @Throws CryptoJniException if there an error occurs duting the check + * @returns if the request is acceptable, otherwise throws a + * NotAcceptableException */ public static void acceptableAtomicStatement(AtomicStatement statement, List rangeTags, - List setTags, AttributeCheck attributeCheck) { + List setTags, AttributeCheck attributeCheck) throws NotAcceptableException { StringResult result = null; try { @@ -56,10 +73,12 @@ public static void acceptableAtomicStatement(AtomicStatement statement, List From for CryptoJniResult>; + /// The JNI wrapper for checking that a request is acceptable. -/// * `input` - the JSON string of [`wallet_library::proofs::RequestStatement`] +/// * `input` - the JSON string of [`concordium_base::web3id::Request`] #[no_mangle] #[allow(non_snake_case)] pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptableRequest( @@ -1349,25 +1352,28 @@ pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptab ) -> jstring { let input_string = match get_string(env, input) { Ok(s) => s, - Err(err) => return StringResult::Err(err).to_jstring(&env), + Err(err) => return ErrorResult::Err(err).to_jstring(&env), }; let request: Request = match serde_json::from_str(&input_string) { Ok(req) => req, - Err(err) => return StringResult::from(err).to_jstring(&env), + Err(err) => return ErrorResult::from(err).to_jstring(&env), }; match request.acceptable_request(&wallet_library::default_wallet_config::default_wallet_config()) { Ok(r) => r, - Err(err) => return StringResult::from(err).to_jstring(&env), + Err(err) => return ErrorResult::Ok(Some(err.to_string())).to_jstring(&env), }; - CryptoJniResult::Ok(()).to_jstring(&env) + ErrorResult::Ok(None).to_jstring(&env) } -/// The JNI wrapper for checking that a request is acceptable. -/// * `input` - the JSON string of [`wallet_library::proofs::RequestStatement`] +/// The JNI wrapper for checking that an atomic statement is acceptable, according to the given rules. +/// * `statement_input` - the JSON string of [`concordium_base::id::id_proof_types::AtomicStatement`] +/// * `range_tags_input` - the JSON string for a list of strings, that represent attribute tags allowed for range statements +/// * `set_tags_input` - the JSON string for a list of strings, that represent attribute tags allowed for membership statements +/// * `attribute_check` - an object implementing the "check_attribute" function, to do a custom check for the attribute #[no_mangle] #[allow(non_snake_case)] pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptableAtomicStatement< @@ -1375,40 +1381,40 @@ pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptab >( env: JNIEnv<'a>, _: JClass, - request_input: JString, + statement_input: JString, range_tags_input: JString, set_tags_input: JString, attribute_check: JObject<'a>, ) -> jstring { - let input_string = match get_string(env, request_input) { + let input_string = match get_string(env, statement_input) { Ok(s) => s, - Err(err) => return StringResult::Err(err).to_jstring(&env), + Err(err) => return ErrorResult::Err(err).to_jstring(&env), }; - let request: AtomicStatement = + let statement: AtomicStatement = match serde_json::from_str(&input_string) { Ok(req) => req, - Err(err) => return StringResult::from(err).to_jstring(&env), + Err(err) => return ErrorResult::from(err).to_jstring(&env), }; let input_string = match get_string(env, set_tags_input) { Ok(s) => s, - Err(err) => return StringResult::Err(err).to_jstring(&env), + Err(err) => return ErrorResult::Err(err).to_jstring(&env), }; let set_tags: HashSet = match serde_json::from_str(&input_string) { Ok(req) => req, - Err(err) => return StringResult::from(err).to_jstring(&env), + Err(err) => return ErrorResult::from(err).to_jstring(&env), }; let input_string = match get_string(env, range_tags_input) { Ok(s) => s, - Err(err) => return StringResult::Err(err).to_jstring(&env), + Err(err) => return ErrorResult::Err(err).to_jstring(&env), }; let range_tags: HashSet = match serde_json::from_str(&input_string) { Ok(req) => req, - Err(err) => return StringResult::from(err).to_jstring(&env), + Err(err) => return ErrorResult::from(err).to_jstring(&env), }; let check = |tag: &String, attribute: &Web3IdAttribute| { @@ -1442,7 +1448,7 @@ pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptab match env.call_method( attribute_check, "check_attribute", - "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + "(Ljava/lang/String;Ljava/lang/String;)V", args, ) { Ok(_) => Ok(()), @@ -1457,10 +1463,10 @@ pub extern "system" fn Java_com_concordium_sdk_crypto_CryptoJniNative_isAcceptab _marker: std::marker::PhantomData, }; - match request.acceptable_atomic_statement(Some(rules).as_ref()) { + match statement.acceptable_atomic_statement(Some(rules).as_ref()) { Ok(r) => r, - Err(err) => return StringResult::from(err).to_jstring(&env), + Err(err) => return ErrorResult::Ok(Some(err.to_string())).to_jstring(&env), }; - CryptoJniResult::Ok(()).to_jstring(&env) + ErrorResult::Ok(None).to_jstring(&env) }